1 /******************************************************************************
3 * Copyright (C) 2012-2018 Icinga Development Team (https://icinga.com/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "base/exception.hpp"
21 #include "base/io-engine.hpp"
22 #include "base/lazy-init.hpp"
23 #include "base/logger.hpp"
27 #include <boost/asio/io_service.hpp>
28 #include <boost/asio/spawn.hpp>
29 #include <boost/date_time/posix_time/ptime.hpp>
30 #include <boost/system/error_code.hpp>
32 using namespace icinga;
34 CpuBoundWork::CpuBoundWork(boost::asio::yield_context yc)
37 auto& ioEngine (IoEngine::Get());
40 auto availableSlots (ioEngine.m_CpuBoundSemaphore.fetch_sub(1));
42 if (availableSlots < 1) {
43 ioEngine.m_CpuBoundSemaphore.fetch_add(1);
44 ioEngine.m_AlreadyExpiredTimer.async_wait(yc);
52 CpuBoundWork::~CpuBoundWork()
55 IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1);
59 void CpuBoundWork::Done()
62 IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1);
68 IoBoundWorkSlot::IoBoundWorkSlot(boost::asio::yield_context yc)
71 IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1);
74 IoBoundWorkSlot::~IoBoundWorkSlot()
76 auto& ioEngine (IoEngine::Get());
79 auto availableSlots (ioEngine.m_CpuBoundSemaphore.fetch_sub(1));
81 if (availableSlots < 1) {
82 ioEngine.m_CpuBoundSemaphore.fetch_add(1);
83 ioEngine.m_AlreadyExpiredTimer.async_wait(yc);
91 LazyInit<std::unique_ptr<IoEngine>> IoEngine::m_Instance ([]() { return std::unique_ptr<IoEngine>(new IoEngine()); });
93 IoEngine& IoEngine::Get()
95 return *m_Instance.Get();
98 boost::asio::io_service& IoEngine::GetIoService()
103 IoEngine::IoEngine() : m_IoService(), m_KeepAlive(m_IoService), m_Threads(decltype(m_Threads)::size_type(std::thread::hardware_concurrency() * 2u)), m_AlreadyExpiredTimer(m_IoService)
105 m_AlreadyExpiredTimer.expires_at(boost::posix_time::neg_infin);
106 m_CpuBoundSemaphore.store(std::thread::hardware_concurrency() * 3u / 2u);
108 for (auto& thread : m_Threads) {
109 thread = std::thread(&IoEngine::RunEventLoop, this);
113 IoEngine::~IoEngine()
115 for (auto& thread : m_Threads) {
116 m_IoService.post([]() {
117 throw TerminateIoThread();
121 for (auto& thread : m_Threads) {
126 void IoEngine::RunEventLoop()
133 } catch (const TerminateIoThread&) {
135 } catch (const std::exception& e) {
136 Log(LogCritical, "IoEngine", "Exception during I/O operation!");
137 Log(LogDebug, "IoEngine") << "Exception during I/O operation: " << DiagnosticInformation(e);
142 AsioConditionVariable::AsioConditionVariable(boost::asio::io_service& io, bool init)
145 m_Timer.expires_at(init ? boost::posix_time::neg_infin : boost::posix_time::pos_infin);
148 void AsioConditionVariable::Set()
150 m_Timer.expires_at(boost::posix_time::neg_infin);
153 void AsioConditionVariable::Clear()
155 m_Timer.expires_at(boost::posix_time::pos_infin);
158 void AsioConditionVariable::Wait(boost::asio::yield_context yc)
160 boost::system::error_code ec;
161 m_Timer.async_wait(yc[ec]);