diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 9e04de723..9260b31ed 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -34,6 +34,7 @@ set(base_SOURCES filelogger.cpp filelogger.hpp filelogger-ti.hpp function.cpp function.hpp function-ti.hpp function-script.cpp functionwrapper.hpp initialize.cpp initialize.hpp + io-engine.cpp io-engine.hpp json.cpp json.hpp json-script.cpp lazy-init.hpp library.cpp library.hpp diff --git a/lib/base/io-engine.cpp b/lib/base/io-engine.cpp new file mode 100644 index 000000000..e1aeeb094 --- /dev/null +++ b/lib/base/io-engine.cpp @@ -0,0 +1,96 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/exception.hpp" +#include "base/io-engine.hpp" +#include "base/lazy-init.hpp" +#include "base/logger.hpp" +#include +#include +#include +#include +#include +#include + +using namespace icinga; + +CpuBoundWork::CpuBoundWork(boost::asio::yield_context yc) +{ + auto& ioEngine (IoEngine::Get()); + + for (;;) { + auto availableSlots (ioEngine.m_CpuBoundSemaphore.fetch_sub(1)); + + if (availableSlots < 1) { + ioEngine.m_CpuBoundSemaphore.fetch_add(1); + ioEngine.m_AlreadyExpiredTimer.async_wait(yc); + continue; + } + + break; + } +} + +CpuBoundWork::~CpuBoundWork() +{ + IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1); +} + +LazyInit> IoEngine::m_Instance ([]() { return std::unique_ptr(new IoEngine()); }); + +IoEngine& IoEngine::Get() +{ + return *m_Instance.Get(); +} + +boost::asio::io_service& IoEngine::GetIoService() +{ + return m_IoService; +} + +IoEngine::IoEngine() : m_IoService(), m_KeepAlive(m_IoService), m_AlreadyExpiredTimer(m_IoService) +{ + m_AlreadyExpiredTimer.expires_at(boost::posix_time::neg_infin); + + auto concurrency (std::thread::hardware_concurrency()); + + if (concurrency < 2) { + m_CpuBoundSemaphore.store(1); + } else { + m_CpuBoundSemaphore.store(concurrency - 1u); + } + + for (auto i (std::thread::hardware_concurrency()); i; --i) { + std::thread(&IoEngine::RunEventLoop, this).detach(); + } +} + +void IoEngine::RunEventLoop() +{ + for (;;) { + try { + m_IoService.run(); + + break; + } catch (const std::exception& e) { + Log(LogCritical, "IoEngine", "Exception during I/O operation!"); + Log(LogDebug, "IoEngine") << "Exception during I/O operation: " << DiagnosticInformation(e); + } + } +} diff --git a/lib/base/io-engine.hpp b/lib/base/io-engine.hpp new file mode 100644 index 000000000..df84df9ce --- /dev/null +++ b/lib/base/io-engine.hpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2018 Icinga Development Team (https://icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef IO_ENGINE_H +#define IO_ENGINE_H + +/** + * Boost.Coroutine2 (the successor of Boost.Coroutine) + * (1) doesn't even exist in old Boost versions and + * (2) isn't supported by ASIO, yet. + */ +#define BOOST_COROUTINES_NO_DEPRECATION_WARNING 1 + +#include "base/lazy-init.hpp" +#include +#include +#include +#include +#include + +/** + * Scope lock for CPU-bound work done in an I/O thread + * + * @ingroup base + */ +class CpuBoundWork +{ +public: + CpuBoundWork(boost::asio::yield_context yc); + CpuBoundWork(const CpuBoundWork&) = delete; + CpuBoundWork(CpuBoundWork&&) = delete; + CpuBoundWork& operator=(const CpuBoundWork&) = delete; + CpuBoundWork& operator=(CpuBoundWork&&) = delete; + ~CpuBoundWork(); +}; + +/** + * Async I/O engine + * + * @ingroup base + */ +class IoEngine +{ + friend CpuBoundWork; + +public: + IoEngine(const IoEngine&) = delete; + IoEngine(IoEngine&&) = delete; + IoEngine& operator=(const IoEngine&) = delete; + IoEngine& operator=(IoEngine&&) = delete; + + static IoEngine& Get(); + + boost::asio::io_service& GetIoService(); + +private: + IoEngine(); + + void RunEventLoop(); + + static LazyInit> m_Instance; + + boost::asio::io_service m_IoService; + boost::asio::io_service::work m_KeepAlive; + boost::asio::deadline_timer m_AlreadyExpiredTimer; + std::atomic_uint_fast32_t m_CpuBoundSemaphore; +}; + +#endif /* IO_ENGINE_H */