97 lines
4.1 KiB
C++
97 lines
4.1 KiB
C++
#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
|