/* Icinga 2 | (c) 2019 Icinga GmbH | GPLv2+ */ #ifndef ATOMIC_H #define ATOMIC_H #include #include namespace icinga { /** * Like std::atomic, but enforces usage of its only safe constructor. * * "The default-initialized std::atomic does not contain a T object, * and its only valid uses are destruction and * initialization by std::atomic_init, see LWG issue 2334." * -- https://en.cppreference.com/w/cpp/atomic/atomic/atomic * * @ingroup base */ template class Atomic : public std::atomic { public: /** * The only safe constructor of std::atomic#atomic * * @param desired Initial value */ inline Atomic(T desired) : std::atomic(desired) { } }; class LockedMutex; /** * Wraps any T into an interface similar to std::atomic, that locks using a mutex. * * In contrast to std::atomic, Locked is also valid for types that are not trivially copyable. * In case T is trivially copyable, std::atomic is almost certainly the better choice. * * @ingroup base */ template class Locked { public: T load(LockedMutex& mtx) const; void store(T desired, LockedMutex& mtx); private: T m_Value; }; /** * Wraps std::mutex, so that only Locked can (un)lock it. * * The latter tiny lock scope is enforced this way to prevent deadlocks while passing around mutexes. * * @ingroup base */ class LockedMutex { template friend class Locked; private: std::mutex m_Mutex; }; template T Locked::load(LockedMutex& mtx) const { std::unique_lock lock (mtx.m_Mutex); return m_Value; } template void Locked::store(T desired, LockedMutex& mtx) { std::unique_lock lock (mtx.m_Mutex); m_Value = std::move(desired); } } #endif /* ATOMIC_H */