2 * Copyright (c) 2005, Eric Crahen
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is furnished
9 * to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 #ifndef __ZTCONDITIONIMPL_H__
24 #define __ZTCONDITIONIMPL_H__
26 #include "zthread/Guard.h"
29 #include "Scheduling.h"
30 #include "DeferredInterruptionScope.h"
35 * @class ConditionImpl
36 * @author Eric Crahen <http://www.code-foo.com>
37 * @date <2003-07-18T08:15:37-0400>
40 * The ConditionImpl template allows how waiter lists are sorted
43 template <typename List
>
46 //! Waiters currently blocked
49 //! Serialize access to this object
53 Lockable
& _predicateLock
;
58 * Create a new ConditionImpl.
60 * @exception Initialization_Exception thrown if resources could not be
63 ConditionImpl(Lockable
& predicateLock
) : _predicateLock(predicateLock
) {
68 * Destroy this ConditionImpl, release its resources
78 bool wait(unsigned long timeout
);
83 template <typename List
>
84 ConditionImpl
<List
>::~ConditionImpl() {
88 // It is an error to destroy a condition with threads waiting on it.
89 if(!_waiters
.empty()) {
91 ZTDEBUG("** You are destroying a condition variable which still has waiting threads. **\n");
102 * Signal the condition variable, waking one thread if any.
104 template <typename List
>
105 void ConditionImpl
<List
>::signal() {
107 Guard
<FastLock
> g1(_lock
);
109 // Try to find a waiter with a backoff & retry scheme
112 // Go through the list, attempt to notify() a waiter.
113 for(typename
List::iterator i
= _waiters
.begin(); i
!= _waiters
.end();) {
115 // Try the monitor lock, if it cant be locked skip to the next waiter
116 ThreadImpl
* impl
= *i
;
117 Monitor
& m
= impl
->getMonitor();
121 // Notify the monitor & remove from the waiter list so time isn't
122 // wasted checking it again.
123 i
= _waiters
.erase(i
);
125 // If notify() is not sucessful, it is because the wait() has already
126 // been ended (killed/interrupted/notify'd)
127 bool woke
= m
.notify();
131 // Once notify() succeeds, return
142 { // Backoff and try again
144 Guard
<FastLock
, UnlockedScope
> g2(g1
);
154 * Broadcast to the condition variable, waking all threads waiting at the time of
157 template <typename List
>
158 void ConditionImpl
<List
>::broadcast() {
160 Guard
<FastLock
> g1(_lock
);
162 // Try to find a waiter with a backoff & retry scheme
165 // Go through the list, attempt to notify() a waiter.
166 for(typename
List::iterator i
= _waiters
.begin(); i
!= _waiters
.end();) {
168 // Try the monitor lock, if it cant be locked skip to the next waiter
169 ThreadImpl
* impl
= *i
;
170 Monitor
& m
= impl
->getMonitor();
174 // Notify the monitor & remove from the waiter list so time isn't
175 // wasted checking it again.
176 i
= _waiters
.erase(i
);
178 // Try to wake the waiter, it doesn't matter if this is successful
179 // or not (only fails when the monitor is already going to stop waiting).
191 { // Backoff and try again
193 Guard
<FastLock
, UnlockedScope
> g2(g1
);
203 * Cause the currently executing thread to block until this ConditionImpl has
204 * been signaled, the threads state changes.
206 * @param predicate Lockable&
208 * @exception Interrupted_Exception thrown when the caller status is interrupted
209 * @exception Synchronization_Exception thrown if there is some other error.
211 template <typename List
>
212 void ConditionImpl
<List
>::wait() {
214 // Get the monitor for the current thread
215 ThreadImpl
* self
= ThreadImpl::current();
216 Monitor
& m
= self
->getMonitor();
218 Monitor::STATE state
;
222 Guard
<FastLock
> g1(_lock
);
224 // Release the _predicateLock
225 _predicateLock
.release();
227 // Stuff the waiter into the list
228 _waiters
.insert(self
);
230 // Move to the monitor's lock
235 Guard
<FastLock
, UnlockedScope
> g2(g1
);
240 // Move back to the Condition's lock
243 // Remove from waiter list, regarless of weather release() is called or
244 // not. The monitor is sticky, so its possible a state 'stuck' from a
245 // previous operation and will leave the wait() w/o release() having
247 typename
List::iterator i
= std::find(_waiters
.begin(), _waiters
.end(), self
);
248 if(i
!= _waiters
.end())
253 // Defer interruption until the external lock is acquire()d
254 Guard
<Monitor
, DeferredInterruptionScope
> g3(m
);
260 _predicateLock
.acquire(); // Should not throw
262 } catch(...) { assert(0); }
269 case Monitor::SIGNALED
:
272 case Monitor::INTERRUPTED
:
273 throw Interrupted_Exception();
276 throw Synchronization_Exception();
283 * Cause the currently executing thread to block until this ConditionImpl has
284 * been signaled, or the timeout expires or the threads state changes.
286 * @param _predicateLock Lockable&
287 * @param timeout maximum milliseconds to block.
291 * @exception Interrupted_Exception thrown when the caller status is interrupted
292 * @exception Synchronization_Exception thrown if there is some other error.
294 template <typename List
>
295 bool ConditionImpl
<List
>::wait(unsigned long timeout
) {
297 // Get the monitor for the current thread
298 ThreadImpl
* self
= ThreadImpl::current();
299 Monitor
& m
= self
->getMonitor();
301 Monitor::STATE state
;
305 Guard
<FastLock
> g1(_lock
);
307 // Release the _predicateLock
308 _predicateLock
.release();
310 // Stuff the waiter into the list
311 _waiters
.insert(self
);
313 state
= Monitor::TIMEDOUT
;
315 // Don't bother waiting if the timeout is 0
322 Guard
<FastLock
, UnlockedScope
> g2(g1
);
323 state
= m
.wait(timeout
);
331 // Remove from waiter list, regarless of weather release() is called or
332 // not. The monitor is sticky, so its possible a state 'stuck' from a
333 // previous operation and will leave the wait() w/o release() having
335 typename
List::iterator i
= std::find(_waiters
.begin(), _waiters
.end(), self
);
336 if(i
!= _waiters
.end())
342 // Defer interruption until the external lock is acquire()d
343 Guard
<Monitor
, DeferredInterruptionScope
> g3(m
);
349 _predicateLock
.acquire(); // Should not throw
351 } catch(...) { assert(0); }
358 case Monitor::SIGNALED
:
361 case Monitor::INTERRUPTED
:
362 throw Interrupted_Exception();
364 case Monitor::TIMEDOUT
:
368 throw Synchronization_Exception();
375 } // namespace ZThread
377 #endif // __ZTCONDITIONIMPL_H__