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.
25 #include "RecursiveMutexImpl.h"
26 #include "ThreadImpl.h"
28 #include "zthread/Guard.h"
37 * Create a new RecursiveMutexImpl
39 * @exception Initialization_Exception thrown if resources could not be
42 RecursiveMutexImpl::RecursiveMutexImpl()
43 : _owner(0), _count(0) {
48 * Destroy this RecursiveMutexImpl and release its resources
50 RecursiveMutexImpl::~RecursiveMutexImpl() {
54 // It is an error to destroy a mutex that has not been released
57 ZTDEBUG("** You are destroying a mutex which was never released. **\n");
58 assert(0); // Destroyed mutex while in use
62 if(!_waiters
.empty()) {
64 ZTDEBUG("** You are destroying a mutex which is blocking %d threads. **\n", _waiters
.size());
65 assert(0); // Destroyed mutex while in use
74 void RecursiveMutexImpl::acquire() {
76 // Get the monitor for the current thread
77 Monitor
& m
= ThreadImpl::current()->getMonitor();
80 Guard
<FastLock
> g1(_lock
);
82 // If there is an entry count and the current thread is
83 // the owner, increment the count and continue.
89 // Acquire the lock if it is free and there are no waiting threads
90 if(_owner
== 0 && _waiters
.empty()) {
97 } else { // Otherwise, wait()
99 _waiters
.push_back(&m
);
105 Guard
<FastLock
, UnlockedScope
> g2(g1
);
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
116 List::iterator i
= std::find(_waiters
.begin(), _waiters
.end(), &m
);
117 if(i
!= _waiters
.end())
120 // If awoke due to a notify(), take ownership.
122 case Monitor::SIGNALED
:
132 case Monitor::INTERRUPTED
:
133 throw Interrupted_Exception();
136 throw Synchronization_Exception();
145 bool RecursiveMutexImpl::tryAcquire(unsigned long timeout
) {
147 // Get the monitor for the current thread
148 Monitor
& m
= ThreadImpl::current()->getMonitor();
150 Guard
<FastLock
> g1(_lock
);
152 // If there is an entry count and the current thread is
153 // the owner, increment the count and continue.
159 // Acquire the lock if it is free and there are no waiting threads
160 if(_owner
== 0 && _waiters
.empty()) {
167 } else { // Otherwise, wait()
169 _waiters
.push_back(&m
);
171 Monitor::STATE state
= Monitor::TIMEDOUT
;
173 // Don't bother waiting if the timeout is 0
180 Guard
<FastLock
, UnlockedScope
> g2(g1
);
181 state
= m
.wait(timeout
);
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
193 List::iterator i
= std::find(_waiters
.begin(), _waiters
.end(), &m
);
194 if(i
!= _waiters
.end())
197 // If awoke due to a notify(), take ownership.
199 case Monitor::SIGNALED
:
209 case Monitor::INTERRUPTED
:
210 throw Interrupted_Exception();
212 case Monitor::TIMEDOUT
:
216 throw Synchronization_Exception();
227 void RecursiveMutexImpl::release() {
229 // Get the monitor for the current thread
230 Monitor
& m
= ThreadImpl::current()->getMonitor();
232 Guard
<FastLock
> g1(_lock
);
234 // Make sure the operation is valid
236 throw InvalidOp_Exception();
238 // Update the count, if it has reached 0, wake another waiter.
243 // Try to find a waiter with a backoff & retry scheme
246 // Go through the list, attempt to notify() a waiter.
247 for(List::iterator i
= _waiters
.begin(); i
!= _waiters
.end();) {
249 // Try the monitor lock, if it cant be locked skip to the next waiter
251 if(n
->tryAcquire()) {
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();
258 // Once notify() succeeds, return
269 { // Backoff and try again
271 Guard
<FastLock
, UnlockedScope
> g2(g1
);
282 } // namespace ZThread