Panda3D
conditionVarDebug.cxx
1 // Filename: conditionVarDebug.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 "conditionVarDebug.h"
16 #include "thread.h"
17 #include "config_pipeline.h"
18 
19 #ifdef DEBUG_THREADS
20 
21 ////////////////////////////////////////////////////////////////////
22 // Function: ConditionVarDebug::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 ConditionVarDebug::
32 ConditionVarDebug(MutexDebug &mutex) :
33  _mutex(mutex),
34  _impl(*mutex.get_global_lock())
35 {
36  nassertv(!_mutex._allow_recursion);
37 }
38 
39 ////////////////////////////////////////////////////////////////////
40 // Function: ConditionVarDebug::Destructor
41 // Access: Public, Virtual
42 // Description:
43 ////////////////////////////////////////////////////////////////////
44 ConditionVarDebug::
45 ~ConditionVarDebug() {
46 }
47 
48 ////////////////////////////////////////////////////////////////////
49 // Function: ConditionVarDebug::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 ConditionVarDebug 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 ConditionVarDebug::
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 = 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 == this) {
103  }
104  current_thread->_waiting_on_cvar = 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: ConditionVarDebug::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 ConditionVarDebug::
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 = 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 == this) {
156  }
157  current_thread->_waiting_on_cvar = 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: ConditionVarDebug::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 ConditionVarDebug::
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: ConditionVarDebug::output
210 // Access: Published, Virtual
211 // Description: This method is declared virtual in ConditionVarDebug,
212 // but non-virtual in ConditionVarDirect.
213 ////////////////////////////////////////////////////////////////////
214 void ConditionVarDebug::
215 output(ostream &out) const {
216  out << "ConditionVar " << (void *)this << " on " << _mutex;
217 }
218 
219 #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