#pragma once #include #include #include namespace boost_ext { /** Work-around for missing "thread_local" keyword support in non-C++11 compilers. Ideally, BOOST_THREAD_LOCAL should be moved to a common folder to facilitate re-use elsewhere. */ #ifndef BOOST_THREAD_LOCAL # if __cplusplus >= 201103L # define BOOST_THREAD_LOCAL thread_local # elif defined _MSC_VER # define BOOST_THREAD_LOCAL __declspec(thread) # else # define BOOST_THREAD_LOCAL __thread # endif #endif /** Hierarchical mutex for implementing lock hierarchies. Hierarchy layering violations are checked during locking at run-time. This translates potential deadlocks into deterministic run-time failures that can more easily be detected during testing. Copyright (c) 2013, Fredrik Orderud. The basic design is that all mutexes are assigned a "layer" value for their position within the software architecture. Layer values typically increment with "minor" steps within a module/library, and with "major" steps between modules/libraries. Lock requests are only allowed downwards in the software architecture, meaning mutexes with lower layer-number than the layer-number of the previous mutex locked by the same thread. Failure to lock downwards generate an assertion failure in debug, or exception in debug & release if BOOST_HIERARCHICAL_MUTEX_ALWAYS_CHECK has been defined. Based on Anthony Williams: C++ Concurrency in Action (c) 2012 (ISBN 9781933988771), and Herb Sutter: Use Lock Hierarchies to Avoid Deadlock (c) 2007 (http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163). */ class hierarchical_mutex { public: explicit hierarchical_mutex (unsigned long value) : m_layer(value), m_prev_layer(0) { } /** Convenience contructor for decomposing the layer number into a "major" and "minor" version. This can e.g. be useful for separating within-module layer numbers from intra-module layer number. */ hierarchical_mutex (unsigned short major, unsigned short minor) : m_layer(pack(major, minor)), m_prev_layer(0) { } void lock () { check_for_layering_violation(); m_mutex.lock(); update_layer(); } void unlock () { s_thread_layer = m_prev_layer; m_mutex.unlock(); } bool try_lock () { check_for_layering_violation(); if (!m_mutex.try_lock()) return false; update_layer(); return true; } /** Convenience function for packing "major" and "minor" layer numbers. */ static unsigned int pack (unsigned short major, unsigned short minor) { unsigned int val; unsigned short * ptr = reinterpret_cast(&val); #if defined BOOST_LITTLE_ENDIAN ptr[0] = major; ptr[1] = minor; #else ptr[0] = minor; ptr[1] = major; #endif return val; } private: void check_for_layering_violation () const { #ifdef BOOST_HIERARCHICAL_MUTEX_ALWAYS_CHECK // check in both debug an release (with exception) if (m_layer >= s_thread_layer) throw std::logic_error("locking hierarchy violated"); #else // only check in debug (with assert) assert(m_layer < s_thread_layer); #endif } void update_layer () { m_prev_layer = s_thread_layer; s_thread_layer = m_layer; } mutable boost::mutex m_mutex; ///< underlying mutex unsigned long const m_layer; ///< mutex layer value unsigned long m_prev_layer; ///< previous layer for thread static BOOST_THREAD_LOCAL unsigned long s_thread_layer; ///< current layer for each thread }; BOOST_THREAD_LOCAL unsigned long hierarchical_mutex::s_thread_layer(ULONG_MAX); } // namespace boost_ext