| 1 | #pragma once
|
|---|
| 2 | #include <limits>
|
|---|
| 3 | #include <cassert>
|
|---|
| 4 | #include <boost/thread/mutex.hpp>
|
|---|
| 5 |
|
|---|
| 6 | namespace boost_ext {
|
|---|
| 7 |
|
|---|
| 8 | /** Work-around for missing "thread_local" keyword support in non-C++11 compilers.
|
|---|
| 9 | Ideally, BOOST_THREAD_LOCAL should be moved to a common folder to facilitate re-use elsewhere. */
|
|---|
| 10 | #ifndef BOOST_THREAD_LOCAL
|
|---|
| 11 | # if __cplusplus >= 201103L
|
|---|
| 12 | # define BOOST_THREAD_LOCAL thread_local
|
|---|
| 13 | # elif defined _MSC_VER
|
|---|
| 14 | # define BOOST_THREAD_LOCAL __declspec(thread)
|
|---|
| 15 | # else
|
|---|
| 16 | # define BOOST_THREAD_LOCAL __thread
|
|---|
| 17 | # endif
|
|---|
| 18 | #endif
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 | /** Hierarchical mutex for implementing lock hierarchies.
|
|---|
| 22 | Hierarchy layering violations are checked during locking at run-time. This
|
|---|
| 23 | translates potential deadlocks into deterministic run-time failures that
|
|---|
| 24 | can more easily be detected during testing.
|
|---|
| 25 |
|
|---|
| 26 | Copyright (c) 2013, Fredrik Orderud.
|
|---|
| 27 |
|
|---|
| 28 | The basic design is that all mutexes are assigned a "layer" value for their position
|
|---|
| 29 | within the software architecture. Layer values typically increment with "minor"
|
|---|
| 30 | steps within a module/library, and with "major" steps between modules/libraries.
|
|---|
| 31 |
|
|---|
| 32 | Lock requests are only allowed downwards in the software architecture, meaning mutexes
|
|---|
| 33 | with lower layer-number than the layer-number of the previous mutex locked by the same thread.
|
|---|
| 34 | Failure to lock downwards generate an assertion failure in debug, or exception in debug & release
|
|---|
| 35 | if BOOST_HIERARCHICAL_MUTEX_ALWAYS_CHECK has been defined.
|
|---|
| 36 |
|
|---|
| 37 | Based on Anthony Williams: C++ Concurrency in Action (c) 2012 (ISBN 9781933988771), and
|
|---|
| 38 | Herb Sutter: Use Lock Hierarchies to Avoid Deadlock (c) 2007 (http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163). */
|
|---|
| 39 | class hierarchical_mutex {
|
|---|
| 40 | public:
|
|---|
| 41 | explicit hierarchical_mutex (unsigned long value) :
|
|---|
| 42 | m_layer(value), m_prev_layer(0) {
|
|---|
| 43 | }
|
|---|
| 44 |
|
|---|
| 45 | /** Convenience contructor for decomposing the layer number into a "major" and "minor" version.
|
|---|
| 46 | This can e.g. be useful for separating within-module layer numbers from intra-module layer number. */
|
|---|
| 47 | hierarchical_mutex (unsigned short major, unsigned short minor) : m_layer(pack(major, minor)), m_prev_layer(0) {
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | void lock () {
|
|---|
| 51 | check_for_layering_violation();
|
|---|
| 52 | m_mutex.lock();
|
|---|
| 53 | update_layer();
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | void unlock () {
|
|---|
| 57 | s_thread_layer = m_prev_layer;
|
|---|
| 58 | m_mutex.unlock();
|
|---|
| 59 | }
|
|---|
| 60 |
|
|---|
| 61 | bool try_lock () {
|
|---|
| 62 | check_for_layering_violation();
|
|---|
| 63 | if (!m_mutex.try_lock())
|
|---|
| 64 | return false;
|
|---|
| 65 | update_layer();
|
|---|
| 66 | return true;
|
|---|
| 67 | }
|
|---|
| 68 |
|
|---|
| 69 | /** Convenience function for packing "major" and "minor" layer numbers. */
|
|---|
| 70 | static unsigned int pack (unsigned short major, unsigned short minor) {
|
|---|
| 71 | unsigned int val;
|
|---|
| 72 | unsigned short * ptr = reinterpret_cast<unsigned short*>(&val);
|
|---|
| 73 | #if defined BOOST_LITTLE_ENDIAN
|
|---|
| 74 | ptr[0] = major;
|
|---|
| 75 | ptr[1] = minor;
|
|---|
| 76 | #else
|
|---|
| 77 | ptr[0] = minor;
|
|---|
| 78 | ptr[1] = major;
|
|---|
| 79 | #endif
|
|---|
| 80 | return val;
|
|---|
| 81 | }
|
|---|
| 82 |
|
|---|
| 83 | private:
|
|---|
| 84 | void check_for_layering_violation () const {
|
|---|
| 85 | #ifdef BOOST_HIERARCHICAL_MUTEX_ALWAYS_CHECK
|
|---|
| 86 | // check in both debug an release (with exception)
|
|---|
| 87 | if (m_layer >= s_thread_layer)
|
|---|
| 88 | throw std::logic_error("locking hierarchy violated");
|
|---|
| 89 | #else
|
|---|
| 90 | // only check in debug (with assert)
|
|---|
| 91 | assert(m_layer < s_thread_layer);
|
|---|
| 92 | #endif
|
|---|
| 93 | }
|
|---|
| 94 |
|
|---|
| 95 | void update_layer () {
|
|---|
| 96 | m_prev_layer = s_thread_layer;
|
|---|
| 97 | s_thread_layer = m_layer;
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | mutable boost::mutex m_mutex; ///< underlying mutex
|
|---|
| 101 | unsigned long const m_layer; ///< mutex layer value
|
|---|
| 102 | unsigned long m_prev_layer; ///< previous layer for thread
|
|---|
| 103 | static BOOST_THREAD_LOCAL unsigned long s_thread_layer; ///< current layer for each thread
|
|---|
| 104 | };
|
|---|
| 105 | BOOST_THREAD_LOCAL unsigned long hierarchical_mutex::s_thread_layer(ULONG_MAX);
|
|---|
| 106 |
|
|---|
| 107 | } // namespace boost_ext
|
|---|