Mutex objects

#include <x/mutex.H>

x::mutex m(x::mutex::create());

// ...

{
  x::mutex::base::mlock lock=m->lock();

  // ...

}

x::mutex is a reference-counted object with similar semantics as std::mutex. Its lock() method instantiates a x::mutex::base::mlock, which itself a reference on a reference-counted object. Only one lock object may exist at a time. The mutex gets unlocked when the last reference to the lock object goes out of scope and gets destroyed.

x::mutex and x::mutex::base::mlock are references to reference-counted objects; as such, for example, a lock can get acquired by one thread, and passed around. Whichever thread holds the last reference to the lock unlocks the mutex when it goes out of scope and gets destroyed. The std::mutex limitations, that the mutex must be unlocked by the same thread that locked it, do not apply here.

If a lock object already exists, lock() waits until the existing lock object goes out of scope. Following the usual convention for reference-counted objects, x::mutexptr is a typedef for a nullable pointer reference to a mutex, and x::mutex::base::mlockptr is a nullable pointer reference to a lock object:

x::mutex::base::mlockptr lock=m->trylock();

if (lock.null())
   // ...

trylock() does not wait until an existing lock object goes out of scope. If one exists, a null pointer reference gets returned instead of constructing a lock. There's also an overloaded wait_until() that takes a time point that sets the wait expiration. If a lock is not immediately available, it gets waited for until the specified time point. A null pointer reference gets returned also, instead of a lock object reference:

#include <x/mutex.H>
#include <chrono>

x::mutex m=x::mutex::create();

x::mutex::base::mlockptr p=
    m->wait_until(std::chrono::monotonic_clock::now() +
        std::chrono::milliseconds(500));

    if (!p.null())
        // ...

This example waits half a second for the lock to be acquired, before giving up.