diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 59e836443..d44254a35 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -87,6 +87,7 @@ set(base_SOURCES unixsocket.cpp unixsocket.hpp utility.cpp utility.hpp value.cpp value.hpp value-operators.cpp + wait-group.cpp wait-group.hpp win32.hpp workqueue.cpp workqueue.hpp ) diff --git a/lib/base/wait-group.cpp b/lib/base/wait-group.cpp new file mode 100644 index 000000000..1e1ad00ee --- /dev/null +++ b/lib/base/wait-group.cpp @@ -0,0 +1,38 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#include "base/wait-group.hpp" + +using namespace icinga; + +bool StoppableWaitGroup::try_lock_shared() +{ + std::unique_lock lock (m_Mutex); + + if (m_Stopped) { + return false; + } + + ++m_SharedLocks; + return true; +} + +void StoppableWaitGroup::unlock_shared() +{ + std::unique_lock lock (m_Mutex); + + if (!--m_SharedLocks && m_Stopped) { + lock.unlock(); + m_CV.notify_all(); + } +} + +/** + * Disallow new shared locks, wait for all existing ones. + */ +void StoppableWaitGroup::Join() +{ + std::unique_lock lock (m_Mutex); + + m_Stopped = true; + m_CV.wait(lock, [this] { return !m_SharedLocks; }); +} diff --git a/lib/base/wait-group.hpp b/lib/base/wait-group.hpp new file mode 100644 index 000000000..5b4527011 --- /dev/null +++ b/lib/base/wait-group.hpp @@ -0,0 +1,54 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#pragma once + +#include "base/object.hpp" +#include +#include +#include + +namespace icinga +{ + +/** + * A synchronization interface that allows concurrent shared locking. + * + * @ingroup base + */ +class WaitGroup : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(WaitGroup); + + virtual bool try_lock_shared() = 0; + virtual void unlock_shared() = 0; +}; + +/** + * A thread-safe wait group that can be stopped to prevent further shared locking. + * + * @ingroup base + */ +class StoppableWaitGroup : public WaitGroup +{ +public: + DECLARE_PTR_TYPEDEFS(StoppableWaitGroup); + + StoppableWaitGroup() = default; + StoppableWaitGroup(const StoppableWaitGroup&) = delete; + StoppableWaitGroup(StoppableWaitGroup&&) = delete; + StoppableWaitGroup& operator=(const StoppableWaitGroup&) = delete; + StoppableWaitGroup& operator=(StoppableWaitGroup&&) = delete; + + bool try_lock_shared() override; + void unlock_shared() override; + void Join(); + +private: + std::mutex m_Mutex; + std::condition_variable m_CV; + uint_fast32_t m_SharedLocks = 0; + bool m_Stopped = false; +}; + +}