[7297] Fixed profession spells sorting in trainer spell list at client.
[getmangos.git] / dep / src / zthread / RecursiveMutexImpl.cxx
blob41ca03547f88120a495d71d32bc12c485a32fce9
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 "Debug.h"
25 #include "RecursiveMutexImpl.h"
26 #include "ThreadImpl.h"
28 #include "zthread/Guard.h"
30 #include <assert.h>
31 #include <errno.h>
32 #include <algorithm>
34 namespace ZThread {
36 /**
37 * Create a new RecursiveMutexImpl
39 * @exception Initialization_Exception thrown if resources could not be
40 * properly allocated
42 RecursiveMutexImpl::RecursiveMutexImpl()
43 : _owner(0), _count(0) {
47 /**
48 * Destroy this RecursiveMutexImpl and release its resources
50 RecursiveMutexImpl::~RecursiveMutexImpl() {
52 #ifndef NDEBUG
54 // It is an error to destroy a mutex that has not been released
55 if(_owner != 0) {
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
69 #endif
74 void RecursiveMutexImpl::acquire() {
76 // Get the monitor for the current thread
77 Monitor& m = ThreadImpl::current()->getMonitor();
78 Monitor::STATE state;
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.
84 if(_owner == &m)
85 _count++;
87 else {
89 // Acquire the lock if it is free and there are no waiting threads
90 if(_owner == 0 && _waiters.empty()) {
92 assert(_count == 0);
94 _owner = &m;
95 _count++;
97 } else { // Otherwise, wait()
99 _waiters.push_back(&m);
101 m.acquire();
105 Guard<FastLock, UnlockedScope> g2(g1);
106 state = m.wait();
110 m.release();
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);
120 // If awoke due to a notify(), take ownership.
121 switch(state) {
122 case Monitor::SIGNALED:
124 assert(_owner == 0);
125 assert(_count == 0);
127 _owner = &m;
128 _count++;
130 break;
132 case Monitor::INTERRUPTED:
133 throw Interrupted_Exception();
135 default:
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.
154 if(_owner == &m)
155 _count++;
157 else {
159 // Acquire the lock if it is free and there are no waiting threads
160 if(_owner == 0 && _waiters.empty()) {
162 assert(_count == 0);
164 _owner = &m;
165 _count++;
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
174 if(timeout) {
176 m.acquire();
180 Guard<FastLock, UnlockedScope> g2(g1);
181 state = m.wait(timeout);
185 m.release();
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);
197 // If awoke due to a notify(), take ownership.
198 switch(state) {
199 case Monitor::SIGNALED:
201 assert(_count == 0);
202 assert(_owner == 0);
204 _owner = &m;
205 _count++;
207 break;
209 case Monitor::INTERRUPTED:
210 throw Interrupted_Exception();
212 case Monitor::TIMEDOUT:
213 return false;
215 default:
216 throw Synchronization_Exception();
223 return true;
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
235 if(!(_owner == &m))
236 throw InvalidOp_Exception();
238 // Update the count, if it has reached 0, wake another waiter.
239 if(--_count == 0) {
241 _owner = 0;
243 // Try to find a waiter with a backoff & retry scheme
244 for(;;) {
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
250 Monitor* n = *i;
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();
256 n->release();
258 // Once notify() succeeds, return
259 if(woke)
260 return;
262 } else ++i;
266 if(_waiters.empty())
267 return;
269 { // Backoff and try again
271 Guard<FastLock, UnlockedScope> g2(g1);
272 ThreadImpl::yield();
282 } // namespace ZThread