Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE > Struct Template Reference

#include <ConditionVariable.h>

Public Types

using MutexType = MUTEX
 
using ConditionVariableType = CONDITION_VARIABLE
 
using LockType = unique_lock< MUTEX >
 
using QuickLockType = lock_guard< MUTEX >
 

Public Member Functions

nonvirtual void release_and_notify_one (LockType &lock) noexcept
 
nonvirtual void release_and_notify_all (LockType &lock) noexcept
 
nonvirtual void notify_one () noexcept
 forward notify_one () call to underlying std::condition_variable'
 
nonvirtual void notify_all () noexcept
 forward notify_all () call to underlying std::condition_variable'
 
nonvirtual cv_status wait_until (LockType &lock, Time::TimePointSeconds timeoutAt)
 
nonvirtual cv_status wait_for (LockType &lock, Time::DurationSeconds timeout)
 
template<invocable FUNCTION>
nonvirtual void MutateDataNotifyAll (FUNCTION &&mutatorFunction)
 
template<invocable FUNCTION>
nonvirtual void MutateDataNotifyOne (FUNCTION &&mutatorFunction)
 

Public Attributes

MutexType fMutex
 
CONDITION_VARIABLE fConditionVariable
 

Static Public Attributes

static Time::DurationSeconds sConditionVariableWaitChunkTime {0.25s}
 

Detailed Description

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
struct Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >

NOTE - XCode 15 still has this limitation #if __cpp_lib_jthread < 201911 #warning "NOTE __cpp_lib_jthread < 201911: see if we can lose tests" #endif – LGP 2024-09-17 ConditionVariable is a thin abstraction/wrapper on a std::condition_variable, with support for Stroika thread Cancelation, and other shortcuts to simplify use. (combines related mutex with condition variable). Since you always use a condition variable with a mutex, its helpful to also include the associated mutex in the condition variable (type can be changed with template parameters).

Intentionally leave 'mutex' and 'condition_variable' elements public, because users will OFTEN need to directly access a reference to the mutex, and so there is little opportunity to hide.

This is just meant to codify some good practices and share some code. Its a VERY thin wrapper - if even that - on the std::mutex.

Example Usage
bool fTriggered = false;
// THREAD A:
fConditionVariable.MutateDataNotifyAll ([=]() { fTriggered = true; });
// THREAD B:
std::unique_lock<mutex> lock (fConditionVariable.fMutex);
if (fConditionVariable.wait_until (lock, timeoutAt, [this]() { return fTriggered; })) {
REACT TO CHANGE ();
}
else {
}
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
nonvirtual void MutateDataNotifyAll(FUNCTION &&mutatorFunction)
Note
Design Note on implementing thread cancelation (
>   Using EINTR (POSIX) and alertable states on windows - this is what we do most other places
    in Stroika for thread cancelability.

    Sadly, the POSIX (gcc/clang) of condition_variable don't appear to support EINTR, the windows
    implementation doesn't support alertable states, and (FIND REFERENCE***) I THINK I saw documented
    someplace the standard mentions that this cannot be done (no idea why)

>   Maintain a list of 'waiting' condition variables, and then have thread-abort notify_all() on each of these
    This significantly adds to the cost of waiting (add/remove safely from linked list with thread safety) - but
    that's probably still the best approach.

    Also have to code this very carefully to avoid deadlocks, since the cancelation code needs to lock something to
    allow it to safely signal the condition variable.

    ***Probably best approach but trickier todo***

>   Just wake periodically (shorten the wait time) and look for aborts). This is arguably the most costly approach
    but also simplest to implement, and what we have for now (as of January 2018, version v2.0a225)
Note
From http://en.cppreference.com/w/cpp/thread/condition_variable "Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread."

I believe this is because: The total ordering created by this pattern of update is crucial!

Thread A lock and then unlocks mutex, so change of state of triggered always BEFORE (or after) the change of state in the condition variable mutex, so no ambiguity about when fTriggered set.

If its set before, condition variable sees that on entry (in its pred check).

