root/trunk/dep/src/zthread/ConditionImpl.h @ 2

Revision 2, 8.8 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/*
2 * Copyright (c) 2005, Eric Crahen
3 *
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:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
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.
20 *
21 */
22 
23#ifndef __ZTCONDITIONIMPL_H__
24#define __ZTCONDITIONIMPL_H__
25
26#include "zthread/Guard.h"
27
28#include "Debug.h"
29#include "Scheduling.h"
30#include "DeferredInterruptionScope.h"
31
32namespace ZThread {
33
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
39 *
40 * The ConditionImpl template allows how waiter lists are sorted
41 * to be parameteized
42 */ 
43template <typename List> 
44class ConditionImpl {
45 
46  //! Waiters currently blocked
47  List _waiters;
48
49  //! Serialize access to this object
50  FastLock _lock;
51 
52  //! External lock
53  Lockable& _predicateLock;
54
55 public:
56
57  /**
58   * Create a new ConditionImpl.
59   *
60   * @exception Initialization_Exception thrown if resources could not be
61   * allocated
62   */
63  ConditionImpl(Lockable& predicateLock) : _predicateLock(predicateLock) {
64
65  }
66
67  /**
68   * Destroy this ConditionImpl, release its resources
69   */
70  ~ConditionImpl();
71
72  void signal();
73
74  void broadcast();
75
76  void wait();
77
78  bool wait(unsigned long timeout);
79
80};
81
82
83template <typename List> 
84ConditionImpl<List>::~ConditionImpl() {
85
86#ifndef NDEBUG
87
88    // It is an error to destroy a condition with threads waiting on it.
89    if(!_waiters.empty()) {
90
91      ZTDEBUG("** You are destroying a condition variable which still has waiting threads. **\n");
92      assert(0); 
93
94    }
95
96#endif
97
98  }
99
100
101/**
102 * Signal the condition variable, waking one thread if any.
103 */
104template <typename List> 
105void ConditionImpl<List>::signal() {
106
107    Guard<FastLock> g1(_lock);
108
109    // Try to find a waiter with a backoff & retry scheme
110    for(;;) {
111   
112      // Go through the list, attempt to notify() a waiter.
113      for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) {
114
115        // Try the monitor lock, if it cant be locked skip to the next waiter
116        ThreadImpl* impl = *i;
117        Monitor& m = impl->getMonitor();
118
119        if(m.tryAcquire()) {
120       
121          // Notify the monitor & remove from the waiter list so time isn't
122          // wasted checking it again.
123          i = _waiters.erase(i);
124       
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();
128       
129          m.release();
130       
131          // Once notify() succeeds, return
132          if(woke) 
133            return;
134       
135        } else ++i;
136     
137      }
138   
139      if(_waiters.empty())
140        return;
141   
142      { // Backoff and try again
143
144        Guard<FastLock, UnlockedScope> g2(g1);
145        ThreadImpl::yield();
146
147      }
148
149    }
150
151  }
152
153/**
154 * Broadcast to the condition variable, waking all threads waiting at the time of
155 * the broadcast.
156 */
157template <typename List> 
158void ConditionImpl<List>::broadcast() {
159
160    Guard<FastLock> g1(_lock);
161
162    // Try to find a waiter with a backoff & retry scheme
163    for(;;) {
164   
165      // Go through the list, attempt to notify() a waiter.
166      for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) {
167
168        // Try the monitor lock, if it cant be locked skip to the next waiter
169        ThreadImpl* impl = *i;
170        Monitor& m = impl->getMonitor();
171
172        if(m.tryAcquire()) {
173       
174          // Notify the monitor & remove from the waiter list so time isn't
175          // wasted checking it again.
176          i = _waiters.erase(i);
177       
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();
181       
182          m.release();
183       
184        } else ++i;
185     
186      }
187   
188      if(_waiters.empty())
189        return;
190
191      { // Backoff and try again
192
193        Guard<FastLock, UnlockedScope> g2(g1);
194        ThreadImpl::yield();
195
196      }
197
198    }
199
200  }
201
202/**
203 * Cause the currently executing thread to block until this ConditionImpl has
204 * been signaled, the threads state changes.
205 *
206 * @param predicate Lockable&
207 *
208 * @exception Interrupted_Exception thrown when the caller status is interrupted
209 * @exception Synchronization_Exception thrown if there is some other error.
210 */
211template <typename List> 
212void ConditionImpl<List>::wait() {
213
214    // Get the monitor for the current thread
215    ThreadImpl* self = ThreadImpl::current();
216    Monitor& m = self->getMonitor();
217
218    Monitor::STATE state;
219
220    {
221
222      Guard<FastLock> g1(_lock);
223   
224      // Release the _predicateLock
225      _predicateLock.release();
226   
227      // Stuff the waiter into the list
228      _waiters.insert(self);
229   
230      // Move to the monitor's lock
231      m.acquire();
232
233      {
234
235        Guard<FastLock, UnlockedScope> g2(g1);
236        state = m.wait();
237   
238      }
239
240      // Move back to the Condition's lock
241      m.release();
242   
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);
250   
251    }
252
253    // Defer interruption until the external lock is acquire()d
254    Guard<Monitor, DeferredInterruptionScope> g3(m);
255    {
256
257#if !defined(NDEBUG)
258      try {
259#endif
260        _predicateLock.acquire(); // Should not throw
261#if !defined(NDEBUG)
262      } catch(...) { assert(0); }
263#endif
264
265    }
266
267    switch(state) {
268   
269      case Monitor::SIGNALED:
270        break;
271     
272      case Monitor::INTERRUPTED:
273        throw Interrupted_Exception();
274     
275      default:
276        throw Synchronization_Exception();
277    }   
278
279  }
280
281
282/**
283 * Cause the currently executing thread to block until this ConditionImpl has
284 * been signaled, or the timeout expires or the threads state changes.
285 *
286 * @param _predicateLock Lockable&
287 * @param timeout maximum milliseconds to block.
288 *
289 * @return bool
290 *
291 * @exception Interrupted_Exception thrown when the caller status is interrupted
292 * @exception Synchronization_Exception thrown if there is some other error.
293 */
294template <typename List> 
295bool ConditionImpl<List>::wait(unsigned long timeout) {
296 
297    // Get the monitor for the current thread
298    ThreadImpl* self = ThreadImpl::current();
299    Monitor& m = self->getMonitor();
300
301    Monitor::STATE state;
302
303    {
304
305      Guard<FastLock> g1(_lock);
306   
307      // Release the _predicateLock
308      _predicateLock.release();
309   
310      // Stuff the waiter into the list
311      _waiters.insert(self);
312   
313      state = Monitor::TIMEDOUT;
314   
315      // Don't bother waiting if the timeout is 0
316      if(timeout) {
317   
318        m.acquire();
319
320        {
321
322          Guard<FastLock, UnlockedScope> g2(g1);
323          state = m.wait(timeout);
324
325        }
326
327        m.release();
328     
329      }
330   
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);
338   
339    }
340
341
342    // Defer interruption until the external lock is acquire()d
343    Guard<Monitor, DeferredInterruptionScope> g3(m);
344    {
345
346#if !defined(NDEBUG)
347      try {
348#endif
349        _predicateLock.acquire(); // Should not throw
350#if !defined(NDEBUG)
351      } catch(...) { assert(0); }
352#endif
353
354    }
355
356    switch(state) {
357   
358      case Monitor::SIGNALED:
359        break;
360     
361      case Monitor::INTERRUPTED:
362        throw Interrupted_Exception();
363     
364      case Monitor::TIMEDOUT:
365        return false;
366
367      default:
368        throw Synchronization_Exception();
369    }   
370
371    return true;
372
373  }
374
375} // namespace ZThread
376
377#endif // __ZTCONDITIONIMPL_H__
Note: See TracBrowser for help on using the browser.