#include <x/ref.H> #include <x/mpobj.H> class msgObj; // A container for pending messages typedef std::list<x::ref<msgObj> > messages_t; // Pending messages x::mpcobj<messages_t> messages; // A lock on the messages queue typedef x::mpcobj<messages_t>::lock lock_t; // ... { lock_t lock(messages); lock->push_back(msg); lock.notify_all(); } // ... x::ref<msgObj> msg=({ lock_t lock(messages); while (lock->empty()) lock.wait(); auto msg=lock->front(); lock->pop_front(); msg; });
The
x::mpcobj
template class implements a design pattern for a
“mutex-protected object”, which
attaches a std::mutex
and a
std::condition_variable
.
Access to the object requires obtaining a lock, which then may be used
as a pointer to the locked object. The lock also provides access to the
underlying condition variable, for signaling and waiting purposes.
These are not reference-counted classes. The underlying mutex-protected object instance must remain in scope as long as there are instances of instantiated locks.
An
x::mpobj
template class implements a mutex-protected object without a condition
variable. Use it when a condition variable is not needed, only an
object that's protected by a mutex.
The second optional parameter to the
x::mpobj
or
the x::mpcobj
template overrides std::mutex
as the
underlying mutex type.
<url>x::mptobj</url> implements a mutex-protected object whose lock ownership is transferrable to a different execution thread.
#include <x/mpthreadlock.H> typedef x::mptobj<locked_info_s> locked_info_t; locked_info_t locked_info; // ... locked_info_t::lock original_lock{locked_info}; // Access something. x::mpthreadlock<locked_info_s> preserver=original_lock.threadlock(); x::w::run_lambda([preserver] { locked_info_t::lock new_lock{preserver}; // ... });
Locks on mutex-protected objects must be constructed in automatic
scope, and released by the same execution thread; but
x::mpthreadlock
provides a means of
preserving a lock on the mutex-protected object after the
original_lock
goes away, preventing other locks from getting acquired.
Its threadlock
() method returns a
preserver object, a
reference-counted object that holds
the lock (after the real lock goes away). The preserver
gets passed
to the new_lock
's constructor, which now owns
a real lock on the underlying object.
new_lock
's constructor blocks until the
original_lock
goes out of scope and gets
destroyed, if it still exists at the time the constructor gets
invoked.
The preserver guarantees that the new_lock
acquires the real lock as long as the original_lock
no longer exists, even if other execution threads are attempting
to acquire the real lock. The preserver "loses its mojo" after the
lock ownership gets transferred to the new_lock
and using it to construct another lock, afterwards, has no
effect.