FBB::Sh...ndition(3bobcat)
π FBB::Sh...ndition β the 3bobcat manual, bestie edition π
okay so. you're here for FBB::SharedCondition and honestly? iconic of you. that's the Shared Memory Condition Variable π
and it's lowkey one of the most useful little gremlins in bobcat. this is part 1 of the whole page, so buckle up, we're just getting started π
β¨ SYNOPSIS β¨
#include <bobcat/sharedcondition>
Linking option: -lpthread, -lbobcat
πΈ DESCRIPTION πΈ
so condition variables?? they're the thing you use to synchronize threads based on the values of data. that's it, that's the whole vibe. a condition variable lets threads wait until a certain condition has occurred, and then the threads go on with their little lives. the whole point is that waiting threads don't have to continuously poll the state of a variable β because polling would mean the thread has to gain access to the variable before it can even inspect its value, and that's just exhausting π. with condition variables, waiting threads literally just wait until they get notified. periodt.
SharedCondition objects can be used in combination with shared memory. a SharedCondition object interfaces to objects (called Condition objects in this man-page, iykyk) which are defined in shared memory and which contain a SharedMutex and a shared condition object. these Condition objects may be accessed by threads running in different processes. and those different processes might each run a single main thread, or they themselves can be multi-threaded. no cap.
condition variables get used in situations like these:
- There exists a thread which should be suspended until a certain condition has been met.
- This thread locks a mutex (or waits until the lock has been obtained)
- While the condition hasn't been met, the thread is suspended (i.e., waits), automatically releasing the mutex's lock.
- Somehow (see below) the thread is resumed, at which point the thread has automatically reacquired the lock.
- Once the condition has been met, the while loop ends, and the mutex's lock is released.
- 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.
- This second thread locks the same mutex as used by the first thread.
- The second thread modifies the variables that are involved, and if the required condition has been met, it notifies the first thread.
- The second thread releases the mutex's lock, allowing the first thread to obtain the mutex's lock.
β οΈ okay important one, bestie, do not skip this: while that first thread is waiting, it is suspended. it may be resumed when it receives a notification from another thread β but also for spurious reasons. so the first thread must verify that the condition has been met after resuming its actions. the way that a wake-up is not automatically a "yes the condition is true" is so real. check it yourself. every time. π
since condition variables are always used in combination with a mutex, SharedMutex encapsulates all the mutex-handling for you. the software using SharedCondition objects doesn't have to handle the mutex itself. you're welcome, we love a class that does the heavy lifting π«Ά
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. and the SharedCondition objects themselves? they're small objects β they just contain the necessary information to access the actual shared memory condition variable. tiny but mighty, main character energy β
π¦ NAMESPACE π¦
FBB
All constructors, members, operators and manipulators, mentioned in this man-page, are defined in the namespace FBB.
π INHERITS FROM π
π CONSTRUCTORS, DESTRUCTOR π
SharedCondition()β the default constructor creates an empty stub which cannot yet be used (or anFBB::Exceptionis thrown). but here's the slay: as theSharedConditionclass supports assignment operators, empty stubs can easily be (re)configured at any time after their construction. build it empty, dress it up later π~SharedCondition()β the class's destructor releases (if applicable) its lock on the shared condition variables mutex lock. and if its object is an empty stub? the destructor takes no action. it's giving "if you don't have anything to release, don't do anything" π
Default, copy, and move constructors as well as the copy and move assignment operators are available. β¨
more parts coming, don't go anywhere π
π MEMBER FUNCTIONS π
okay so here's the tea, bestie: every single one of these member functions leaves the offset of the SharedMemory object (the one where the condition variable lives) exactly the way it found it. No shifting, no sliding. Internally it's like β save the current offset, do the thing you asked for, put the original offset back. So you can call these whenever, wherever, and not even think about what the SharedMemory's current offset is. Iconic behaviour honestly. π
void lock() constβ when this one comes back to you, your current process has locked theSharedConditionobject. be careful though β do not calllocktwice in the same thread of execution (peepsharedmutex(3bobcat)if you want the whole saga).void notify() noexeptβ exactly one of the threads waiting on theSharedConditionobject wakes up. The thread that callednotifyshould drop its mutex lock shortly after, so the notified thread can actually grab that lock. A lil prototypical pseudo code moment showing hownotifygets used:
sharedCondition.lock(); // lock the mutex
... // operate on the condition's variables
if (conditionWasMet) // ready to notify
sharedCondition.notify();
sharedCondition.unlock(); // release the lock
Since that sharedCondition.lock ... sharedCondition.unlock sequence might itself run in totally different flow-of-control sections, unlock can't be called from inside notify. Just how it is. π
void notifyAll() noexeptβ unlike plain oldnotify, this one wakes up all the threads waiting on theSharedConditionobject. But here's the catch: once the current thread releases its mutex lock, only one of those signaled threads actually gets the lock. The pseudo code fornotifyAllis the same as the pseudo code fornotify(except obviously you're callingnotifyAll, bestie).std::streamsize offset() constβ hands you back the location of the shared condition variable, measured inside theSharedMemoryobject. The shared condition object ends atoffset() + SharedCondition::width(), which you'll meet down below. πvoid unlock() constβ releases the object's lock. If the current object doesn't have the object's lock, literally nothing happens. Chill.void wait()β before you callwait, the current thread should already have obtained a lock on theSharedConditionobject. fr fr.
When you call wait, the running thread suspends its whole vibe and waits until it gets notified. Once notified, it reacquires the lock and continues on its merry way. Shortly after that, the process should release its lock on the SharedCondition object again. lock. A prototypical pseudo code moment for wait:
sharedCondition.lock(); // lock the mutex
while (conditionWasNotYetMet) // waiting required
sharedCondition.wait();
... // do something: we have the lock
sharedCondition.unlock(); // release the lock
void wait(Predicate pred)β this one was implemented as a member template.Predicateis either 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 aspredkeeps returningfalse,wait()(the no-arguments one) gets called. The function returns oncepredhas returnedtrue.
The running thread should have obtained a lock on the SharedCondition condition variable before calling this member, and should release the lock after this member has returned.
The pseudo code for wait(pred) is identical to the pseudo code for wait (just with pred passed to wait, obviously π
).
std::cv_status wait_for(std::chrono::duration<Type, Unit> const &relTime)β member template alert π¨.Typedefines the type of the variable holding the amount of time (usuallyint64_t), specified in time unitUnit. Predefined duration types live in thestd::chrononamespace, likestd::chrono::seconds(4)(that's 4 seconds) orstd::chrono::milliseconds(30)(that's 30 milliseconds).
The running thread should have obtained a lock on SharedCondition before 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 arrived before relTime passed. Otherwise you get std::cv_status::timeout back.
A prototypical pseudo code moment for wait_for:
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 wait_for returns, the current thread has obtained the shared condition's lock β but maybe that's because of a timeout π. You can verify by inspecting wait_for's return value, and pick an appropriate action from there.
bool wait_for(std::chrono::duration<Type, Unit> const &relTime, Predicate pred)β also implemented as a member template.Typedefines the type of the variable holding the amount of time (usuallyint64_t), specified in time unitUnit.Predicateis either 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 before calling this member, and should release the lock after this member has returned.
As long as pred returns false, wait_for(relTime) gets called. If that function returns std::cv_status::timeout, then pred is called and its return value is what you get back. Otherwise true is returned.
The pseudo code for this member is identical to the pseudo code for the wait_for member above (just with pred also passed to wait_for, of course).
std::cv_status wait_until(std::chrono::time_point<Clock, Duration> const &absTime)β implemented as a member template.Clockdefines the clock-type to use (usuallystd::chrono::system_clock), andDurationis the type name of a duration type (same kind of thing you'd use withwait_for). For example, to specify 5 seconds after the current time, you'd call this member like:
std::chrono::system_clock::now() + std::chrono::seconds(5)
The running thread should have obtained a lock on SharedCondition before 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 arrived before absTime passed. Otherwise std::cv_status::timeout is returned.
The pseudo code for this member is identical to the pseudo code for the wait_for(relative-time) member above (except you gotta specify absolute time, bestie).
bool wait_until(std::chrono::time_point<Clock, Duration> const &absTime, Predicate pred)β implemented as a member template.ClockandDurationdefine identical types as mentioned at the previous member.Predicateis either a predicate function or a predicate function object (and it is not expecting arguments).
The running thread should have obtained a lock on SharedCondition before calling this member, and should release the lock after this member has returned.
As long as pred returns false, wait_until(absTime) gets called. If that function returns std::cv_status::timeout, then pred is called and its return value is what you get back. Otherwise true is returned.
The pseudo code for this member is identical to the pseudo code for the wait_until member above (just with pred also passed to wait_until, of course).
β¨ STATIC MEMBER FUNCTIONS β¨
SharedCondition &attach(SharedMemory &shmem, std::ios::off_type offset = 0, std::ios::seekdir origin = std::ios::beg)β returns theSharedConditionobject that interfaces to the shared condition variable sitting atoffset(relative toorigin) insideshmem.
An FBB::Exception gets thrown if the requested offset is invalid β meaning smaller than 0 or exceeding shmem.maxOffset(). π«
FBB::SharedCondition create(SharedMemory &shmem)β a shared condition variable gets initialized at the current offset of theSharedMemoryobject referred to byshmem, or at the first offset of the next physical shared data segment.
You get back a SharedCondition object interfacing to that freshly initialized shared condition variable. β¨
An FBB::Exception is thrown if there isn't enough memory available in the SharedMemory object to define a shared condition variable.
size_t size() constβ returns the size, in bytes, of the shared condition variables stored inSharedMemoryobjects. π
π EXAMPLE π
okay bestie, this is the moment. here's a whole lil demo program that shows off everything SharedCondition can do β creating a shared memory segment with a condition attached, killing a segment, waiting, getting notified, the whole vibe. it's giving main character energy fr fr.
heads up: this is the exact code from the page, so copy it character for character and it'll work. don't go "fixing" the quotes, the curly ones are part of the deal π
#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;
}
the tea β: run it with no arguments and it prints its own lil menu (that's the argc == 1 branch, iconic honestly). then argv[1][0] decides which case you land in β c to create, k <id> to kill, m <id> to wait for 5-sec timeouts, w <id> to wait forever, n <id> to notify. and everything is wrapped in a try/catch so any exception gets printed instead of nuking your terminal. no cap, that's just good manners π«Ά
π FILES π
bobcat/sharedconditionβ defines the class interface
π SEE ALSO π
go read these too bestie, they're all part of the same friend group:
bobcat(7)isharedstream(3bobcat)osharedstream(3bobcat)sharedblock(3bobcat)sharedmemory(3bobcat)sharedpos(3bobcat)sharedreadme(7bobcat)sharedsegment(3bobcat)sharedstream(3bobcat)sharedbuf(3bobcat)
π BUGS π
None Reported.
periodt. nothing to see here, we're clean β¨
π¦ BOBCAT PROJECT FILES π¦
- https://fbb-git.gitlab.io/bobcat/ β gitlab project page;
Debian Bobcat project files:
libbobcat6β debian package containing the shared library, changelog and copyright note;libbobcat-devβ debian package containing the static library, headers, manual pages, and developer info;
π BOBCAT π
Bobcat is an acronym of `Brokken's Own Base Classes And Templates'.
like, the name is literally an acronym and that's so real of it π
π COPYRIGHT π
This is free software, distributed under the terms of the GNU General Public License (GPL).
π AUTHOR π
Frank B. Brokken (f.b.brokken@rug.nl).
and that's a wrap on the whole page, bestie π
this was part of libbobcat-dev version 6.07.01, with the copyright years running 2005-2025, and the page you just read is the FBB::Sh...ndition(3bobcat) manual. go forth and condition your shared memory, you absolute icon πΈπ¦β¨
π¦ where this page comes from
- debian trixie: libbobcat-dev 6.07.01-2 Β· GPL-3.0-or-later WITH OpenSSL exception
- ubuntu questing: libbobcat-dev 6.07.01-2
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms