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 #include "zthread/Exceptions.h"
24 #include "zthread/Guard.h"
28 #include "Scheduling.h"
37 * @author Eric Crahen <http://www.code-foo.com>
38 * @date <2003-07-16T19:52:12-0400>
45 inline void waiterArrived(ThreadImpl
*) { }
47 inline void waiterDeparted(ThreadImpl
*) { }
49 inline void ownerAcquired(ThreadImpl
*) { }
51 inline void ownerReleased(ThreadImpl
*) { }
56 * @author Eric Crahen <http://www.code-foo.com>
57 * @date <2003-07-16T19:52:12-0400>
61 * The MutexImpl template allows how waiter lists are sorted, and
62 * what actions are taken when a thread interacts with the mutex
65 template <typename List
, typename Behavior
>
66 class MutexImpl
: Behavior
{
68 //! List of Events that are waiting for notification
71 //! Serialize access to this Mutex
75 volatile ThreadImpl
* _owner
;
81 * Create a new MutexImpl
83 * @exception Initialization_Exception thrown if resources could not be
86 MutexImpl() : _owner(0) { }
94 bool tryAcquire(unsigned long timeout
);
99 * Destroy this MutexImpl and release its resources
101 template<typename List
, typename Behavior
>
102 MutexImpl
<List
, Behavior
>::~MutexImpl() {
106 // It is an error to destroy a mutex that has not been released
109 ZTDEBUG("** You are destroying a mutex which was never released. **\n");
110 assert(0); // Destroyed mutex while in use
114 if(!_waiters
.empty()) {
116 ZTDEBUG("** You are destroying a mutex which is blocking %d threads. **\n", _waiters
.size());
117 assert(0); // Destroyed mutex while in use
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.
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.
136 template<typename List
, typename Behavior
>
137 void MutexImpl
<List
, Behavior
>::acquire() {
139 ThreadImpl
* self
= ThreadImpl::current();
140 Monitor
& m
= self
->getMonitor();
142 Monitor::STATE state
;
144 Guard
<FastLock
> g1(_lock
);
146 // Deadlock will occur if the current thread is the owner
147 // and there is no entry count.
149 throw Deadlock_Exception();
151 // Acquire the lock if it is free and there are no waiting threads
152 if(_owner
== 0 && _waiters
.empty()) {
156 this->ownerAcquired(self
);
160 // Otherwise, wait for a signal from a thread releasing its
161 // ownership of the lock
164 _waiters
.insert(self
);
167 this->waiterArrived(self
);
171 Guard
<FastLock
, UnlockedScope
> g2(g1
);
176 this->waiterDeparted(self
);
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())
188 // If awoke due to a notify(), take ownership.
190 case Monitor::SIGNALED
:
195 this->ownerAcquired(self
);
199 case Monitor::INTERRUPTED
:
200 throw Interrupted_Exception();
203 throw Synchronization_Exception();
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.
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.
221 template<typename List
, typename Behavior
>
222 bool MutexImpl
<List
, Behavior
>::tryAcquire(unsigned long timeout
) {
224 ThreadImpl
* self
= ThreadImpl::current();
225 Monitor
& m
= self
->getMonitor();
227 Guard
<FastLock
> g1(_lock
);
229 // Deadlock will occur if the current thread is the owner
230 // and there is no entry count.
232 throw Deadlock_Exception();
234 // Acquire the lock if it is free and there are no waiting threads
235 if(_owner
== 0 && _waiters
.empty()) {
239 this->ownerAcquired(self
);
243 // Otherwise, wait for a signal from a thread releasing its
244 // ownership of the lock
247 _waiters
.insert(self
);
249 Monitor::STATE state
= Monitor::TIMEDOUT
;
251 // Don't bother waiting if the timeout is 0
256 this->waiterArrived(self
);
260 Guard
<FastLock
, UnlockedScope
> g2(g1
);
261 state
= m
.wait(timeout
);
265 this->waiterDeparted(self
);
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
276 typename
List::iterator i
= std::find(_waiters
.begin(), _waiters
.end(), self
);
277 if(i
!= _waiters
.end())
280 // If awoke due to a notify(), take ownership.
282 case Monitor::SIGNALED
:
287 this->ownerAcquired(self
);
291 case Monitor::INTERRUPTED
:
292 throw Interrupted_Exception();
294 case Monitor::TIMEDOUT
:
298 throw Synchronization_Exception();
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
313 * @exception InvalidOp_Exception - thrown if an attempt is made to
314 * release a mutex not owned by the calling thread.
316 template<typename List
, typename Behavior
>
317 void MutexImpl
<List
, Behavior
>::release() {
319 ThreadImpl
* impl
= ThreadImpl::current();
321 Guard
<FastLock
> g1(_lock
);
323 // Make sure the operation is valid
325 throw InvalidOp_Exception();
329 this->ownerReleased(impl
);
331 // Try to find a waiter with a backoff & retry scheme
334 // Go through the list, attempt to notify() a waiter.
335 for(typename
List::iterator i
= _waiters
.begin(); i
!= _waiters
.end();) {
337 // Try the monitor lock, if it cant be locked skip to the next waiter
339 Monitor
& m
= impl
->getMonitor();
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();
349 // Once notify() succeeds, return
360 { // Backoff and try again
362 Guard
<FastLock
, UnlockedScope
> g2(g1
);
371 } // namespace ZThread