If its after, then on the mutex unlock (I DON'T KNOW HOW THIS HAPPENS YET) - the Condition var must somehow get 'awakened - probably by a TLS list (queue) of waiters to get notified and that gets hooked in the mutex code???? GUESSING.

So anyhow - note - its critical when changing values of underlying 'condition' - to wrap that in mutex lock/unlock.

This MAYBE related to why "std::condition_variable works only with std::unique_lock<std::mutex>". BUT - then i don't understand how " std::condition_variable_any provides a condition variable that works with any BasicLockable"

Note
use of condition_variable (not condition_variable_any) with stop_token See https://stackoverflow.com/questions/66309276/why-does-c20-stdcondition-variable-not-support-stdstop-token Before Stroika v3, we used typename CONDITION_VARIABLE = conditional_t<same_as<mutex, MUTEX>, condition_variable, condition_variable_any> For better stop_token interop, we've changed the default to typename CONDITION_VARIABLE = condition_variable_any

Definition at line 114 of file ConditionVariable.h.

Member Typedef Documentation

◆ MutexType

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
using Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::MutexType = MUTEX

This is the type of the mutex associated with the condition variable.

Definition at line 118 of file ConditionVariable.h.

◆ ConditionVariableType

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
using Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::ConditionVariableType = CONDITION_VARIABLE

This is the type of condition variable. This is generally going to be condition_variable_any (so it will work with stop_tokens) but could be condition_variable (or others).

Definition at line 124 of file ConditionVariable.h.

◆ LockType

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
using Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::LockType = unique_lock<MUTEX>

explicitly unlockable lock (on the mutex). Use, for example, with condition variables, where the apis need to unlock/lock and track the 'locked' state.

Definition at line 130 of file ConditionVariable.h.

◆ QuickLockType

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
using Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::QuickLockType = lock_guard<MUTEX>

just lock and unlock. Basically the same as LockType, but less flexible (cannot explicitly unlock) and more efficient (cuz no data to track if locked).

Definition at line 136 of file ConditionVariable.h.

Member Function Documentation

◆ release_and_notify_one()

template<typename MUTEX , typename CONDITION_VARIABLE >
void Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::release_and_notify_one ( LockType lock)
noexcept

NOTIFY the condition variable (notify_one), but unlock first due to:
http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all

The notifying thread does not need to hold the lock on the same mutex as the 
one held by the waiting thread(s); in fact doing so is a pessimization, since
the notified thread would immediately block again, waiting for the notifying
thread to release the lock.
Note
NOT a Cancelation Point

Definition at line 23 of file ConditionVariable.inl.

◆ release_and_notify_all()

template<typename MUTEX , typename CONDITION_VARIABLE >
void Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::release_and_notify_all ( LockType lock)
noexcept

NOTIFY the condition variable (notify_all), but unlock first due to:
http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all

The notifying thread does not need to hold the lock on the same mutex as the 
one held by the waiting thread(s); in fact doing so is a pessimization, since
the notified thread would immediately block again, waiting for the notifying
Note
NOT a Cancelation Point

Definition at line 29 of file ConditionVariable.inl.

◆ notify_one()

template<typename MUTEX , typename CONDITION_VARIABLE >
void Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::notify_one ( )
noexcept

forward notify_one () call to underlying std::condition_variable'

Note
NOT a Cancelation Point

Definition at line 35 of file ConditionVariable.inl.

◆ notify_all()

template<typename MUTEX , typename CONDITION_VARIABLE >
void Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::notify_all ( )
noexcept

forward notify_all () call to underlying std::condition_variable'

Note
NOT a Cancelation Point

Definition at line 40 of file ConditionVariable.inl.

◆ wait_until()

template<typename MUTEX , typename CONDITION_VARIABLE >
cv_status Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::wait_until ( LockType lock,
Time::TimePointSeconds  timeoutAt 
)

Like condition_variable wait_until, except using float instead of chrono (fix) supports Stroika thread interruption

readyToWake - predicate which returns false if the waiting should be continued.

Returns: 1) std::cv_status::timeout if the relative timeout specified by rel_time expired, std::cv_status::no_timeout otherwise. 2) true of 'readyToWake' () is reason we woke

