Mutex-protected objects

#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.

Note

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.

Transferrable mutex locks

<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.

Note

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.