@@ -46,14 +46,42 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII
46
46
// //
47
47
// /////////////////////////////
48
48
49
+ #ifdef DEBUG_LOCKORDER
50
+ void EnterCritical (const char * pszName, const char * pszFile, int nLine, void * cs, bool fTry = false );
51
+ void LeaveCritical ();
52
+ std::string LocksHeld ();
53
+ void AssertLockHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs) ASSERT_EXCLUSIVE_LOCK(cs);
54
+ void AssertLockNotHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs);
55
+ void DeleteLock (void * cs);
56
+
49
57
/* *
50
- * Template mixin that adds -Wthread-safety locking
51
- * annotations to a subset of the mutex API.
58
+ * Call abort() if a potential lock order deadlock bug is detected, instead of
59
+ * just logging information and throwing a logic_error. Defaults to true, and
60
+ * set to false in DEBUG_LOCKORDER unit tests.
61
+ */
62
+ extern bool g_debug_lockorder_abort;
63
+ #else
64
+ void static inline EnterCritical (const char * pszName, const char * pszFile, int nLine, void * cs, bool fTry = false ) {}
65
+ void static inline LeaveCritical () {}
66
+ void static inline AssertLockHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
67
+ void static inline AssertLockNotHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs) {}
68
+ void static inline DeleteLock (void * cs) {}
69
+ #endif
70
+ #define AssertLockHeld (cs ) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
71
+ #define AssertLockNotHeld (cs ) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
72
+
73
+ /* *
74
+ * Template mixin that adds -Wthread-safety locking annotations and lock order
75
+ * checking to a subset of the mutex API.
52
76
*/
53
77
template <typename PARENT>
54
78
class LOCKABLE AnnotatedMixin : public PARENT
55
79
{
56
80
public:
81
+ ~AnnotatedMixin () {
82
+ DeleteLock ((void *)this );
83
+ }
84
+
57
85
void lock () EXCLUSIVE_LOCK_FUNCTION()
58
86
{
59
87
PARENT::lock ();
@@ -68,92 +96,67 @@ class LOCKABLE AnnotatedMixin : public PARENT
68
96
{
69
97
return PARENT::try_lock ();
70
98
}
71
- };
72
99
73
- #ifdef DEBUG_LOCKORDER
74
- void EnterCritical (const char * pszName, const char * pszFile, int nLine, void * cs, bool fTry = false );
75
- void LeaveCritical ();
76
- std::string LocksHeld ();
77
- void AssertLockHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs) ASSERT_EXCLUSIVE_LOCK(cs);
78
- void AssertLockNotHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs);
79
- void DeleteLock (void * cs);
80
- #else
81
- void static inline EnterCritical (const char * pszName, const char * pszFile, int nLine, void * cs, bool fTry = false ) {}
82
- void static inline LeaveCritical () {}
83
- void static inline AssertLockHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
84
- void static inline AssertLockNotHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs) {}
85
- void static inline DeleteLock (void * cs) {}
86
- #endif
87
- #define AssertLockHeld (cs ) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
88
- #define AssertLockNotHeld (cs ) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
100
+ using UniqueLock = std::unique_lock<PARENT>;
101
+ };
89
102
90
103
/* *
91
104
* Wrapped mutex: supports recursive locking, but no waiting
92
105
* TODO: We should move away from using the recursive lock by default.
93
106
*/
94
- class CCriticalSection : public AnnotatedMixin <std::recursive_mutex>
95
- {
96
- public:
97
- ~CCriticalSection () {
98
- DeleteLock ((void *)this );
99
- }
100
- };
107
+ typedef AnnotatedMixin<std::recursive_mutex> CCriticalSection;
101
108
102
109
/* * Wrapped mutex: supports waiting but not recursive locking */
103
110
typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection;
104
111
105
112
/* * Just a typedef for std::condition_variable, can be wrapped later if desired */
106
113
typedef std::condition_variable CConditionVariable;
107
114
108
- /* * Just a typedef for std::unique_lock, can be wrapped later if desired */
109
- typedef std::unique_lock<std::mutex> WaitableLock;
110
-
111
115
#ifdef DEBUG_LOCKCONTENTION
112
116
void PrintLockContention (const char * pszName, const char * pszFile, int nLine);
113
117
#endif
114
118
115
- /* * Wrapper around std::unique_lock<CCriticalSection> */
116
- class SCOPED_LOCKABLE CCriticalBlock
119
+ /* * Wrapper around std::unique_lock style lock for Mutex. */
120
+ template <typename Mutex, typename Base = typename Mutex::UniqueLock>
121
+ class SCOPED_LOCKABLE CCriticalBlock : public Base
117
122
{
118
123
private:
119
- std::unique_lock<CCriticalSection> lock;
120
-
121
124
void Enter (const char * pszName, const char * pszFile, int nLine)
122
125
{
123
- EnterCritical (pszName, pszFile, nLine, (void *)(lock. mutex ()));
126
+ EnterCritical (pszName, pszFile, nLine, (void *)(Base:: mutex ()));
124
127
#ifdef DEBUG_LOCKCONTENTION
125
- if (!lock. try_lock ()) {
128
+ if (!Base:: try_lock ()) {
126
129
PrintLockContention (pszName, pszFile, nLine);
127
130
#endif
128
- lock. lock ();
131
+ Base:: lock ();
129
132
#ifdef DEBUG_LOCKCONTENTION
130
133
}
131
134
#endif
132
135
}
133
136
134
137
bool TryEnter (const char * pszName, const char * pszFile, int nLine)
135
138
{
136
- EnterCritical (pszName, pszFile, nLine, (void *)(lock. mutex ()), true );
137
- lock. try_lock ();
138
- if (!lock. owns_lock ())
139
+ EnterCritical (pszName, pszFile, nLine, (void *)(Base:: mutex ()), true );
140
+ Base:: try_lock ();
141
+ if (!Base:: owns_lock ())
139
142
LeaveCritical ();
140
- return lock. owns_lock ();
143
+ return Base:: owns_lock ();
141
144
}
142
145
143
146
public:
144
- CCriticalBlock (CCriticalSection & mutexIn, const char * pszName, const char * pszFile, int nLine, bool fTry = false ) EXCLUSIVE_LOCK_FUNCTION (mutexIn) : lock (mutexIn, std::defer_lock)
147
+ CCriticalBlock (Mutex & mutexIn, const char * pszName, const char * pszFile, int nLine, bool fTry = false ) EXCLUSIVE_LOCK_FUNCTION (mutexIn) : Base (mutexIn, std::defer_lock)
145
148
{
146
149
if (fTry )
147
150
TryEnter (pszName, pszFile, nLine);
148
151
else
149
152
Enter (pszName, pszFile, nLine);
150
153
}
151
154
152
- CCriticalBlock (CCriticalSection * pmutexIn, const char * pszName, const char * pszFile, int nLine, bool fTry = false ) EXCLUSIVE_LOCK_FUNCTION (pmutexIn)
155
+ CCriticalBlock (Mutex * pmutexIn, const char * pszName, const char * pszFile, int nLine, bool fTry = false ) EXCLUSIVE_LOCK_FUNCTION (pmutexIn)
153
156
{
154
157
if (!pmutexIn) return ;
155
158
156
- lock = std::unique_lock<CCriticalSection> (*pmutexIn, std::defer_lock);
159
+ * static_cast <Base*>( this ) = Base (*pmutexIn, std::defer_lock);
157
160
if (fTry )
158
161
TryEnter (pszName, pszFile, nLine);
159
162
else
@@ -162,22 +165,28 @@ class SCOPED_LOCKABLE CCriticalBlock
162
165
163
166
~CCriticalBlock () UNLOCK_FUNCTION ()
164
167
{
165
- if (lock. owns_lock ())
168
+ if (Base:: owns_lock ())
166
169
LeaveCritical ();
167
170
}
168
171
169
172
operator bool ()
170
173
{
171
- return lock. owns_lock ();
174
+ return Base:: owns_lock ();
172
175
}
173
176
};
174
177
178
+ template <typename MutexArg>
179
+ using DebugLock = CCriticalBlock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
180
+
175
181
#define PASTE (x, y ) x ## y
176
182
#define PASTE2 (x, y ) PASTE(x, y)
177
183
178
- #define LOCK (cs ) CCriticalBlock PASTE2 (criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
179
- #define LOCK2 (cs1, cs2 ) CCriticalBlock criticalblock1 (cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__)
180
- #define TRY_LOCK (cs, name ) CCriticalBlock name (cs, #cs, __FILE__, __LINE__, true )
184
+ #define LOCK (cs ) DebugLock<decltype (cs)> PASTE2 (criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
185
+ #define LOCK2 (cs1, cs2 ) \
186
+ DebugLock<decltype (cs1)> criticalblock1 (cs1, #cs1, __FILE__, __LINE__); \
187
+ DebugLock<decltype (cs2)> criticalblock2 (cs2, #cs2, __FILE__, __LINE__);
188
+ #define TRY_LOCK (cs, name ) DebugLock<decltype (cs)> name (cs, #cs, __FILE__, __LINE__, true )
189
+ #define WAIT_LOCK (cs, name ) DebugLock<decltype (cs)> name (cs, #cs, __FILE__, __LINE__)
181
190
182
191
#define ENTER_CRITICAL_SECTION (cs ) \
183
192
{ \
0 commit comments