aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAleric Inglewood2010-11-11 00:06:08 +0100
committerAleric Inglewood2010-11-17 01:26:14 +0100
commit7bc679e616a4b8e0fafe2c1f6cac821208b649ff (patch)
tree5ef9d2203bfe4f1e7e493211deb8ac2cab811ac9
parentIMP-590: Added a thread-safe and robust wrapper for APR pools. (diff)
downloadmeta-impy-7bc679e616a4b8e0fafe2c1f6cac821208b649ff.zip
meta-impy-7bc679e616a4b8e0fafe2c1f6cac821208b649ff.tar.gz
meta-impy-7bc679e616a4b8e0fafe2c1f6cac821208b649ff.tar.bz2
meta-impy-7bc679e616a4b8e0fafe2c1f6cac821208b649ff.tar.xz
IMP-701: An API to wrap objects for thread-safe access.
See http://redmine.imprudenceviewer.org/issues/701
Diffstat (limited to '')
-rw-r--r--linden/doc/contributions.txt1
-rw-r--r--linden/indra/llcommon/CMakeLists.txt1
-rw-r--r--linden/indra/llcommon/aiaprpool.h8
-rw-r--r--linden/indra/llcommon/aithreadsafe.h463
-rw-r--r--linden/indra/llcommon/llthread.h96
5 files changed, 565 insertions, 4 deletions
diff --git a/linden/doc/contributions.txt b/linden/doc/contributions.txt
index c8dd773..0ca56be 100644
--- a/linden/doc/contributions.txt
+++ b/linden/doc/contributions.txt
@@ -92,6 +92,7 @@ Aleric Inglewood
92 IMP-670 92 IMP-670
93 IMP-688 93 IMP-688
94 IMP-692 94 IMP-692
95 IMP-701
95 IMP-712 96 IMP-712
96Alissa Sabre 97Alissa Sabre
97 VWR-81 98 VWR-81
diff --git a/linden/indra/llcommon/CMakeLists.txt b/linden/indra/llcommon/CMakeLists.txt
index a05ee6f..ee80ec3 100644
--- a/linden/indra/llcommon/CMakeLists.txt
+++ b/linden/indra/llcommon/CMakeLists.txt
@@ -76,6 +76,7 @@ set(llcommon_HEADER_FILES
76 CMakeLists.txt 76 CMakeLists.txt
77 77
78 aiaprpool.h 78 aiaprpool.h
79 aithreadsafe.h
79 bitpack.h 80 bitpack.h
80 ctype_workaround.h 81 ctype_workaround.h
81 doublelinkedlist.h 82 doublelinkedlist.h
diff --git a/linden/indra/llcommon/aiaprpool.h b/linden/indra/llcommon/aiaprpool.h
index f6d7ffa..72e9ddb 100644
--- a/linden/indra/llcommon/aiaprpool.h
+++ b/linden/indra/llcommon/aiaprpool.h
@@ -157,17 +157,19 @@ public:
157/** 157/**
158 * @brief Root memory pool (allocates memory from the operating system). 158 * @brief Root memory pool (allocates memory from the operating system).
159 * 159 *
160 * This class should only be used by AIThreadLocalData (and LLMutexRootPool 160 * This class should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase
161 * when APR_HAS_THREADS isn't defined). 161 * (and LLMutexRootPool when APR_HAS_THREADS isn't defined).
162 */ 162 */
163class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool 163class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool
164{ 164{
165private: 165private:
166 friend class AIThreadLocalData; 166 friend class AIThreadLocalData;
167 friend class AIThreadSafeSimpleDCRootPool_pbase;
167#if !APR_HAS_THREADS 168#if !APR_HAS_THREADS
168 friend class LLMutexRootPool; 169 friend class LLMutexRootPool;
169#endif 170#endif
170 //! Construct a root memory pool. Should only be used by AIThreadLocalData. 171 //! Construct a root memory pool.
172 // Should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase.
171 AIAPRRootPool(void); 173 AIAPRRootPool(void);
172 ~AIAPRRootPool(); 174 ~AIAPRRootPool();
173 175
diff --git a/linden/indra/llcommon/aithreadsafe.h b/linden/indra/llcommon/aithreadsafe.h
new file mode 100644
index 0000000..e1b93ba
--- /dev/null
+++ b/linden/indra/llcommon/aithreadsafe.h
@@ -0,0 +1,463 @@
1/**
2 * @file aithreadsafe.h
3 * @brief Implementation of AIThreadSafe, AIReadAccessConst, AIReadAccess and AIWriteAccess.
4 *
5 * CHANGELOG
6 * and additional copyright holders.
7 *
8 * 31/03/2010
9 * Initial version, written by Aleric Inglewood @ SL
10 */
11
12#ifndef AITHREADSAFE_H
13#define AITHREADSAFE_H
14
15#include <new>
16
17#include "llthread.h"
18#include "llerror.h"
19
20template<typename T> struct AIReadAccessConst;
21template<typename T> struct AIReadAccess;
22template<typename T> struct AIWriteAccess;
23template<typename T> struct AIAccess;
24
25template<typename T>
26class AIThreadSafeBits
27{
28private:
29 // AIThreadSafe is a wrapper around an instance of T.
30 // Because T might not have a default constructor, it is constructed
31 // 'in place', with placement new, in the memory reserved here.
32 //
33 // Make sure that the memory that T will be placed in is properly
34 // aligned by using an array of long's.
35 long mMemory[(sizeof(T) + sizeof(long) - 1) / sizeof(long)];
36
37public:
38 // The wrapped objects are constructed in-place with placement new *outside*
39 // of this object (by AITHREADSAFE macro(s) or derived classes).
40 // However, we are responsible for the destruction of the wrapped object.
41 ~AIThreadSafeBits() { ptr()->~T(); }
42
43 // Only for use by AITHREADSAFE, see below.
44 void* memory() const { return const_cast<long*>(&mMemory[0]); }
45
46protected:
47 // Accessors.
48 T const* ptr() const { return reinterpret_cast<T const*>(mMemory); }
49 T* ptr() { return reinterpret_cast<T*>(mMemory); }
50};
51
52/**
53 * @brief A wrapper class for objects that need to be accessed by more than one thread, allowing concurrent readers.
54 *
55 * Use AITHREADSAFE to define instances of any type, and use AIReadAccessConst,
56 * AIReadAccess and AIWriteAccess to get access to the instance.
57 *
58 * For example,
59 *
60 * <code>
61 * class Foo { public: Foo(int, int); };
62 *
63 * AITHREADSAFE(Foo, foo, (2, 3));
64 *
65 * AIReadAccess<Foo> foo_r(foo);
66 * // Use foo_r-> for read access.
67 *
68 * AIWriteAccess<Foo> foo_w(foo);
69 * // Use foo_w-> for write access.
70 * </code>
71 *
72 * If <code>foo</code> is constant, you have to use <code>AIReadAccessConst<Foo></code>.
73 *
74 * It is possible to pass access objects to a function that
75 * downgrades the access, for example:
76 *
77 * <code>
78 * void readfunc(AIReadAccess const& access);
79 *
80 * AIWriteAccess<Foo> foo_w(foo);
81 * readfunc(foo_w); // readfunc will perform read access to foo_w.
82 * </code>
83 *
84 * If <code>AIReadAccess</code> is non-const, you can upgrade the access by creating
85 * an <code>AIWriteAccess</code> object from it. For example:
86 *
87 * <code>
88 * AIWriteAccess<Foo> foo_w(foo_r);
89 * </code>
90 *
91 * This API is Robust(tm). If you try anything that could result in problems,
92 * it simply won't compile. The only mistake you can still easily make is
93 * to obtain write access to an object when it is not needed, or to unlock
94 * an object in between accesses while the state of the object should be
95 * preserved. For example:
96 *
97 * <code>
98 * // This resets foo to point to the first file and then returns that.
99 * std::string filename = AIWriteAccess<Foo>(foo)->get_first_filename();
100 *
101 * // WRONG! The state between calling get_first_filename and get_next_filename should be preserved!
102 *
103 * AIWriteAccess<Foo> foo_w(foo); // Wrong. The code below only needs read-access.
104 * while (!filename.empty())
105 * {
106 * something(filename);
107 * filename = foo_w->next_filename();
108 * }
109 * </code>
110 *
111 * Correct would be
112 *
113 * <code>
114 * AIReadAccess<Foo> foo_r(foo);
115 * std::string filename = AIWriteAccess<Foo>(foo_r)->get_first_filename();
116 * while (!filename.empty())
117 * {
118 * something(filename);
119 * filename = foo_r->next_filename();
120 * }
121 * </code>
122 *
123 */
124template<typename T>
125class AIThreadSafe : public AIThreadSafeBits<T>
126{
127protected:
128 // Only these may access the object (through ptr()).
129 friend struct AIReadAccessConst<T>;
130 friend struct AIReadAccess<T>;
131 friend struct AIWriteAccess<T>;
132
133 // Locking control.
134 AIRWLock mRWLock;
135
136 // For use by AIThreadSafeDC
137 AIThreadSafe(void) { }
138 AIThreadSafe(AIAPRPool& parent) : mRWLock(parent) { }
139
140public:
141 // Only for use by AITHREADSAFE, see below.
142 AIThreadSafe(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); }
143};
144
145/**
146 * @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafe, using an arbitrary constructor.
147 *
148 * For example, instead of doing
149 *
150 * <code>
151 * Foo foo(x, y);
152 * static Bar bar;
153 * </code>
154 *
155 * One can instantiate a thread-safe instance with
156 *
157 * <code>
158 * AITHREADSAFE(Foo, foo, (x, y));
159 * static AITHREADSAFE(Bar, bar, );
160 * </code>
161 *
162 * Note: This macro does not allow to allocate such object on the heap.
163 * If that is needed, have a look at AIThreadSafeDC.
164 */
165#define AITHREADSAFE(type, var, paramlist) AIThreadSafe<type> var(new (var.memory()) type paramlist)
166
167/**
168 * @brief A wrapper class for objects that need to be accessed by more than one thread.
169 *
170 * This class is the same as an AIThreadSafe wrapper, except that it can only
171 * be used for default constructed objects.
172 *
173 * For example, instead of
174 *
175 * <code>
176 * Foo foo;
177 * </code>
178 *
179 * One would use
180 *
181 * <code>
182 * AIThreadSafeDC<Foo> foo;
183 * </code>
184 *
185 * The advantage over AITHREADSAFE is that this object can be allocated with
186 * new on the heap. For example:
187 *
188 * <code>
189 * AIThreadSafeDC<Foo>* ptr = new AIThreadSafeDC<Foo>;
190 * </code>
191 *
192 * which is not possible with AITHREADSAFE.
193 */
194template<typename T>
195class AIThreadSafeDC : public AIThreadSafe<T>
196{
197public:
198 // Construct a wrapper around a default constructed object.
199 AIThreadSafeDC(void) { new (AIThreadSafe<T>::ptr()) T; }
200};
201
202/**
203 * @brief Read lock object and provide read access.
204 */
205template<typename T>
206struct AIReadAccessConst
207{
208 //! Internal enum for the lock-type of the AI*Access object.
209 enum state_type
210 {
211 readlocked, //!< A AIReadAccessConst or AIReadAccess.
212 read2writelocked, //!< A AIWriteAccess constructed from a AIReadAccess.
213 writelocked, //!< A AIWriteAccess constructed from a AIThreadSafe.
214 write2writelocked //!< A AIWriteAccess constructed from (the AIReadAccess base class of) a AIWriteAccess.
215 };
216
217 //! Construct a AIReadAccessConst from a constant AIThreadSafe.
218 AIReadAccessConst(AIThreadSafe<T> const& wrapper)
219 : mWrapper(const_cast<AIThreadSafe<T>&>(wrapper)),
220 mState(readlocked)
221 {
222 mWrapper.mRWLock.rdlock();
223 }
224
225 //! Destruct the AI*Access object.
226 // These should never be dynamically allocated, so there is no need to make this virtual.
227 ~AIReadAccessConst()
228 {
229 if (mState == readlocked)
230 mWrapper.mRWLock.rdunlock();
231 else if (mState == writelocked)
232 mWrapper.mRWLock.wrunlock();
233 else if (mState == read2writelocked)
234 mWrapper.mRWLock.wr2rdlock();
235 }
236
237 //! Access the underlaying object for read access.
238 T const* operator->() const { return mWrapper.ptr(); }
239
240 //! Access the underlaying object for read access.
241 T const& operator*() const { return *mWrapper.ptr(); }
242
243protected:
244 //! Constructor used by AIReadAccess.
245 AIReadAccessConst(AIThreadSafe<T>& wrapper, state_type state)
246 : mWrapper(wrapper), mState(state) { }
247
248 AIThreadSafe<T>& mWrapper; //!< Reference to the object that we provide access to.
249 state_type const mState; //!< The lock state that mWrapper is in.
250
251private:
252 // Disallow copy constructing directly.
253 AIReadAccessConst(AIReadAccessConst const&);
254};
255
256/**
257 * @brief Read lock object and provide read access, with possible promotion to write access.
258 */
259template<typename T>
260struct AIReadAccess : public AIReadAccessConst<T>
261{
262 typedef typename AIReadAccessConst<T>::state_type state_type;
263 using AIReadAccessConst<T>::readlocked;
264
265 //! Construct a AIReadAccess from a non-constant AIThreadSafe.
266 AIReadAccess(AIThreadSafe<T>& wrapper) : AIReadAccessConst<T>(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(); }
267
268protected:
269 //! Constructor used by AIWriteAccess.
270 AIReadAccess(AIThreadSafe<T>& wrapper, state_type state) : AIReadAccessConst<T>(wrapper, state) { }
271
272 friend class AIWriteAccess<T>;
273};
274
275/**
276 * @brief Write lock object and provide read/write access.
277 */
278template<typename T>
279struct AIWriteAccess : public AIReadAccess<T>
280{
281 using AIReadAccessConst<T>::readlocked;
282 using AIReadAccessConst<T>::read2writelocked;
283 using AIReadAccessConst<T>::writelocked;
284 using AIReadAccessConst<T>::write2writelocked;
285
286 //! Construct a AIWriteAccess from a non-constant AIThreadSafe.
287 AIWriteAccess(AIThreadSafe<T>& wrapper) : AIReadAccess<T>(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();}
288
289 //! Promote read access to write access.
290 explicit AIWriteAccess(AIReadAccess<T>& access)
291 : AIReadAccess<T>(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked)
292 {
293 if (this->mState == read2writelocked)
294 {
295 this->mWrapper.mRWLock.rd2wrlock();
296 }
297 }
298
299 //! Access the underlaying object for (read and) write access.
300 T* operator->() const { return this->mWrapper.ptr(); }
301
302 //! Access the underlaying object for (read and) write access.
303 T& operator*() const { return *this->mWrapper.ptr(); }
304};
305
306/**
307 * @brief A wrapper class for objects that need to be accessed by more than one thread.
308 *
309 * Use AITHREADSAFESIMPLE to define instances of any type, and use AIAccess
310 * to get access to the instance.
311 *
312 * For example,
313 *
314 * <code>
315 * class Foo { public: Foo(int, int); };
316 *
317 * AITHREADSAFESIMPLE(Foo, foo, (2, 3));
318 *
319 * AIAccess<Foo> foo_w(foo);
320 * // Use foo_w-> for read and write access.
321 *
322 * See also AIThreadSafe
323 */
324template<typename T>
325class AIThreadSafeSimple : public AIThreadSafeBits<T>
326{
327protected:
328 // Only this one may access the object (through ptr()).
329 friend struct AIAccess<T>;
330
331 // Locking control.
332 LLMutex mMutex;
333
334 // For use by AIThreadSafeSimpleDC
335 AIThreadSafeSimple(void) { }
336 AIThreadSafeSimple(AIAPRPool& parent) : mMutex(parent) { }
337
338public:
339 // Only for use by AITHREADSAFESIMPLE, see below.
340 AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); }
341};
342
343/**
344 * @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafeSimple, using an arbitrary constructor.
345 *
346 * For example, instead of doing
347 *
348 * <code>
349 * Foo foo(x, y);
350 * static Bar bar;
351 * </code>
352 *
353 * One can instantiate a thread-safe instance with
354 *
355 * <code>
356 * AITHREADSAFESIMPLE(Foo, foo, (x, y));
357 * static AITHREADSAFESIMPLE(Bar, bar, );
358 * </code>
359 *
360 * Note: This macro does not allow to allocate such object on the heap.
361 * If that is needed, have a look at AIThreadSafeSimpleDC.
362 */
363#define AITHREADSAFESIMPLE(type, var, paramlist) AIThreadSafeSimple<type> var(new (var.memory()) type paramlist)
364
365/**
366 * @brief A wrapper class for objects that need to be accessed by more than one thread.
367 *
368 * This class is the same as an AIThreadSafeSimple wrapper, except that it can only
369 * be used for default constructed objects.
370 *
371 * For example, instead of
372 *
373 * <code>
374 * Foo foo;
375 * </code>
376 *
377 * One would use
378 *
379 * <code>
380 * AIThreadSafeSimpleDC<Foo> foo;
381 * </code>
382 *
383 * The advantage over AITHREADSAFESIMPLE is that this object can be allocated with
384 * new on the heap. For example:
385 *
386 * <code>
387 * AIThreadSafeSimpleDC<Foo>* ptr = new AIThreadSafeSimpleDC<Foo>;
388 * </code>
389 *
390 * which is not possible with AITHREADSAFESIMPLE.
391 */
392template<typename T>
393class AIThreadSafeSimpleDC : public AIThreadSafeSimple<T>
394{
395public:
396 // Construct a wrapper around a default constructed object.
397 AIThreadSafeSimpleDC(void) { new (AIThreadSafeSimple<T>::ptr()) T; }
398
399protected:
400 // For use by AIThreadSafeSimpleDCRootPool
401 AIThreadSafeSimpleDC(AIAPRPool& parent) : AIThreadSafeSimple<T>(parent) { new (AIThreadSafeSimple<T>::ptr()) T; }
402};
403
404// Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of
405// the root pool before constructing AIThreadSafeSimpleDC.
406class AIThreadSafeSimpleDCRootPool_pbase
407{
408protected:
409 AIAPRRootPool mRootPool;
410
411private:
412 template<typename T> friend class AIThreadSafeSimpleDCRootPool;
413 AIThreadSafeSimpleDCRootPool_pbase(void) { }
414};
415
416/**
417 * @brief A wrapper class for objects that need to be accessed by more than one thread.
418 *
419 * The same as AIThreadSafeSimpleDC except that this class creates its own AIAPRRootPool
420 * for the internally used mutexes and condition, instead of using the current threads
421 * root pool. The advantage of this is that it can be used for objects that need to
422 * be accessed from the destructors of global objects (after main). The disadvantage
423 * is that it's less efficient to use your own root pool, therefore it's use should be
424 * restricted to those cases where it is absolutely necessary.
425 */
426template<typename T>
427class AIThreadSafeSimpleDCRootPool : private AIThreadSafeSimpleDCRootPool_pbase, public AIThreadSafeSimpleDC<T>
428{
429public:
430 // Construct a wrapper around a default constructed object, using memory allocated
431 // from the operating system for the internal APR objects (mutexes and conditional),
432 // as opposed to allocated from the current threads root pool.
433 AIThreadSafeSimpleDCRootPool(void) :
434 AIThreadSafeSimpleDCRootPool_pbase(),
435 AIThreadSafeSimpleDC<T>(mRootPool) { }
436};
437
438/**
439 * @brief Write lock object and provide read/write access.
440 */
441template<typename T>
442struct AIAccess
443{
444 //! Construct a AIAccess from a non-constant AIThreadSafeSimple.
445 AIAccess(AIThreadSafeSimple<T>& wrapper) : mWrapper(wrapper) { this->mWrapper.mMutex.lock(); }
446
447 //! Access the underlaying object for (read and) write access.
448 T* operator->() const { return this->mWrapper.ptr(); }
449
450 //! Access the underlaying object for (read and) write access.
451 T& operator*() const { return *this->mWrapper.ptr(); }
452
453 ~AIAccess() { this->mWrapper.mMutex.unlock(); }
454
455protected:
456 AIThreadSafeSimple<T>& mWrapper; //!< Reference to the object that we provide access to.
457
458private:
459 // Disallow copy constructing directly.
460 AIAccess(AIAccess const&);
461};
462
463#endif
diff --git a/linden/indra/llcommon/llthread.h b/linden/indra/llcommon/llthread.h
index 8708929..1e982cc 100644
--- a/linden/indra/llcommon/llthread.h
+++ b/linden/indra/llcommon/llthread.h
@@ -235,6 +235,101 @@ private:
235 LLMutexBase* mMutex; 235 LLMutexBase* mMutex;
236}; 236};
237 237
238class AIRWLock
239{
240public:
241 AIRWLock(AIAPRPool& parent = LLThread::tldata().mRootPool) :
242 mWriterWaitingMutex(parent), mNoHoldersCondition(parent), mHoldersCount(0), mWriterIsWaiting(false) { }
243
244private:
245 LLMutex mWriterWaitingMutex; //!< This mutex is locked while some writer is waiting for access.
246 LLCondition mNoHoldersCondition; //!< Access control for mHoldersCount. Condition true when there are no more holders.
247 int mHoldersCount; //!< Number of readers or -1 if a writer locked this object.
248 // This is volatile because we read it outside the critical area of mWriterWaitingMutex, at [1].
249 // That means that other threads can change it while we are already in the (inlined) function rdlock.
250 // Without volatile, the following assembly would fail:
251 // register x = mWriterIsWaiting;
252 // /* some thread changes mWriterIsWaiting */
253 // if (x ...
254 // However, because the function is fuzzy to begin with (we don't mind that this race
255 // condition exists) it would work fine without volatile. So, basically it's just here
256 // out of principle ;). -- Aleric
257 bool volatile mWriterIsWaiting; //!< True when there is a writer waiting for write access.
258
259public:
260 void rdlock(bool high_priority = false)
261 {
262 // Give a writer a higher priority (kinda fuzzy).
263 if (mWriterIsWaiting && !high_priority) // [1] If there is a writer interested,
264 {
265 mWriterWaitingMutex.lock(); // [2] then give it precedence and wait here.
266 // If we get here then the writer got it's access; mHoldersCount == -1.
267 mWriterWaitingMutex.unlock();
268 }
269 mNoHoldersCondition.lock(); // [3] Get exclusive access to mHoldersCount.
270 while (mHoldersCount == -1) // [4]
271 {
272 mNoHoldersCondition.wait(); // [5] Wait till mHoldersCount is (or just was) 0.
273 }
274 ++mHoldersCount; // One more reader.
275 mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
276 }
277 void rdunlock(void)
278 {
279 mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
280 if (--mHoldersCount == 0) // Was this the last reader?
281 {
282 mNoHoldersCondition.signal(); // Tell waiting threads, see [5], [6] and [7].
283 }
284 mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
285 }
286 void wrlock(void)
287 {
288 mWriterWaitingMutex.lock(); // Block new readers, see [2],
289 mWriterIsWaiting = true; // from this moment on, see [1].
290 mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
291 while (mHoldersCount != 0) // Other readers or writers have this lock?
292 {
293 mNoHoldersCondition.wait(); // [6] Wait till mHoldersCount is (or just was) 0.
294 }
295 mWriterIsWaiting = false; // Stop checking the lock for new readers, see [1].
296 mWriterWaitingMutex.unlock(); // Release blocked readers, they will still hang at [3].
297 mHoldersCount = -1; // We are a writer now (will cause a hang at [5], see [4]).
298 mNoHoldersCondition.unlock(); // Release lock on mHolders (readers go from [3] to [5]).
299 }
300 void wrunlock(void)
301 {
302 mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
303 mHoldersCount = 0; // We have no writer anymore.
304 mNoHoldersCondition.signal(); // Tell waiting threads, see [5], [6] and [7].
305 mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
306 }
307 void rd2wrlock(void)
308 {
309 mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount. Blocks new readers at [3].
310 if (--mHoldersCount > 0) // Any other reads left?
311 {
312 mWriterWaitingMutex.lock(); // Block new readers, see [2],
313 mWriterIsWaiting = true; // from this moment on, see [1].
314 while (mHoldersCount != 0) // Other readers (still) have this lock?
315 {
316 mNoHoldersCondition.wait(); // [7] Wait till mHoldersCount is (or just was) 0.
317 }
318 mWriterIsWaiting = false; // Stop checking the lock for new readers, see [1].
319 mWriterWaitingMutex.unlock(); // Release blocked readers, they will still hang at [3].
320 }
321 mHoldersCount = -1; // We are a writer now (will cause a hang at [5], see [4]).
322 mNoHoldersCondition.unlock(); // Release lock on mHolders (readers go from [3] to [5]).
323 }
324 void wr2rdlock(void)
325 {
326 mNoHoldersCondition.lock(); // Get exclusive access to mHoldersCount.
327 mHoldersCount = 1; // Turn writer into a reader.
328 mNoHoldersCondition.signal(); // Tell waiting readers, see [5].
329 mNoHoldersCondition.unlock(); // Release lock on mHoldersCount.
330 }
331};
332
238//============================================================================ 333//============================================================================
239 334
240void LLThread::lockData() 335void LLThread::lockData()
@@ -247,7 +342,6 @@ void LLThread::unlockData()
247 mRunCondition->unlock(); 342 mRunCondition->unlock();
248} 343}
249 344
250
251//============================================================================ 345//============================================================================
252 346
253// see llmemory.h for LLPointer<> definition 347// see llmemory.h for LLPointer<> definition