Condition variables are used to synchronize threads based on the values of data. Condition variables allow threads to wait until a certain condition has occurred, after which the threads continue their actions. Thus waiting threads don't continuously have to poll the state of a variable (requiring the threads to gain access to the variable before they can inspect its value). Using condition variables waiting threads simply wait until they are notified.
SharedCondition objects can be used in combination with shared memory. SharedCondition objects interface to objects (called Condition objects in this man-page) which are defined in shared memory and contain a SharedMutex and a shared condition object. These Condition objects may be accessed by threads running in different processes. These different processes might run a single main thread, or they themselves can be multi-threaded.
Condition variables are used in situations like these:
While the first thread is waiting, it is suspended. It may be resumed when it receives a notification from another thread, but also for spurious reasons. Therefore the first thread must verify that the condition has been met after resuming its actions.
As condition variables are always used in combination with a mutex, SharedMutex encapsulates the mutex-handling. The software using SharedCondition objects doesn't have to handle the mutex itself.
SharedCondition objects are used to synchronize actions by different processes, using shared memory as their vehicle of synchronization/communication. The actual condition variable that is used by a SharedCondition object is defined in shared memory. SharedCondition objects themselves are small objects, containing the necessary information to access the actual shared memory condition variable.
Default, copy, and move constructors as well as the copy and move assignment operators are available.
Returning from SharedCondition member functions the offset of the SharedMemory object in which the condition variable has been defined has not changed. Internally, the current offset is saved; the requested function is performed; and the original offset is restored. Consequently, SharedCondition member functions can be used disregarding the SharedMemory's current offset.
sharedCondition.lock(); // lock the mutex ... // operate on the condition's variables if (conditionWasMet) // ready to notify sharedCondition.notify(); sharedCondition.unlock(); // release the lockAs the sharedCondition.lock ... sharedCondition.unlock sequence itself may be executed at different flow of control sections, the unlock member cannot be called from within notify.
When calling wait the running thread suspends its activities and waits until being notified. Once notified, it reacquires the lock and continues. Shortly after this the process should again release its lock on the SharedCondition object. lock. A prototypical piece of pseudo code illustrating how to use wait looks like this:
sharedCondition.lock(); // lock the mutex while (conditionWasNotYetMet) // waiting required sharedCondition.wait(); ... // do something: we have the lock sharedCondition.unlock(); // release the lock
The running thread should have obtained a lock on the SharedCondition condition variable prior to calling this member, and should release the lock after this member has returned.
The pseudo code for using wait(pred) is identical to the pseudo code for using wait (albeit that pred has to be passed to wait, of course).
The running thread should have obtained a lock on SharedCondition prior to calling this member, and should release the lock after this member has returned.
This member acts like wait, returning std::cv_status::no_timeout if a notification was received before relTime has passed. Otherwise std::cv_status::timeout is returned.
A prototypical piece of pseudo code illustrating how to use wait_for looks like this:
sharedCondition.lock(); // lock the mutex while (conditionWasNotYetMet) // waiting required { while (sharedCondition.wait_for(someTime) == std::cv_status::timeout) handle_timeout do_something } sharedCondition.unlock(); // release the lockWhen returning from wait_for the current thread has obtained the shared condition's lock, but maybe due to a timeout: this can be verified by inspecting wait_for's return value, and an appropriate action can be selected.
The running thread should have obtained a lock on SharedCondition prior to calling this member, and should release the lock after this member has returned.
As long as pred returns false, wait_for(relTime) is called. If the latter function returns std::cv_status::timeout, then pred is called, and its return value is returned. Otherwise true is returned.
The pseudo code for using this member is identical to the pseudo code for using the abovementioned wait_for member (albeit that pred must also be passed to wait_for, of course).
std::chrono::system_clock::now() + std::chrono::seconds(5)
The running thread should have obtained a lock on SharedCondition prior to calling this member, and should release the lock after this member has returned.
This member acts like wait_for(relative-time), returning std::cv_status::no_timeout if a notification was received before absTime has passed. Otherwise std::cv_status::timeout is returned.
The pseudo code for using this member is identical to the pseudo code for using the abovementioned wait_for(relative-time) member (albeit that absolute time must be specified).
The running thread should have obtained a lock on SharedCondition prior to calling this member, and should release the lock after this member has returned.
As long as pred returns false, wait_until(absTime) is called. If the latter function returns std::cv_status::timeout, then pred is called, and its return value is returned. Otherwise true is returned.
The pseudo code for using this member is identical to the pseudo code for using the abovementioned wait_until member (albeit that pred must also be passed to wait_until, of course).
An FBB::Exception is thrown if the requested offset is invalid (i.e., smaller than 0 or exceeding shmem.maxOffset()).
A SharedCondition object interfacing to the initialized shared condition variable is returned.
An FBB::Exception is thrown if there isn't enough memory available in the SharedMemory object to define a shared condition variable.
#include <iostream> #include <bobcat/sharedcondition> #include <bobcat/sharedmemory> using namespace std; using namespace FBB; int main(int argc, char **argv) try { if (argc == 1) { cout << "Argument:\n" " c: create a shared memory segment + SharedCondition " ", display ID\n" " k <id>: kill shared memory segment <id>\n" " m <id>: show a message every 5 secs, otherwise wait until\n" " being notified in segment <id>\n" " n <id>: notify the SharedCondition in segment ID <id>\n" ; return 0; } switch (argv[1][0]) { case 'c': { SharedMemory shmem(1, SharedMemory::kB); SharedCondition cond = SharedCondition::create(shmem); void *ptr = shmem.ptr(); cout << "ID = " << shmem.id() << ", SharedCondition at " << cond.offset() << endl; break; } case 'k': { SharedMemory shmem(stoll(argv[2])); shmem.kill(); break; } case 'm': { SharedMemory shmem(stoll(argv[2])); SharedCondition cond = SharedCondition::attach(shmem); cond.lock(); cout << "Obtained the lock. Now waiting for a notification\n"; while (true) { switch (cond.wait_for(chrono::seconds(5))) { case cv_status::timeout: cout << "Waited for 5 seconds\n\n"; break; case cv_status::no_timeout: cond.unlock(); cout << "Received the notification. Unlocked.\n"; return 0; } } } case 'w': { SharedMemory shmem(stoll(argv[2])); SharedCondition cond = SharedCondition::attach(shmem); cond.lock(); cout << "Obtained the lock. Now waiting for a notification\n"; cond.wait(); cout << "Received the notification. Unlocking.\n"; cond.unlock(); break; } case 'n': { SharedMemory shmem(stoll(argv[2])); SharedCondition cond = SharedCondition::attach(shmem); cout << "Notifying the other after Enter "; cin.ignore(1000, '\n'); cond.lock(); cout << "Obtained the lock. Now notifying the other\n"; cond.notify(); cout << "Sent the notification. Now unlocking.\n"; cond.unlock(); break; } } } catch (exception const &exc) { cout << "Exception: " << exc.what() << endl; }