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