Panda3D
conditionVarFullDebug.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file conditionVarFullDebug.cxx
10  * @author drose
11  * @date 2006-08-28
12  */
13 
14 #include "conditionVarFullDebug.h"
15 #include "thread.h"
16 #include "config_pipeline.h"
17 
18 #ifdef DEBUG_THREADS
19 
20 using std::ostream;
21 using std::ostringstream;
22 
23 /**
24  * You must pass in a Mutex to the condition variable constructor. This mutex
25  * may be shared by other condition variables, if desired. It is the caller's
26  * responsibility to ensure the Mutex object does not destruct during the
27  * lifetime of the condition variable.
28  */
29 ConditionVarFullDebug::
30 ConditionVarFullDebug(MutexDebug &mutex) :
31  _mutex(mutex),
32  _impl(*mutex.get_global_lock())
33 {
34  nassertv(!_mutex._allow_recursion);
35 }
36 
37 /**
38  *
39  */
40 ConditionVarFullDebug::
41 ~ConditionVarFullDebug() {
42 }
43 
44 /**
45  * Waits on the condition. The caller must already be holding the lock
46  * associated with the condition variable before calling this function.
47  *
48  * wait() will release the lock, then go to sleep until some other thread
49  * calls notify() on this condition variable. At that time at least one
50  * thread waiting on the same ConditionVarFullDebug will grab the lock again,
51  * and then return from wait().
52  *
53  * It is possible that wait() will return even if no one has called notify().
54  * It is the responsibility of the calling process to verify the condition on
55  * return from wait, and possibly loop back to wait again if necessary.
56  *
57  * Note the semantics of a condition variable: the mutex must be held before
58  * wait() is called, and it will still be held when wait() returns. However,
59  * it will be temporarily released during the wait() call itself.
60  */
61 void ConditionVarFullDebug::
62 wait() {
63  _mutex._global_lock->lock();
64 
65  Thread *current_thread = Thread::get_current_thread();
66 
67  if (!_mutex.do_debug_is_locked()) {
68  ostringstream ostr;
69  ostr << *current_thread << " attempted to wait on "
70  << *this << " without holding " << _mutex;
71  nassert_raise(ostr.str());
72  _mutex._global_lock->unlock();
73  return;
74  }
75 
76  if (thread_cat->is_spam()) {
77  thread_cat.spam()
78  << *current_thread << " waiting on " << *this << "\n";
79  }
80 
81  nassertd(current_thread->_waiting_on_cvar == nullptr &&
82  current_thread->_waiting_on_cvar_full == nullptr) {
83  }
84  current_thread->_waiting_on_cvar_full = this;
85 
86  _mutex.do_unlock();
87  _impl.wait(); // temporarily releases _global_lock
88  _mutex.do_lock(current_thread);
89 
90  nassertd(current_thread->_waiting_on_cvar_full == this) {
91  }
92  current_thread->_waiting_on_cvar_full = nullptr;
93 
94  if (thread_cat.is_spam()) {
95  thread_cat.spam()
96  << *current_thread << " awake on " << *this << "\n";
97  }
98 
99  _mutex._global_lock->unlock();
100 }
101 
102 /**
103  * Waits on the condition, with a timeout. The function will return when the
104  * condition variable is notified, or the timeout occurs. There is no way to
105  * directly tell which happened, and it is possible that neither in fact
106  * happened (spurious wakeups are possible).
107  *
108  * See wait() with no parameters for more.
109  */
110 void ConditionVarFullDebug::
111 wait(double timeout) {
112  _mutex._global_lock->lock();
113 
114  Thread *current_thread = Thread::get_current_thread();
115 
116  if (!_mutex.do_debug_is_locked()) {
117  ostringstream ostr;
118  ostr << *current_thread << " attempted to wait on "
119  << *this << " without holding " << _mutex;
120  nassert_raise(ostr.str());
121  _mutex._global_lock->unlock();
122  return;
123  }
124 
125  if (thread_cat.is_spam()) {
126  thread_cat.spam()
127  << *current_thread << " waiting on " << *this
128  << ", with timeout " << timeout << "\n";
129  }
130 
131  nassertd(current_thread->_waiting_on_cvar == nullptr &&
132  current_thread->_waiting_on_cvar_full == nullptr) {
133  }
134  current_thread->_waiting_on_cvar_full = this;
135 
136  _mutex.do_unlock();
137  _impl.wait(timeout); // temporarily releases _global_lock
138  _mutex.do_lock(current_thread);
139 
140  nassertd(current_thread->_waiting_on_cvar_full == this) {
141  }
142  current_thread->_waiting_on_cvar_full = nullptr;
143 
144  if (thread_cat.is_spam()) {
145  thread_cat.spam()
146  << *current_thread << " awake on " << *this << "\n";
147  }
148 
149  _mutex._global_lock->unlock();
150 }
151 
152 /**
153  * Informs one of the other threads who are currently blocked on wait() that
154  * the relevant condition has changed. If multiple threads are currently
155  * waiting, at least one of them will be woken up, although there is no way to
156  * predict which one. It is possible that more than one thread will be woken
157  * up.
158  *
159  * The caller must be holding the mutex associated with the condition variable
160  * before making this call, which will not release the mutex.
161  *
162  * If no threads are waiting, this is a no-op: the notify event is lost.
163  */
164 void ConditionVarFullDebug::
165 notify() {
166  _mutex._global_lock->lock();
167 
168  Thread *current_thread = Thread::get_current_thread();
169 
170  if (!_mutex.do_debug_is_locked()) {
171  ostringstream ostr;
172  ostr << *current_thread << " attempted to notify "
173  << *this << " without holding " << _mutex;
174  nassert_raise(ostr.str());
175  _mutex._global_lock->unlock();
176  return;
177  }
178 
179  if (thread_cat->is_spam()) {
180  thread_cat.spam()
181  << *current_thread << " notifying " << *this << "\n";
182  }
183 
184  _impl.notify();
185  _mutex._global_lock->unlock();
186 }
187 
188 /**
189  * Informs all of the other threads who are currently blocked on wait() that
190  * the relevant condition has changed.
191  *
192  * The caller must be holding the mutex associated with the condition variable
193  * before making this call, which will not release the mutex.
194  *
195  * If no threads are waiting, this is a no-op: the notify event is lost.
196  */
197 void ConditionVarFullDebug::
198 notify_all() {
199  _mutex._global_lock->lock();
200 
201  Thread *current_thread = Thread::get_current_thread();
202 
203  if (!_mutex.do_debug_is_locked()) {
204  ostringstream ostr;
205  ostr << *current_thread << " attempted to notify "
206  << *this << " without holding " << _mutex;
207  nassert_raise(ostr.str());
208  _mutex._global_lock->unlock();
209  return;
210  }
211 
212  if (thread_cat->is_spam()) {
213  thread_cat.spam()
214  << *current_thread << " notifying all " << *this << "\n";
215  }
216 
217  _impl.notify_all();
218  _mutex._global_lock->unlock();
219 }
220 
221 /**
222  * This method is declared virtual in ConditionVarFullDebug, but non-virtual
223  * in ConditionVarFullDirect.
224  */
225 void ConditionVarFullDebug::
226 output(ostream &out) const {
227  out << "ConditionVarFull " << (void *)this << " on " << _mutex;
228 }
229 
230 #endif // DEBUG_THREADS
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
Definition: thread.h:46