Class template acyclic_mutex

poet::acyclic_mutex — A mutex wrapper which automatically detects potential deadlocks due to an inconsistent mutex locking order (e.g. deadly embrace).


template<typename Mutex = boost::mutex, typename Key = std::string, 
         typename KeyCompare = std::less<Key> > 
class acyclic_mutex : public acyclic_mutex_base {
  // types
  typedef Mutex      mutex_type; 
  typedef Key        key_type;   
  typedef KeyCompare key_compare;

  // construct/copy/destruct
  acyclic_mutex(const Key &);

  // public member functions
  boost::optional<Key> node_key() const;

  // Boost.Thread Lockable concept support
  void lock();
  bool try_lock();
  void unlock();

  // Boost.Thread TimedLockable concept support
  template<typename Timeout> bool timed_lock(const Timeout &);

  // Boost.Thread SharedLockable concept support
  void lock_shared();
  bool try_lock_shared();
  template<typename Timeout> bool timed_lock_shared(const Timeout &);
  void unlock_shared();
  void unlock_and_lock_shared();

  // Boost.Thread UpgradeLockable concept support
  void lock_upgrade();
  void unlock_upgrade();
  void unlock_upgrade_and_lock();
  void unlock_upgrade_and_lock_shared();
  void unlock_and_lock_upgrade();


The acyclic_mutex class automatically tracks the order in which all the program's acyclic_mutex objects are locked, and detects any potential deadlocks. It does so by building up a graph in the mutex_grapher singleton of the mutex locking order, which is checked to insure the locking order remains consistent as the program executes.

An acyclic_mutex will model the same mutex concepts (see Boost.Thread version 1.35.0 or later) modeled by its Mutex template type. Thus, you may use any appropriate lock type from Boost.Thread to lock an acyclic_mutex (for example, a boost::unique_lock).

The Mutex template type may be any of the mutex classes provided by Boost.Thread (version 1.35.0 or later) or libpoet itself. It may also be a foreign mutex classes, as long as it models one of the mutex concepts defined in the Boost.Thread documentation, and you define a specialization of mutex_properties for the foreign mutex class. However, recursive SharedLockable and recursive UpgradeLockable mutexes are not currently supported, due to there being no known implementations.

The KeyCompare template parameter must define a strict weak ordering for the Key type. Note, this is only used to determine if two keys are equivalent or not, and is not taken to imply any particular locking order requirement between mutexes with inequivalent keys. acyclic_mutex objects with equivalent keys share the same vertex in the locking order graph built by mutex_grapher.

For production code, the tracking of mutex locking order may be disabled program-wide at compile time by defining either NDEBUG or ACYCLIC_MUTEX_NDEBUG.

Example Code

See also

  • mutex_grapher: used by acyclic_mutex objects to build up a locking order graph, and test the locking order for potential deadlocks.

acyclic_mutex public construct/copy/destruct

  1. acyclic_mutex();

    The default constructor creates a mutex with no key. This causes the mutex to be allocated its own vertex in the locking order graph. Default construction requires the least effort from the user, and minimizes the possibility of false positives, but also incurs the most overhead in the locking order graph of the mutex_grapher. If your program allocates many mutex objects, it may be worth coming up with a scheme for assigning keys to your mutexes.

  2. acyclic_mutex(const Key & key);

    Creates a mutex with the specified key. All mutexes with equivalent (according to the KeyCompare ordering) keys will share the same vertex in the locking order graph. This limits the size of the locking order graph, and so can reduce overhead if your program creates many mutex objects. However, it does require additional effort from the programmer to group the mutexes by key in a way which will not produce false positives.

acyclic_mutex public member functions

  1. boost::optional<Key> node_key() const;

    Returns the mutex's key (wrapped in a boost::optional). If the mutex was default constructed, it will have no key and an uninitialized boost::optional is returned. Additionally, if mutex debugging has been disabled by defining NDEBUG or ACYCLIC_MUTEX_NDEBUG, then this function will always return an uninitialized boost::optional.

acyclic_mutex Boost.Thread Lockable concept support

  1. void lock();
  2. bool try_lock();
  3. void unlock();

acyclic_mutex Boost.Thread TimedLockable concept support

  1. template<typename Timeout> bool timed_lock(const Timeout & t);

acyclic_mutex Boost.Thread SharedLockable concept support

  1. void lock_shared();
  2. bool try_lock_shared();
  3. template<typename Timeout> bool timed_lock_shared(const Timeout & t);
  4. void unlock_shared();
  5. void unlock_and_lock_shared();

acyclic_mutex Boost.Thread UpgradeLockable concept support

  1. void lock_upgrade();
  2. void unlock_upgrade();
  3. void unlock_upgrade_and_lock();
  4. void unlock_upgrade_and_lock_shared();
  5. void unlock_and_lock_upgrade();