#pragma once #include "Time.h" #include "Lock.h" #include <cstdint> #if PLATFORM_FUTEX_NATIVE_SUPPORT #include "Internal/ConditionVariableData_FutexBased.inl.h" #else #include "Internal/ConditionVariableData_SemaphoreBased.inl.h" #endif namespace baselib { BASELIB_CPP_INTERFACE { // Conceptually a condition variable is a queue of threads, associated with a monitor, on which a thread may wait for some condition to become true. // // Thus each condition variable c is associated with an assertion Pc. While a thread is waiting on a condition variable, that thread is not considered // to occupy the monitor, and so other threads may enter the monitor to change the monitor's state. In most types of monitors, these other threads may // signal the condition variable c to indicate that assertion Pc is true in the current state. // // "Monitor (synchronization)", Wikipedia: The Free Encyclopedia // https://en.wikipedia.org/w/index.php?title=Monitor_(synchronization)&oldid=914426020#Condition_variables_2 // // For optimal performance, baselib::ConditionVariable should be stored at a cache aligned memory location. class ConditionVariable { public: // non-copyable ConditionVariable(const ConditionVariable& other) = delete; ConditionVariable& operator=(const ConditionVariable& other) = delete; // non-movable (strictly speaking not needed but listed to signal intent) ConditionVariable(ConditionVariable&& other) = delete; ConditionVariable& operator=(ConditionVariable&& other) = delete; // Creates a condition variable synchronization primitive. ConditionVariable(Lock& lock) : m_Lock(lock) {} // Reclaim resources and memory held by the condition variable. // // If threads are waiting on the condition variable, destructor will trigger an assert and may cause process abort. ~ConditionVariable() { BaselibAssert(!m_Data.HasWaiters(), "Destruction is not allowed when there are still threads waiting on the condition variable."); NotifyAll(); } // Wait for the condition variable to become available. // // The lock must have been previously acquired. // For the duration of the wait the lock is released and then re-acquired upon exit. // This function is guaranteed to emit an acquire barrier. inline void Wait(); // Wait for the condition variable to become available. // // The lock must have been previously acquired. // For the duration of the wait the lock is released and then re-acquired upon exit. // This function is guaranteed to emit an acquire barrier. // // TimedWait with a zero timeout is guaranteed to be a user space operation. // // \param timeoutInMilliseconds Time to wait for condition variable to become available. // \returns true if the condition variable is available, false if timeout was reached. inline bool TimedWait(const timeout_ms timeoutInMilliseconds); // Wake up threads waiting for the condition variable. // // This function is guaranteed to emit a release barrier. // // \param count At most, `count` waiting threads will be notified, but never more than there are currently waiting. inline void Notify(uint16_t count); // Wake up all threads waiting for the condition variable. // // This function is guaranteed to emit a release barrier. inline void NotifyAll() { Notify(std::numeric_limits<uint16_t>::max()); } private: Lock& m_Lock; detail::ConditionVariableData m_Data; }; } } #if PLATFORM_FUTEX_NATIVE_SUPPORT #include "Internal/ConditionVariable_FutexBased.inl.h" #else #include "Internal/ConditionVariable_SemaphoreBased.inl.h" #endif