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