7
7
#endif
8
8
9
9
#include < sync.h>
10
- #include < tinyformat.h>
11
10
12
11
#include < logging.h>
12
+ #include < tinyformat.h>
13
13
#include < util/strencodings.h>
14
14
#include < util/threadnames.h>
15
15
16
16
#include < map>
17
17
#include < set>
18
18
#include < system_error>
19
+ #include < thread>
20
+ #include < unordered_map>
21
+ #include < utility>
22
+ #include < vector>
19
23
20
24
#ifdef DEBUG_LOCKCONTENTION
21
25
#if !defined(HAVE_THREAD_LOCAL)
@@ -73,35 +77,35 @@ struct CLockLocation {
73
77
int sourceLine;
74
78
};
75
79
76
- typedef std::vector<std:: pair<void *, CLockLocation> > LockStack ;
77
- typedef std::map<std::pair< void *, void *>, LockStack> LockOrders ;
78
- typedef std::set <std::pair< void *, void *> > InvLockOrders ;
80
+ using LockStackItem = std::pair<void *, CLockLocation>;
81
+ using LockStack = std::vector<LockStackItem> ;
82
+ using LockStacks = std::unordered_map <std::thread::id, LockStack> ;
79
83
80
- struct LockData {
81
- // Very ugly hack: as the global constructs and destructors run single
82
- // threaded, we use this boolean to know whether LockData still exists,
83
- // as DeleteLock can get called by global RecursiveMutex destructors
84
- // after LockData disappears.
85
- bool available;
86
- LockData () : available(true ) {}
87
- ~LockData () { available = false ; }
84
+ using LockPair = std::pair<void *, void *>;
85
+ using LockOrders = std::map<LockPair, LockStack>;
86
+ using InvLockOrders = std::set<LockPair>;
88
87
88
+ struct LockData {
89
+ LockStacks m_lock_stacks;
89
90
LockOrders lockorders;
90
91
InvLockOrders invlockorders;
91
92
std::mutex dd_mutex;
92
93
};
94
+
93
95
LockData& GetLockData () {
94
- static LockData lockdata;
95
- return lockdata;
96
+ // This approach guarantees that the object is not destroyed until after its last use.
97
+ // The operating system automatically reclaims all the memory in a program's heap when that program exits.
98
+ // Since the ~LockData() destructor is never called, the LockData class and all
99
+ // its subclasses must have implicitly-defined destructors.
100
+ static LockData& lock_data = *new LockData ();
101
+ return lock_data;
96
102
}
97
103
98
- static thread_local LockStack g_lockstack;
99
-
100
- static void potential_deadlock_detected (const std::pair<void *, void *>& mismatch, const LockStack& s1, const LockStack& s2)
104
+ static void potential_deadlock_detected (const LockPair& mismatch, const LockStack& s1, const LockStack& s2)
101
105
{
102
106
LogPrintf (" POTENTIAL DEADLOCK DETECTED\n " );
103
107
LogPrintf (" Previous lock order was:\n " );
104
- for (const std::pair< void *, CLockLocation> & i : s2) {
108
+ for (const LockStackItem & i : s2) {
105
109
if (i.first == mismatch.first ) {
106
110
LogPrintf (" (1)" ); /* Continued */
107
111
}
@@ -111,7 +115,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
111
115
LogPrintf (" %s\n " , i.second .ToString ());
112
116
}
113
117
LogPrintf (" Current lock order is:\n " );
114
- for (const std::pair< void *, CLockLocation> & i : s1) {
118
+ for (const LockStackItem & i : s1) {
115
119
if (i.first == mismatch.first ) {
116
120
LogPrintf (" (1)" ); /* Continued */
117
121
}
@@ -132,18 +136,18 @@ static void push_lock(void* c, const CLockLocation& locklocation)
132
136
LockData& lockdata = GetLockData ();
133
137
std::lock_guard<std::mutex> lock (lockdata.dd_mutex );
134
138
135
- g_lockstack. push_back ( std::make_pair (c, locklocation)) ;
136
-
137
- for (const std::pair< void *, CLockLocation> & i : g_lockstack ) {
139
+ LockStack& lock_stack = lockdata. m_lock_stacks [ std::this_thread::get_id ()] ;
140
+ lock_stack. emplace_back (c, locklocation);
141
+ for (const LockStackItem & i : lock_stack ) {
138
142
if (i.first == c)
139
143
break ;
140
144
141
- std::pair< void *, void *> p1 = std::make_pair (i.first , c);
145
+ const LockPair p1 = std::make_pair (i.first , c);
142
146
if (lockdata.lockorders .count (p1))
143
147
continue ;
144
- lockdata.lockorders .emplace (p1, g_lockstack );
148
+ lockdata.lockorders .emplace (p1, lock_stack );
145
149
146
- std::pair< void *, void *> p2 = std::make_pair (c, i.first );
150
+ const LockPair p2 = std::make_pair (c, i.first );
147
151
lockdata.invlockorders .insert (p2);
148
152
if (lockdata.lockorders .count (p2))
149
153
potential_deadlock_detected (p1, lockdata.lockorders [p2], lockdata.lockorders [p1]);
@@ -152,7 +156,14 @@ static void push_lock(void* c, const CLockLocation& locklocation)
152
156
153
157
static void pop_lock ()
154
158
{
155
- g_lockstack.pop_back ();
159
+ LockData& lockdata = GetLockData ();
160
+ std::lock_guard<std::mutex> lock (lockdata.dd_mutex );
161
+
162
+ LockStack& lock_stack = lockdata.m_lock_stacks [std::this_thread::get_id ()];
163
+ lock_stack.pop_back ();
164
+ if (lock_stack.empty ()) {
165
+ lockdata.m_lock_stacks .erase (std::this_thread::get_id ());
166
+ }
156
167
}
157
168
158
169
void EnterCritical (const char * pszName, const char * pszFile, int nLine, void * cs, bool fTry )
@@ -162,11 +173,17 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs
162
173
163
174
void CheckLastCritical (void * cs, std::string& lockname, const char * guardname, const char * file, int line)
164
175
{
165
- if (!g_lockstack.empty ()) {
166
- const auto & lastlock = g_lockstack.back ();
167
- if (lastlock.first == cs) {
168
- lockname = lastlock.second .Name ();
169
- return ;
176
+ {
177
+ LockData& lockdata = GetLockData ();
178
+ std::lock_guard<std::mutex> lock (lockdata.dd_mutex );
179
+
180
+ const LockStack& lock_stack = lockdata.m_lock_stacks [std::this_thread::get_id ()];
181
+ if (!lock_stack.empty ()) {
182
+ const auto & lastlock = lock_stack.back ();
183
+ if (lastlock.first == cs) {
184
+ lockname = lastlock.second .Name ();
185
+ return ;
186
+ }
170
187
}
171
188
}
172
189
throw std::system_error (EPERM, std::generic_category (), strprintf (" %s:%s %s was not most recent critical section locked" , file, line, guardname));
@@ -179,49 +196,57 @@ void LeaveCritical()
179
196
180
197
std::string LocksHeld ()
181
198
{
199
+ LockData& lockdata = GetLockData ();
200
+ std::lock_guard<std::mutex> lock (lockdata.dd_mutex );
201
+
202
+ const LockStack& lock_stack = lockdata.m_lock_stacks [std::this_thread::get_id ()];
182
203
std::string result;
183
- for (const std::pair< void *, CLockLocation> & i : g_lockstack )
204
+ for (const LockStackItem & i : lock_stack )
184
205
result += i.second .ToString () + std::string (" \n " );
185
206
return result;
186
207
}
187
208
209
+ static bool LockHeld (void * mutex)
210
+ {
211
+ LockData& lockdata = GetLockData ();
212
+ std::lock_guard<std::mutex> lock (lockdata.dd_mutex );
213
+
214
+ const LockStack& lock_stack = lockdata.m_lock_stacks [std::this_thread::get_id ()];
215
+ for (const LockStackItem& i : lock_stack) {
216
+ if (i.first == mutex) return true ;
217
+ }
218
+
219
+ return false ;
220
+ }
221
+
188
222
void AssertLockHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs)
189
223
{
190
- for (const std::pair<void *, CLockLocation>& i : g_lockstack)
191
- if (i.first == cs)
192
- return ;
224
+ if (LockHeld (cs)) return ;
193
225
tfm::format (std::cerr, " Assertion failed: lock %s not held in %s:%i; locks held:\n %s" , pszName, pszFile, nLine, LocksHeld ());
194
226
abort ();
195
227
}
196
228
197
229
void AssertLockNotHeldInternal (const char * pszName, const char * pszFile, int nLine, void * cs)
198
230
{
199
- for (const std::pair<void *, CLockLocation>& i : g_lockstack) {
200
- if (i.first == cs) {
201
- tfm::format (std::cerr, " Assertion failed: lock %s held in %s:%i; locks held:\n %s" , pszName, pszFile, nLine, LocksHeld ());
202
- abort ();
203
- }
204
- }
231
+ if (!LockHeld (cs)) return ;
232
+ tfm::format (std::cerr, " Assertion failed: lock %s held in %s:%i; locks held:\n %s" , pszName, pszFile, nLine, LocksHeld ());
233
+ abort ();
205
234
}
206
235
207
236
void DeleteLock (void * cs)
208
237
{
209
238
LockData& lockdata = GetLockData ();
210
- if (!lockdata.available ) {
211
- // We're already shutting down.
212
- return ;
213
- }
214
239
std::lock_guard<std::mutex> lock (lockdata.dd_mutex );
215
- std::pair< void *, void *> item = std::make_pair (cs, nullptr );
240
+ const LockPair item = std::make_pair (cs, nullptr );
216
241
LockOrders::iterator it = lockdata.lockorders .lower_bound (item);
217
242
while (it != lockdata.lockorders .end () && it->first .first == cs) {
218
- std::pair< void *, void *> invitem = std::make_pair (it->first .second , it->first .first );
243
+ const LockPair invitem = std::make_pair (it->first .second , it->first .first );
219
244
lockdata.invlockorders .erase (invitem);
220
245
lockdata.lockorders .erase (it++);
221
246
}
222
247
InvLockOrders::iterator invit = lockdata.invlockorders .lower_bound (item);
223
248
while (invit != lockdata.invlockorders .end () && invit->first == cs) {
224
- std::pair< void *, void *> invinvitem = std::make_pair (invit->second , invit->first );
249
+ const LockPair invinvitem = std::make_pair (invit->second , invit->first );
225
250
lockdata.lockorders .erase (invinvitem);
226
251
lockdata.invlockorders .erase (invit++);
227
252
}
0 commit comments