Note
Cancelation Point
The intention here is to be semantically IDENTICAL to condition_variable::wait_until () - except for adding support for thread interruption (and a minor point - Time::TimePointSeconds)
the cv_status returning overload CAN return/wakeup spuriously (not timeout, and not desired condition true) The PREDICATE (readyToWake) overload, never returns a spurious wake (so only returns timeout (false) or true if readyToWake returned true.
Precondition
(lock.owns_lock ());
Postcondition
(lock.owns_lock ());

Definition at line 45 of file ConditionVariable.inl.

◆ wait_for()

template<typename MUTEX , typename CONDITION_VARIABLE >
cv_status Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::wait_for ( LockType lock,
Time::DurationSeconds  timeout 
)

Like condition_variable wait_for, except using float instead of chrono (fix) supports Stroika thread interruption

readyToWake - predicate which returns false if the waiting should be continued.

Returns: 1) std::cv_status::timeout if the relative timeout specified by rel_time expired, std::cv_status::no_timeout otherwise. 2) true of 'readyToWake' () is reason we woke

Note
Cancelation Point
The intention here is to be semantically IDENTICAL to condition_variable::wait_for () - except for adding support for thread interruption (and a minor point - Time::TimePointSeconds)
the cv_status returning overload CAN return/wakeup spuriously (not timeout, and not desired condition true) The PREDICATE (readyToWake) overload, never returns a spurious wake (so only returns timeout (false) or true if readyToWake returned true.
Precondition
(lock.owns_lock ());
Postcondition
(lock.owns_lock ());

Definition at line 165 of file ConditionVariable.inl.

◆ MutateDataNotifyAll()

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
template<invocable FUNCTION>
nonvirtual void Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::MutateDataNotifyAll ( FUNCTION &&  mutatorFunction)

Idea is you pass lambda to do actual data change, and this acquires lock first, and notifies all after.

Example Usage
// from BlockingQueue code...
fConditionVariable_.MutateDataNotifyAll ([=]() { fEndOfInput_ = true; });
Note
Not Cancelation Point – but perhaps should be???

◆ MutateDataNotifyOne()

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
template<invocable FUNCTION>
nonvirtual void Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::MutateDataNotifyOne ( FUNCTION &&  mutatorFunction)

Idea is you pass lambda to do actual data change, and this acquires lock first, and notifies one after.

Example Usage
// from BlockingQueue code...
fConditionVariable_.MutateDataNotifyOne ([=]() { fEndOfInput_ = true; });
Note
Not Cancelation Point – but perhaps should be???

Member Data Documentation

◆ sConditionVariableWaitChunkTime

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
Time::DurationSeconds Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::sConditionVariableWaitChunkTime {0.25s}
static

sConditionVariableWaitChunkTime is used iff kSupportsStopToken is false.

When waiting, with kSupportsStopToken false, this max timeout is used chunk the waits into smaller chunks so we can check for thread cancelation.

This time value is generally NOT used in condition variable operation, except in a few disparate situations:

        o       stop_token overloads not supported (see kSupportsStopToken)
        o       ? may change - cannot interrupt main thread (abort) so have no stop token to use/pass? why does that matter?

\note in Stroika v2.1 this was called sThreadAbortCheckFrequency_Default

Definition at line 166 of file ConditionVariable.h.

◆ fMutex

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
MutexType Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::fMutex

ConditionVariable is a very THIN abstraction. Callers will often need to explicitly access/use the mutex

Definition at line 177 of file ConditionVariable.h.

◆ fConditionVariable

template<typename MUTEX = mutex, typename CONDITION_VARIABLE = condition_variable_any>
CONDITION_VARIABLE Stroika::Foundation::Execution::ConditionVariable< MUTEX, CONDITION_VARIABLE >::fConditionVariable

ConditionVariable is a very THIN abstraction. Callers will often need to explicitly access/use the condition_variable

Definition at line 182 of file ConditionVariable.h.


The documentation for this struct was generated from the following files: