Panda3D
mutexDebug.cxx
1 // Filename: mutexDebug.cxx
2 // Created by: drose (13Feb06)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "mutexDebug.h"
16 #include "thread.h"
17 #include "config_pipeline.h"
18 
19 #ifdef DEBUG_THREADS
20 
21 int MutexDebug::_pstats_count = 0;
22 MutexTrueImpl *MutexDebug::_global_lock;
23 
24 ////////////////////////////////////////////////////////////////////
25 // Function: MutexDebug::Constructor
26 // Access: Protected
27 // Description:
28 ////////////////////////////////////////////////////////////////////
29 MutexDebug::
30 MutexDebug(const string &name, bool allow_recursion, bool lightweight) :
31  Namable(name),
32  _allow_recursion(allow_recursion),
33  _lightweight(lightweight),
34  _locking_thread(NULL),
35  _lock_count(0),
36  _deleted_name(NULL),
37  _cvar_impl(*get_global_lock())
38 {
39 #ifndef SIMPLE_THREADS
40  // If we're using real threads, there's no such thing as a
41  // lightweight mutex.
42  _lightweight = false;
43 #endif
44 }
45 
46 ////////////////////////////////////////////////////////////////////
47 // Function: MutexDebug::Destructor
48 // Access: Protected, Virtual
49 // Description:
50 ////////////////////////////////////////////////////////////////////
51 MutexDebug::
52 ~MutexDebug() {
53  nassertv(_locking_thread == NULL && _lock_count == 0);
54 
55  // If the config variable says to, allocate (and leak) a string name
56  // for the mutex, so we can report which mutex it is that has
57  // destructed after the fact.
58  if (name_deleted_mutexes) {
59  ostringstream strm;
60  strm << *this;
61  string name = strm.str();
62  _deleted_name = strdup((char *)name.c_str());
63  }
64 
65  // Put a distinctive, bogus lock count in upon destruction, so we'll
66  // be more likely to notice a floating pointer.
67  _lock_count = -100;
68 }
69 
70 ////////////////////////////////////////////////////////////////////
71 // Function: MutexDebug::output
72 // Access: Public, Virtual
73 // Description: This method is declared virtual in MutexDebug, but
74 // non-virtual in MutexDirect.
75 ////////////////////////////////////////////////////////////////////
76 void MutexDebug::
77 output(ostream &out) const {
78  if (_lightweight) {
79  out << "Light";
80  }
81  if (_allow_recursion) {
82  out << "ReMutex " << get_name() << " " << (void *)this;
83  } else {
84  out << "Mutex " << get_name() << " " << (void *)this;
85  }
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: MutexDebug::output_with_holder
90 // Access: Public
91 // Description: Reports the mutex as well as the thread that is
92 // currently holding it, if any.
93 ////////////////////////////////////////////////////////////////////
94 void MutexDebug::
95 output_with_holder(ostream &out) const {
96  _global_lock->acquire();
97  output(out);
98  if (_locking_thread != (Thread *)NULL) {
99  out << " (held by " << *_locking_thread << ")\n";
100  }
101  _global_lock->release();
102 }
103 
104 ////////////////////////////////////////////////////////////////////
105 // Function: MutexDebug::increment_pstats
106 // Access: Public, Static
107 // Description: Intended to be called only by
108 // PStatClientImpl::client_connect(), this tells the
109 // global mutex system that PStats is active. Once
110 // PStats is active, all "light" mutexes are treated the
111 // same as full mutexes.
112 ////////////////////////////////////////////////////////////////////
113 void MutexDebug::
114 increment_pstats() {
115  _global_lock->acquire();
116  ++_pstats_count;
117  _global_lock->release();
118 }
119 
120 ////////////////////////////////////////////////////////////////////
121 // Function: MutexDebug::decrement_pstats
122 // Access: Public, Static
123 // Description: Intended to be called only by
124 // PStatClientImpl::client_disconnect(), this tells the
125 // global mutex system that PStats is no longer active.
126 ////////////////////////////////////////////////////////////////////
127 void MutexDebug::
128 decrement_pstats() {
129  _global_lock->acquire();
130  --_pstats_count;
131  _global_lock->release();
132 }
133 
134 ////////////////////////////////////////////////////////////////////
135 // Function: MutexDebug::do_acquire
136 // Access: Private
137 // Description: The private implementation of acquire() assumes that
138 // _lock_impl is held.
139 ////////////////////////////////////////////////////////////////////
140 void MutexDebug::
141 do_acquire(Thread *current_thread) {
142  // If this assertion is triggered, you tried to lock a
143  // recently-destructed mutex.
144  nassertd(_lock_count != -100) {
145  pipeline_cat.error()
146  << "Destructed mutex: " << (void *)this << "\n";
147  if (name_deleted_mutexes && _deleted_name != NULL) {
148  pipeline_cat.error()
149  << _deleted_name << "\n";
150  } else {
151  pipeline_cat.error()
152  << "Configure name-deleted-mutexes 1 to see the mutex name.\n";
153  }
154  return;
155  }
156 
157  if (_locking_thread == (Thread *)NULL) {
158  // The mutex is not already locked by anyone. Lock it.
159  _locking_thread = current_thread;
160  ++_lock_count;
161  nassertv(_lock_count == 1);
162 
163  } else if (_locking_thread == current_thread) {
164  // The mutex is already locked by this thread. Increment the lock
165  // count.
166  nassertv(_lock_count > 0);
167  if (!_allow_recursion) {
168  ostringstream ostr;
169  ostr << *current_thread << " attempted to double-lock non-reentrant "
170  << *this;
171  nassert_raise(ostr.str());
172  }
173  ++_lock_count;
174 
175  } else {
176  // The mutex is locked by some other thread.
177 
178  if (_lightweight && _pstats_count == 0) {
179  // In this case, it's not a real mutex. Just watch it go by.
180  MissedThreads::iterator mi = _missed_threads.insert(MissedThreads::value_type(current_thread, 0)).first;
181  if ((*mi).second == 0) {
182  ostringstream ostr;
183  ostr << *current_thread << " not stopped by " << *this << " (held by "
184  << *_locking_thread << ")\n";
185  nassert_raise(ostr.str());
186  } else {
187  if (!_allow_recursion) {
188  ostringstream ostr;
189  ostr << *current_thread << " attempted to double-lock non-reentrant "
190  << *this;
191  nassert_raise(ostr.str());
192  }
193  }
194  ++((*mi).second);
195 
196  } else {
197  // This is the real case. It's a real mutex, so block if necessary.
198 
199  // Check for deadlock.
200  MutexDebug *next_mutex = this;
201  while (next_mutex != NULL) {
202  if (next_mutex->_locking_thread == current_thread) {
203  // Whoops, the thread is blocked on me! Deadlock!
204  report_deadlock(current_thread);
205  nassert_raise("Deadlock");
206  return;
207  }
208  Thread *next_thread = next_mutex->_locking_thread;
209  if (next_thread == NULL) {
210  // Looks like this mutex isn't actually locked, which means
211  // the last thread isn't really blocked--it just hasn't woken
212  // up yet to discover that. In any case, no deadlock.
213  break;
214  }
215 
216  // The last thread is blocked on this "next thread"'s mutex, but
217  // what mutex is the next thread blocked on?
218  next_mutex = next_thread->_blocked_on_mutex;
219  }
220 
221  // OK, no deadlock detected. Carry on.
222  current_thread->_blocked_on_mutex = this;
223 
224  // Go to sleep on the condition variable until it's unlocked.
225 
226  if (thread_cat->is_debug()) {
227  thread_cat.debug()
228  << *current_thread << " blocking on " << *this << " (held by "
229  << *_locking_thread << ")\n";
230  }
231 
232  while (_locking_thread != (Thread *)NULL) {
233  thread_cat.debug()
234  << *current_thread << " still blocking on " << *this << " (held by "
235  << *_locking_thread << ")\n";
236  _cvar_impl.wait();
237  }
238 
239  if (thread_cat.is_debug()) {
240  thread_cat.debug()
241  << *current_thread << " acquired " << *this << "\n";
242  }
243 
244  current_thread->_blocked_on_mutex = NULL;
245 
246  _locking_thread = current_thread;
247  ++_lock_count;
248  nassertv(_lock_count == 1);
249  }
250  }
251 }
252 
253 ////////////////////////////////////////////////////////////////////
254 // Function: MutexDebug::do_try_acquire
255 // Access: Private
256 // Description: The private implementation of acquire(false) assumes
257 // that _lock_impl is held.
258 ////////////////////////////////////////////////////////////////////
259 bool MutexDebug::
260 do_try_acquire(Thread *current_thread) {
261  // If this assertion is triggered, you tried to lock a
262  // recently-destructed mutex.
263  nassertd(_lock_count != -100) {
264  pipeline_cat.error()
265  << "Destructed mutex: " << (void *)this << "\n";
266  if (name_deleted_mutexes && _deleted_name != NULL) {
267  pipeline_cat.error()
268  << _deleted_name << "\n";
269  } else {
270  pipeline_cat.error()
271  << "Configure name-deleted-mutexes 1 to see the mutex name.\n";
272  }
273  return false;
274  }
275 
276  bool acquired = true;
277  if (_locking_thread == (Thread *)NULL) {
278  // The mutex is not already locked by anyone. Lock it.
279  _locking_thread = current_thread;
280  ++_lock_count;
281  nassertr(_lock_count == 1, false);
282 
283  } else if (_locking_thread == current_thread) {
284  // The mutex is already locked by this thread. Increment the lock
285  // count.
286  nassertr(_lock_count > 0, false);
287  if (!_allow_recursion) {
288  // Non-recursive lock; return false.
289  acquired = false;
290  } else {
291  ++_lock_count;
292  }
293 
294  } else {
295  // The mutex is locked by some other thread. Return false.
296 
297  if (_lightweight && _pstats_count == 0) {
298  // In this case, it's not a real mutex. Just watch it go by.
299  MissedThreads::iterator mi = _missed_threads.insert(MissedThreads::value_type(current_thread, 0)).first;
300  if ((*mi).second == 0) {
301  thread_cat.info()
302  << *current_thread << " not stopped by " << *this << " (held by "
303  << *_locking_thread << ")\n";
304  } else {
305  if (!_allow_recursion) {
306  ostringstream ostr;
307  ostr << *current_thread << " attempted to double-lock non-reentrant "
308  << *this;
309  nassert_raise(ostr.str());
310  }
311  }
312  ++((*mi).second);
313 
314  } else {
315  // This is the real case.
316  acquired = false;
317  }
318  }
319 
320  return acquired;
321 }
322 
323 ////////////////////////////////////////////////////////////////////
324 // Function: MutexDebug::do_release
325 // Access: Private
326 // Description: The private implementation of acquire() assumes that
327 // _lock_impl is held.
328 ////////////////////////////////////////////////////////////////////
329 void MutexDebug::
330 do_release() {
331  // If this assertion is triggered, you tried to release a
332  // recently-destructed mutex.
333  nassertd(_lock_count != -100) {
334  pipeline_cat.error()
335  << "Destructed mutex: " << (void *)this << "\n";
336  if (name_deleted_mutexes && _deleted_name != NULL) {
337  pipeline_cat.error()
338  << _deleted_name << "\n";
339  } else {
340  pipeline_cat.error()
341  << "Configure name-deleted-mutexes 1 to see the mutex name.\n";
342  }
343  return;
344  }
345 
346  Thread *current_thread = Thread::get_current_thread();
347 
348  if (_locking_thread != current_thread) {
349  // We're not holding this mutex.
350 
351  if (_lightweight) {
352  // Not a real mutex. This just means we blew past a mutex
353  // without locking it, above.
354 
355  MissedThreads::iterator mi = _missed_threads.find(current_thread);
356  nassertv(mi != _missed_threads.end());
357  nassertv((*mi).second > 0);
358  --((*mi).second);
359 
360  if ((*mi).second == 0) {
361  _missed_threads.erase(mi);
362  }
363 
364  } else {
365  // In the real-mutex case, this is an error condition.
366  ostringstream ostr;
367  ostr << *current_thread << " attempted to release "
368  << *this << " which it does not own";
369  nassert_raise(ostr.str());
370  }
371  return;
372  }
373 
374  nassertv(_lock_count > 0);
375 
376  --_lock_count;
377  if (_lock_count == 0) {
378  // That was the last lock held by this thread. Release the lock.
379  _locking_thread = (Thread *)NULL;
380 
381  if (_lightweight) {
382  if (!_missed_threads.empty()) {
383  // Promote some other thread to be the honorary lock holder.
384  MissedThreads::iterator mi = _missed_threads.begin();
385  _locking_thread = (*mi).first;
386  _lock_count = (*mi).second;
387  _missed_threads.erase(mi);
388  nassertv(_lock_count > 0);
389  }
390  } else {
391 
392  /*
393  if (thread_cat.is_debug()) {
394  thread_cat.debug()
395  << *current_thread << " releasing " << *this << "\n";
396  }
397  */
398  _cvar_impl.notify();
399  }
400  }
401 }
402 
403 ////////////////////////////////////////////////////////////////////
404 // Function: MutexDebug::do_debug_is_locked
405 // Access: Private
406 // Description: The private implementation of debug_is_locked()
407 // assumes that _lock_impl is held.
408 ////////////////////////////////////////////////////////////////////
409 bool MutexDebug::
410 do_debug_is_locked() const {
411  Thread *current_thread = Thread::get_current_thread();
412  if (_locking_thread == current_thread) {
413  return true;
414  }
415 
416  if (_lightweight) {
417  MissedThreads::const_iterator mi = _missed_threads.find(current_thread);
418  if (mi != _missed_threads.end()) {
419  nassertr((*mi).second > 0, false);
420  return true;
421  }
422  }
423 
424  return false;
425 }
426 
427 ////////////////////////////////////////////////////////////////////
428 // Function: MutexDebug::report_deadlock
429 // Access: Private
430 // Description: Reports a detected deadlock situation. _lock_impl
431 // should be already held.
432 ////////////////////////////////////////////////////////////////////
433 void MutexDebug::
434 report_deadlock(Thread *current_thread) {
435  thread_cat->error()
436  << "\n\n"
437  << "****************************************************************\n"
438  << "***** Deadlock detected! *****\n"
439  << "****************************************************************\n"
440  << "\n";
441 
442  thread_cat.error()
443  << *current_thread << " attempted to lock " << *this
444  << " which is held by " << *_locking_thread << "\n";
445 
446  MutexDebug *next_mutex = this;
447  Thread *next_thread = next_mutex->_locking_thread;
448  next_mutex = next_thread->_blocked_on_mutex;
449  while (next_mutex != NULL) {
450  thread_cat.error()
451  << *next_thread << " is blocked waiting on "
452  << *next_mutex << " which is held by "
453  << *next_mutex->_locking_thread << "\n";
454  next_thread = next_mutex->_locking_thread;
455  next_mutex = next_thread->_blocked_on_mutex;
456  }
457 
458  thread_cat.error()
459  << "Deadlock!\n";
460 }
461 
462 #endif // DEBUG_THREADS
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
A base class for all things which can have a name.
Definition: namable.h:29
A thread; that is, a lightweight process.
Definition: thread.h:51
A fake mutex implementation for single-threaded applications that don&#39;t need any synchronization cont...