root/trunk/dep/src/zthread/MutexImpl.h @ 93

Revision 2, 9.4 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#include "zthread/Exceptions.h"
24#include "zthread/Guard.h"
25
26#include "Debug.h"
27#include "FastLock.h"
28#include "Scheduling.h"
29
30#include <assert.h>
31#include <errno.h>
32
33namespace ZThread {
34
35
36/**
37 * @author Eric Crahen <http://www.code-foo.com>
38 * @date <2003-07-16T19:52:12-0400>
39 * @version 2.2.11
40 * @class NullBehavior
41 */
42class NullBehavior {
43protected:
44
45  inline void waiterArrived(ThreadImpl*) {  }
46
47  inline void waiterDeparted(ThreadImpl*) {  }
48
49  inline void ownerAcquired(ThreadImpl*) {  }
50
51  inline void ownerReleased(ThreadImpl*) {  }
52
53};
54
55/**
56 * @author Eric Crahen <http://www.code-foo.com>
57 * @date <2003-07-16T19:52:12-0400>
58 * @version 2.2.11
59 * @class MutexImpl
60 *
61 * The MutexImpl template allows how waiter lists are sorted, and
62 * what actions are taken when a thread interacts with the mutex
63 * to be parametized.
64 */
65template <typename List, typename Behavior> 
66class MutexImpl : Behavior {
67
68  //! List of Events that are waiting for notification
69  List _waiters;
70
71  //! Serialize access to this Mutex
72  FastLock _lock;
73
74  //! Current owner
75  volatile ThreadImpl* _owner;
76
77 public:
78 
79
80  /**
81   * Create a new MutexImpl
82   *
83   * @exception Initialization_Exception thrown if resources could not be
84   * properly allocated
85   */
86  MutexImpl() : _owner(0) { }
87 
88  ~MutexImpl();
89
90  void acquire();
91 
92  void release();
93
94  bool tryAcquire(unsigned long timeout);
95
96};
97
98  /**
99   * Destroy this MutexImpl and release its resources
100   */
101template<typename List, typename Behavior> 
102MutexImpl<List, Behavior>::~MutexImpl() {
103
104#ifndef NDEBUG
105
106    // It is an error to destroy a mutex that has not been released
107    if(_owner != 0) { 
108
109      ZTDEBUG("** You are destroying a mutex which was never released. **\n");
110      assert(0); // Destroyed mutex while in use
111
112    }
113
114    if(!_waiters.empty()) { 
115
116      ZTDEBUG("** You are destroying a mutex which is blocking %d threads. **\n", _waiters.size());
117      assert(0); // Destroyed mutex while in use
118
119    }
120
121#endif
122
123  }
124
125
126  /**
127   * Acquire a lock on the mutex. If this operation succeeds the calling
128   * thread holds an exclusive lock on this mutex, otherwise it is blocked
129   * until the lock can be acquired.
130   *
131   * @exception Deadlock_Exception thrown when the caller attempts to acquire() more
132   * than once, If the checking flag is set.
133   * @exception Interrupted_Exception thrown when the caller status is interrupted
134   * @exception Synchronization_Exception thrown if there is some other error.
135   */
136template<typename List, typename Behavior> 
137void MutexImpl<List, Behavior>::acquire() {
138
139    ThreadImpl* self = ThreadImpl::current();
140    Monitor& m = self->getMonitor();
141
142    Monitor::STATE state;
143
144    Guard<FastLock> g1(_lock);
145   
146    // Deadlock will occur if the current thread is the owner
147    // and there is no entry count.
148    if(_owner == self) 
149      throw Deadlock_Exception();
150   
151    // Acquire the lock if it is free and there are no waiting threads
152    if(_owner == 0 && _waiters.empty()) {
153
154      _owner = self;
155
156      this->ownerAcquired(self);
157     
158    }
159
160    // Otherwise, wait for a signal from a thread releasing its
161    // ownership of the lock
162    else { 
163       
164      _waiters.insert(self);
165      m.acquire();
166
167      this->waiterArrived(self);
168
169      {       
170     
171        Guard<FastLock, UnlockedScope> g2(g1);
172        state = m.wait();
173     
174      }
175
176      this->waiterDeparted(self);
177
178      m.release();
179       
180      // Remove from waiter list, regardless of wether release() is called or
181      // not. The monitor is sticky, so its possible a state 'stuck' from a
182      // previous operation and will leave the wait() w/o release() having
183      // been called (e.g. interrupted)
184      typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
185      if(i != _waiters.end())
186        _waiters.erase(i);
187
188      // If awoke due to a notify(), take ownership.
189      switch(state) {
190        case Monitor::SIGNALED:
191
192          assert(_owner == 0);
193          _owner = self;   
194
195          this->ownerAcquired(self);
196
197          break;
198       
199        case Monitor::INTERRUPTED:
200          throw Interrupted_Exception();
201       
202        default:
203          throw Synchronization_Exception();
204      } 
205   
206    }
207 
208  }
209
210
211  /**
212   * Acquire a lock on the mutex. If this operation succeeds the calling
213   * thread holds an exclusive lock on this mutex. If the lock cannot be
214   * obtained before the timeout expires, the caller returns false.
215   *
216   * @exception Deadlock_Exception thrown when the caller attempts to acquire() more
217   * than once, If the checking flag is set.
218   * @exception Interrupted_Exception thrown when the caller status is interrupted
219   * @exception Synchronization_Exception thrown if there is some other error.
220   */
221template<typename List, typename Behavior> 
222bool MutexImpl<List, Behavior>::tryAcquire(unsigned long timeout) {
223 
224    ThreadImpl* self = ThreadImpl::current();
225    Monitor& m = self->getMonitor();
226
227    Guard<FastLock> g1(_lock);
228   
229    // Deadlock will occur if the current thread is the owner
230    // and there is no entry count.
231    if(_owner == self) 
232      throw Deadlock_Exception();
233
234    // Acquire the lock if it is free and there are no waiting threads
235    if(_owner == 0 && _waiters.empty()) {
236
237      _owner = self;
238
239      this->ownerAcquired(self);
240     
241    }
242
243    // Otherwise, wait for a signal from a thread releasing its
244    // ownership of the lock
245    else {
246       
247      _waiters.insert(self);
248   
249      Monitor::STATE state = Monitor::TIMEDOUT;
250   
251      // Don't bother waiting if the timeout is 0
252      if(timeout) {
253     
254        m.acquire();
255
256        this->waiterArrived(self);
257     
258        {
259       
260          Guard<FastLock, UnlockedScope> g2(g1);
261          state = m.wait(timeout);
262       
263        }
264
265        this->waiterDeparted(self);
266     
267        m.release();
268       
269      }
270   
271   
272      // Remove from waiter list, regarless of weather release() is called or
273      // not. The monitor is sticky, so its possible a state 'stuck' from a
274      // previous operation and will leave the wait() w/o release() having
275      // been called.
276      typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
277      if(i != _waiters.end())
278        _waiters.erase(i);
279   
280      // If awoke due to a notify(), take ownership.
281      switch(state) {
282        case Monitor::SIGNALED:
283       
284          assert(0 == _owner);
285          _owner = self;
286
287          this->ownerAcquired(self);
288       
289          break;
290       
291        case Monitor::INTERRUPTED:
292          throw Interrupted_Exception();
293       
294        case Monitor::TIMEDOUT:
295          return false;
296       
297        default:
298          throw Synchronization_Exception();
299      } 
300   
301    }
302 
303    return true;
304 
305  }
306
307  /**
308   * Release a lock on the mutex. If this operation succeeds the calling
309   * thread no longer holds an exclusive lock on this mutex. If there are
310   * waiting threads, one will be selected, assigned ownership and specifically
311   * awakened.
312   *
313   * @exception InvalidOp_Exception - thrown if an attempt is made to
314   * release a mutex not owned by the calling thread.
315   */
316template<typename List, typename Behavior> 
317void MutexImpl<List, Behavior>::release() {
318
319    ThreadImpl* impl = ThreadImpl::current();
320
321    Guard<FastLock> g1(_lock);
322 
323    // Make sure the operation is valid
324    if(_owner != impl)
325      throw InvalidOp_Exception();
326
327    _owner = 0;
328
329    this->ownerReleased(impl);
330 
331    // Try to find a waiter with a backoff & retry scheme
332    for(;;) {
333   
334      // Go through the list, attempt to notify() a waiter.
335      for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) {
336     
337        // Try the monitor lock, if it cant be locked skip to the next waiter
338        impl = *i;
339        Monitor& m = impl->getMonitor();
340
341        if(m.tryAcquire()) {
342 
343          // If notify() is not sucessful, it is because the wait() has already
344          // been ended (killed/interrupted/notify'd)
345          bool woke = m.notify();
346         
347          m.release();
348       
349          // Once notify() succeeds, return
350          if(woke)
351            return;
352       
353        } else ++i;
354     
355      }
356   
357      if(_waiters.empty())
358        return;
359
360      { // Backoff and try again
361     
362        Guard<FastLock, UnlockedScope> g2(g1);
363        ThreadImpl::yield();
364
365      }
366
367    }
368 
369  }
370
371} // namespace ZThread
372
373
374
375
376
377
Note: See TracBrowser for help on using the browser.