FBB::SharedCondition - Shared Memory Condition Variable
#include <bobcat/sharedcondition>
Linking option: -lpthread, -lbobcat
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:
- o
- There exists a thread which should be suspended until a certain condition
has been met.
- o
- This thread locks a mutex (or waits until the lock has been obtained)
- o
- While the condition hasn’t been met, the thread is suspended (i.e.,
waits), automatically releasing the mutex’s lock.
- o
- Somehow (see below) the thread is resumed, at which point the thread has
automatically reacquired the lock.
- o
- Once the condition has been met, the while loop ends, and the
mutex’s lock is released.
- o
- There exists a second thread, which influences the variables that are
elements of the condition, and which may notify the waiting thread, once
the required condition has been met.
- o
- This second thread locks the same mutex as used by the first thread.
- o
- The second thread modifies the variables that are involved, and if the
required condition has been met, it notifies the first thread.
- o
- The second thread releases the mutex’s lock, allowing the first
thread to obtain the mutex’s lock.
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.
FBB
All constructors, members, operators and manipulators, mentioned in this
man-page, are defined in the namespace FBB.
- o
- SharedCondition():
The default constructor creates an empty stub which cannot yet be used (or
an FBB::Exception is thrown). As the SharedCondition class
supports assignment operators, empty stubs can easily be (re)configured at
any time after their construction.
- o
- ~SharedCondition():
The class’s destructor releases (if applicable) its lock on the
shared condition variables mutex lock. The destructor takes no action if
its object is an empty stub.
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.
- o
- void lock() const:
When returning from this member, the current process has locked the
SharedCondition object. Be careful not to call lock twice
during the same thread of execution (cf. sharedmutex(3bobcat) for
details).
- o
- void notify() noexept:
One of the threads waiting on the SharedCondition object wakes up.
The thread calling notify should release its mutex lock shortly
after calling notify, allowing the notified thread to obtain the
lock. A prototypical piece of pseudo code illustrating the use of
notify looks like this:
sharedCondition.lock(); // lock the mutex
... // operate on the condition’s variables
if (conditionWasMet) // ready to notify
sharedCondition.notify();
sharedCondition.unlock(); // release the lock
As 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.
- o
- void notifyAll() noexept:
Different from the plain notify member, this member wakes up all of
the threads waiting on the SharedCondition object. However, after
the current thread has released its mutex lock only one of these signaled
threads will actually obtain the lock. The pseudo code for using
notifyAll is identical to the pseudo code for using notify
(i.e., calling notifyAll, of course).
- o
- std::streamsize offset() const:
The location of the shared condition variable (within the
SharedMemory object) is returned. The shared condition object ends
at offset() + SharedCondition::width(), see below.
- o
- void unlock() const:
The object’s lock is released (nothing happens if called when the
current object does not have the object’s lock).
- o
- void wait():
Before calling wait the current thread should have obtained a lock on
the SharedCondition object.
- 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
- o
- void wait(Predicate pred):
This member was implemented as a member template. Predicate either is
a predicate function or a predicate function object. The predicate
function or the predicate function object’s function call operators
may not require arguments. As long as pred is returning false,
wait() (no arguments) is called. The function returns once
pred has returned true.
- 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).
- o
- std::cv_status wait_for(std::chrono::duration<Type, Unit>
const &relTime):
This member was implemented as a member template. Type defines the
type of the variable holding the amount of time (usually int64_t),
specified in time unit Unit. Predefined duration types are
available from the std::chrono namespace, like
std::chrono::seconds(4), representing 4 seconds, or
std::chrono::milliseconds(30), representing 30 milliseconds.
- 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 lock
When 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.
- o
- bool wait_for(std::chrono::duration<Type, Unit> const
&relTime, Predicate pred):
This member was implemented as a member template. Type defines the
type of the variable holding the amount of time (usually int64_t),
specified in time unit Unit. Predicate either is a predicate
function or a predicate function object. The predicate function or the
predicate function object’s function call operators may not require
arguments.
- 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).
- o
- std::cv_status wait_until(std::chrono::time_point<Clock,
Duration> const &absTime):
This member has been implemented as a member template. Clock defines
the clock-type to use (usually std::chrono::system_clock),
Duration is the type name of a duration type (as used with
wait_for). E.g., to specify 5 seconds after the current time this
member could be called like this:
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).
- o
- bool wait_until(std::chrono::time_point<Clock, Duration>
const &absTime, Predicate pred):
This member was implemented as a member template. Clock and
Duration define identical types as mentioned at the previous
member. Predicate either is a predicate function or a predicate
function object (not expecting arguments).
- 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).
- o
- SharedCondition &attach(SharedMemory &shmem,
std::ios::off_type offset = 0, std::ios::seekdir origin =
std::ios::beg):
The SharedCondition object interfacing to the shared condition
variable located at offset (relative to origin) in
shmem is returned.
- An FBB::Exception is thrown if the requested offset is invalid
(i.e., smaller than 0 or exceeding shmem.maxOffset()).
- o
- FBB::SharedCondition create(SharedMemory &shmem):
A shared condition variable is initialized at the current offset of the
SharedMemory object referred to by shmem, or at the first
offset of the next physical shared data segment.
- 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.
- o
- size_t size() const:
Returns the size in bytes of the shared condition variables stored in
SharedMemory objects.
#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;
}
bobcat/sharedcondition - defines the class interface
- o
- https://fbb-git.gitlab.io/bobcat/: gitlab project page;
- o
- bobcat_5.07.00-x.dsc: detached signature;
- o
- bobcat_5.07.00-x.tar.gz: source archive;
- o
- bobcat_5.07.00-x_i386.changes: change log;
- o
- libbobcat1_5.07.00-x_*.deb: debian package containing the
libraries;
- o
- libbobcat1-dev_5.07.00-x_*.deb: debian package containing the
libraries, headers and manual pages;
Bobcat is an acronym of `Brokken’s Own Base Classes And
Templates’.
This is free software, distributed under the terms of the GNU
General Public License (GPL).
Frank B. Brokken (f.b.brokken@rug.nl).