Initial Patch of Auction House bot rev. 135
[auctionmangos.git] / dep / src / zthread / ConditionImpl.h
blobeeeaba10bbc3cb83d7852c1f48c196620258394c
1 /*
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"
28 #include "Debug.h"
29 #include "Scheduling.h"
30 #include "DeferredInterruptionScope.h"
32 namespace ZThread {
34 /**
35 * @class ConditionImpl
36 * @author Eric Crahen <http://www.code-foo.com>
37 * @date <2003-07-18T08:15:37-0400>
38 * @version 2.2.11
40 * The ConditionImpl template allows how waiter lists are sorted
41 * to be parameteized
42 */
43 template <typename List>
44 class ConditionImpl {
46 //! Waiters currently blocked
47 List _waiters;
49 //! Serialize access to this object
50 FastLock _lock;
52 //! External lock
53 Lockable& _predicateLock;
55 public:
57 /**
58 * Create a new ConditionImpl.
60 * @exception Initialization_Exception thrown if resources could not be
61 * allocated
63 ConditionImpl(Lockable& predicateLock) : _predicateLock(predicateLock) {
67 /**
68 * Destroy this ConditionImpl, release its resources
70 ~ConditionImpl();
72 void signal();
74 void broadcast();
76 void wait();
78 bool wait(unsigned long timeout);
83 template <typename List>
84 ConditionImpl<List>::~ConditionImpl() {
86 #ifndef NDEBUG
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");
92 assert(0);
96 #endif
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
110 for(;;) {
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();
119 if(m.tryAcquire()) {
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();
129 m.release();
131 // Once notify() succeeds, return
132 if(woke)
133 return;
135 } else ++i;
139 if(_waiters.empty())
140 return;
142 { // Backoff and try again
144 Guard<FastLock, UnlockedScope> g2(g1);
145 ThreadImpl::yield();
154 * Broadcast to the condition variable, waking all threads waiting at the time of
155 * the broadcast.
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
163 for(;;) {
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();
172 if(m.tryAcquire()) {
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).
180 m.notify();
182 m.release();
184 } else ++i;
188 if(_waiters.empty())
189 return;
191 { // Backoff and try again
193 Guard<FastLock, UnlockedScope> g2(g1);
194 ThreadImpl::yield();
202 /**
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
231 m.acquire();
235 Guard<FastLock, UnlockedScope> g2(g1);
236 state = m.wait();
240 // Move back to the Condition's lock
241 m.release();
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
246 // been called.
247 typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
248 if(i != _waiters.end())
249 _waiters.erase(i);
253 // Defer interruption until the external lock is acquire()d
254 Guard<Monitor, DeferredInterruptionScope> g3(m);
257 #if !defined(NDEBUG)
258 try {
259 #endif
260 _predicateLock.acquire(); // Should not throw
261 #if !defined(NDEBUG)
262 } catch(...) { assert(0); }
263 #endif
267 switch(state) {
269 case Monitor::SIGNALED:
270 break;
272 case Monitor::INTERRUPTED:
273 throw Interrupted_Exception();
275 default:
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.
289 * @return bool
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
316 if(timeout) {
318 m.acquire();
322 Guard<FastLock, UnlockedScope> g2(g1);
323 state = m.wait(timeout);
327 m.release();
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
334 // been called.
335 typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
336 if(i != _waiters.end())
337 _waiters.erase(i);
342 // Defer interruption until the external lock is acquire()d
343 Guard<Monitor, DeferredInterruptionScope> g3(m);
346 #if !defined(NDEBUG)
347 try {
348 #endif
349 _predicateLock.acquire(); // Should not throw
350 #if !defined(NDEBUG)
351 } catch(...) { assert(0); }
352 #endif
356 switch(state) {
358 case Monitor::SIGNALED:
359 break;
361 case Monitor::INTERRUPTED:
362 throw Interrupted_Exception();
364 case Monitor::TIMEDOUT:
365 return false;
367 default:
368 throw Synchronization_Exception();
371 return true;
375 } // namespace ZThread
377 #endif // __ZTCONDITIONIMPL_H__