diff options
Diffstat (limited to '')
-rw-r--r-- | linden/doc/contributions.txt | 1 | ||||
-rw-r--r-- | linden/indra/llcommon/CMakeLists.txt | 1 | ||||
-rw-r--r-- | linden/indra/llcommon/aiaprpool.h | 8 | ||||
-rw-r--r-- | linden/indra/llcommon/aithreadsafe.h | 463 | ||||
-rw-r--r-- | linden/indra/llcommon/llthread.h | 96 |
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 |
96 | Alissa Sabre | 97 | Alissa 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 | */ |
163 | class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool | 163 | class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool |
164 | { | 164 | { |
165 | private: | 165 | private: |
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 | |||
20 | template<typename T> struct AIReadAccessConst; | ||
21 | template<typename T> struct AIReadAccess; | ||
22 | template<typename T> struct AIWriteAccess; | ||
23 | template<typename T> struct AIAccess; | ||
24 | |||
25 | template<typename T> | ||
26 | class AIThreadSafeBits | ||
27 | { | ||
28 | private: | ||
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 | |||
37 | public: | ||
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 | |||
46 | protected: | ||
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 | */ | ||
124 | template<typename T> | ||
125 | class AIThreadSafe : public AIThreadSafeBits<T> | ||
126 | { | ||
127 | protected: | ||
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 | |||
140 | public: | ||
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 | */ | ||
194 | template<typename T> | ||
195 | class AIThreadSafeDC : public AIThreadSafe<T> | ||
196 | { | ||
197 | public: | ||
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 | */ | ||
205 | template<typename T> | ||
206 | struct 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 | |||
243 | protected: | ||
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 | |||
251 | private: | ||
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 | */ | ||
259 | template<typename T> | ||
260 | struct 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 | |||
268 | protected: | ||
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 | */ | ||
278 | template<typename T> | ||
279 | struct 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 | */ | ||
324 | template<typename T> | ||
325 | class AIThreadSafeSimple : public AIThreadSafeBits<T> | ||
326 | { | ||
327 | protected: | ||
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 | |||
338 | public: | ||
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 | */ | ||
392 | template<typename T> | ||
393 | class AIThreadSafeSimpleDC : public AIThreadSafeSimple<T> | ||
394 | { | ||
395 | public: | ||
396 | // Construct a wrapper around a default constructed object. | ||
397 | AIThreadSafeSimpleDC(void) { new (AIThreadSafeSimple<T>::ptr()) T; } | ||
398 | |||
399 | protected: | ||
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. | ||
406 | class AIThreadSafeSimpleDCRootPool_pbase | ||
407 | { | ||
408 | protected: | ||
409 | AIAPRRootPool mRootPool; | ||
410 | |||
411 | private: | ||
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 | */ | ||
426 | template<typename T> | ||
427 | class AIThreadSafeSimpleDCRootPool : private AIThreadSafeSimpleDCRootPool_pbase, public AIThreadSafeSimpleDC<T> | ||
428 | { | ||
429 | public: | ||
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 | */ | ||
441 | template<typename T> | ||
442 | struct 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 | |||
455 | protected: | ||
456 | AIThreadSafeSimple<T>& mWrapper; //!< Reference to the object that we provide access to. | ||
457 | |||
458 | private: | ||
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 | ||
238 | class AIRWLock | ||
239 | { | ||
240 | public: | ||
241 | AIRWLock(AIAPRPool& parent = LLThread::tldata().mRootPool) : | ||
242 | mWriterWaitingMutex(parent), mNoHoldersCondition(parent), mHoldersCount(0), mWriterIsWaiting(false) { } | ||
243 | |||
244 | private: | ||
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 | |||
259 | public: | ||
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 | ||
240 | void LLThread::lockData() | 335 | void 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 |