[7297] Fixed profession spells sorting in trainer spell list at client.
[getmangos.git] / dep / src / zthread / MutexImpl.h
blob10a9160ce5a69ac5fdf64a8377ca8faf27039a36
1 /*
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"
26 #include "Debug.h"
27 #include "FastLock.h"
28 #include "Scheduling.h"
30 #include <assert.h>
31 #include <errno.h>
33 namespace ZThread {
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
42 class NullBehavior {
43 protected:
45 inline void waiterArrived(ThreadImpl*) { }
47 inline void waiterDeparted(ThreadImpl*) { }
49 inline void ownerAcquired(ThreadImpl*) { }
51 inline void ownerReleased(ThreadImpl*) { }
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
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.
65 template <typename List, typename Behavior>
66 class MutexImpl : Behavior {
68 //! List of Events that are waiting for notification
69 List _waiters;
71 //! Serialize access to this Mutex
72 FastLock _lock;
74 //! Current owner
75 volatile ThreadImpl* _owner;
77 public:
80 /**
81 * Create a new MutexImpl
83 * @exception Initialization_Exception thrown if resources could not be
84 * properly allocated
86 MutexImpl() : _owner(0) { }
88 ~MutexImpl();
90 void acquire();
92 void release();
94 bool tryAcquire(unsigned long timeout);
98 /**
99 * Destroy this MutexImpl and release its resources
101 template<typename List, typename Behavior>
102 MutexImpl<List, Behavior>::~MutexImpl() {
104 #ifndef NDEBUG
106 // It is an error to destroy a mutex that has not been released
107 if(_owner != 0) {
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
121 #endif
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.
148 if(_owner == self)
149 throw Deadlock_Exception();
151 // Acquire the lock if it is free and there are no waiting threads
152 if(_owner == 0 && _waiters.empty()) {
154 _owner = self;
156 this->ownerAcquired(self);
160 // Otherwise, wait for a signal from a thread releasing its
161 // ownership of the lock
162 else {
164 _waiters.insert(self);
165 m.acquire();
167 this->waiterArrived(self);
171 Guard<FastLock, UnlockedScope> g2(g1);
172 state = m.wait();
176 this->waiterDeparted(self);
178 m.release();
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);
188 // If awoke due to a notify(), take ownership.
189 switch(state) {
190 case Monitor::SIGNALED:
192 assert(_owner == 0);
193 _owner = self;
195 this->ownerAcquired(self);
197 break;
199 case Monitor::INTERRUPTED:
200 throw Interrupted_Exception();
202 default:
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.
231 if(_owner == self)
232 throw Deadlock_Exception();
234 // Acquire the lock if it is free and there are no waiting threads
235 if(_owner == 0 && _waiters.empty()) {
237 _owner = self;
239 this->ownerAcquired(self);
243 // Otherwise, wait for a signal from a thread releasing its
244 // ownership of the lock
245 else {
247 _waiters.insert(self);
249 Monitor::STATE state = Monitor::TIMEDOUT;
251 // Don't bother waiting if the timeout is 0
252 if(timeout) {
254 m.acquire();
256 this->waiterArrived(self);
260 Guard<FastLock, UnlockedScope> g2(g1);
261 state = m.wait(timeout);
265 this->waiterDeparted(self);
267 m.release();
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);
280 // If awoke due to a notify(), take ownership.
281 switch(state) {
282 case Monitor::SIGNALED:
284 assert(0 == _owner);
285 _owner = self;
287 this->ownerAcquired(self);
289 break;
291 case Monitor::INTERRUPTED:
292 throw Interrupted_Exception();
294 case Monitor::TIMEDOUT:
295 return false;
297 default:
298 throw Synchronization_Exception();
303 return true;
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.
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
324 if(_owner != impl)
325 throw InvalidOp_Exception();
327 _owner = 0;
329 this->ownerReleased(impl);
331 // Try to find a waiter with a backoff & retry scheme
332 for(;;) {
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
338 impl = *i;
339 Monitor& m = impl->getMonitor();
341 if(m.tryAcquire()) {
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();
347 m.release();
349 // Once notify() succeeds, return
350 if(woke)
351 return;
353 } else ++i;
357 if(_waiters.empty())
358 return;
360 { // Backoff and try again
362 Guard<FastLock, UnlockedScope> g2(g1);
363 ThreadImpl::yield();
371 } // namespace ZThread