Ticket #8094: hierarchical_mutex.hpp

File hierarchical_mutex.hpp, 3.7 KB (added by Fredrik Orderud <forderud@…>, 10 years ago)
Line 
1#pragma once
2#include <limits>
3#include <cassert>
4#include <boost/thread/mutex.hpp>
5
6namespace 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). */
39class hierarchical_mutex {
40public:
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
83private:
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};
105BOOST_THREAD_LOCAL unsigned long hierarchical_mutex::s_thread_layer(ULONG_MAX);
106
107} // namespace boost_ext