root/trunk/dep/src/zthread/RecursiveMutexImpl.cxx @ 2

Revision 2, 6.9 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 "Debug.h"
24
25#include "RecursiveMutexImpl.h"
26#include "ThreadImpl.h"
27
28#include "zthread/Guard.h"
29
30#include <assert.h>
31#include <errno.h>
32#include <algorithm>
33
34namespace ZThread {
35
36  /**
37   * Create a new RecursiveMutexImpl
38   *
39   * @exception Initialization_Exception thrown if resources could not be
40   * properly allocated
41   */
42  RecursiveMutexImpl::RecursiveMutexImpl()
43    : _owner(0), _count(0) {
44 
45  }
46
47  /**
48   * Destroy this RecursiveMutexImpl and release its resources
49   */
50  RecursiveMutexImpl::~RecursiveMutexImpl() {
51
52#ifndef NDEBUG
53
54    // It is an error to destroy a mutex that has not been released
55    if(_owner != 0) {
56
57      ZTDEBUG("** You are destroying a mutex which was never released. **\n");
58      assert(0); // Destroyed mutex while in use
59
60    }
61
62    if(!_waiters.empty()) {
63
64      ZTDEBUG("** You are destroying a mutex which is blocking %d threads. **\n", _waiters.size());
65      assert(0); // Destroyed mutex while in use
66
67    }
68
69#endif
70
71  }
72
73
74  void RecursiveMutexImpl::acquire() {
75
76    // Get the monitor for the current thread
77    Monitor& m = ThreadImpl::current()->getMonitor();
78    Monitor::STATE state;
79
80    Guard<FastLock> g1(_lock);
81     
82    // If there is an entry count and the current thread is
83    // the owner, increment the count and continue.
84    if(_owner == &m)
85      _count++;
86
87    else {
88
89      // Acquire the lock if it is free and there are no waiting threads
90      if(_owner == 0 && _waiters.empty()) {
91
92        assert(_count == 0);
93
94        _owner = &m;   
95        _count++;
96
97      } else { // Otherwise, wait()
98       
99        _waiters.push_back(&m);
100
101        m.acquire();
102
103        {
104
105          Guard<FastLock, UnlockedScope> g2(g1);
106          state = m.wait();
107
108        }
109
110        m.release();
111       
112        // Remove from waiter list, regarless of weather release() is called or
113        // not. The monitor is sticky, so its possible a state 'stuck' from a
114        // previous operation and will leave the wait() w/o release() having
115        // been called.
116        List::iterator i = std::find(_waiters.begin(), _waiters.end(), &m);
117        if(i != _waiters.end())
118          _waiters.erase(i);
119
120        // If awoke due to a notify(), take ownership.
121        switch(state) {
122          case Monitor::SIGNALED:
123         
124            assert(_owner == 0);
125            assert(_count == 0);
126
127            _owner = &m;
128            _count++;
129           
130            break;
131
132          case Monitor::INTERRUPTED:
133            throw Interrupted_Exception();
134           
135          default:
136            throw Synchronization_Exception();
137        }
138           
139      }
140     
141    }
142
143  }
144
145  bool RecursiveMutexImpl::tryAcquire(unsigned long timeout) {
146 
147    // Get the monitor for the current thread
148    Monitor& m = ThreadImpl::current()->getMonitor();
149
150    Guard<FastLock> g1(_lock);
151 
152    // If there is an entry count and the current thread is
153    // the owner, increment the count and continue.
154    if(_owner == &m)
155      _count++;
156
157    else {
158
159      // Acquire the lock if it is free and there are no waiting threads
160      if(_owner == 0 && _waiters.empty()) {
161
162        assert(_count == 0);
163
164        _owner = &m;
165        _count++;
166
167      } else { // Otherwise, wait()
168
169        _waiters.push_back(&m);
170
171        Monitor::STATE state = Monitor::TIMEDOUT;
172
173        // Don't bother waiting if the timeout is 0
174        if(timeout) {
175
176          m.acquire();
177
178          {
179         
180            Guard<FastLock, UnlockedScope> g2(g1);
181            state = m.wait(timeout);
182         
183          }
184
185          m.release();
186       
187        }
188
189        // Remove from waiter list, regarless of weather release() is called or
190        // not. The monitor is sticky, so its possible a state 'stuck' from a
191        // previous operation and will leave the wait() w/o release() having
192        // been called.
193        List::iterator i = std::find(_waiters.begin(), _waiters.end(), &m);
194        if(i != _waiters.end())
195          _waiters.erase(i);
196
197        // If awoke due to a notify(), take ownership.
198        switch(state) {
199          case Monitor::SIGNALED:
200
201            assert(_count == 0);
202            assert(_owner == 0);
203
204            _owner = &m;
205            _count++;
206           
207            break;
208
209          case Monitor::INTERRUPTED:
210            throw Interrupted_Exception();
211         
212          case Monitor::TIMEDOUT:
213            return false;
214
215          default:
216            throw Synchronization_Exception();
217        }
218           
219      }
220     
221    }
222
223    return true;
224
225  }
226
227  void RecursiveMutexImpl::release() {
228
229    // Get the monitor for the current thread
230    Monitor& m = ThreadImpl::current()->getMonitor();
231
232    Guard<FastLock> g1(_lock);
233
234    // Make sure the operation is valid
235    if(!(_owner == &m))
236      throw InvalidOp_Exception();
237
238    // Update the count, if it has reached 0, wake another waiter.
239    if(--_count == 0) {
240   
241      _owner = 0;
242
243      // Try to find a waiter with a backoff & retry scheme
244      for(;;) {
245
246        // Go through the list, attempt to notify() a waiter.
247        for(List::iterator i = _waiters.begin(); i != _waiters.end();) {
248       
249          // Try the monitor lock, if it cant be locked skip to the next waiter
250          Monitor* n = *i;
251          if(n->tryAcquire()) {
252           
253            // If notify() is not sucessful, it is because the wait() has already
254            // been ended (killed/interrupted/notify'd)
255            bool woke = n->notify();
256            n->release();
257         
258            // Once notify() succeeds, return
259            if(woke)
260              return;
261         
262          } else ++i;
263       
264        }
265     
266        if(_waiters.empty())
267          return;
268
269        { // Backoff and try again
270
271          Guard<FastLock, UnlockedScope> g2(g1);
272          ThreadImpl::yield();
273
274        }
275
276      }
277
278    }
279 
280  }
281
282} // namespace ZThread
283
284
285
286
Note: See TracBrowser for help on using the browser.