aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llcommon')
-rw-r--r--linden/indra/llcommon/CMakeLists.txt6
-rw-r--r--linden/indra/llcommon/aiaprpool.cpp198
-rw-r--r--linden/indra/llcommon/aiaprpool.h240
-rw-r--r--linden/indra/llcommon/aithreadsafe.h463
-rw-r--r--linden/indra/llcommon/llapp.cpp5
-rw-r--r--linden/indra/llcommon/llapr.cpp299
-rw-r--r--linden/indra/llcommon/llapr.h74
-rw-r--r--linden/indra/llcommon/llavatarname.cpp150
-rw-r--r--linden/indra/llcommon/llavatarname.h105
-rw-r--r--linden/indra/llcommon/llcommon.cpp13
-rw-r--r--linden/indra/llcommon/llcommon.h2
-rw-r--r--linden/indra/llcommon/llerror.cpp5
-rw-r--r--linden/indra/llcommon/llfile.cpp2
-rw-r--r--linden/indra/llcommon/llfindlocale.cpp2
-rw-r--r--linden/indra/llcommon/llfixedbuffer.cpp3
-rw-r--r--linden/indra/llcommon/llmemory.cpp2
-rw-r--r--linden/indra/llcommon/llprocesslauncher.h2
-rw-r--r--linden/indra/llcommon/llscopedvolatileaprpool.h58
-rw-r--r--linden/indra/llcommon/llthread.cpp117
-rw-r--r--linden/indra/llcommon/llthread.h184
-rw-r--r--linden/indra/llcommon/llworkerthread.cpp3
-rw-r--r--linden/indra/llcommon/llworkerthread.h2
22 files changed, 1518 insertions, 417 deletions
diff --git a/linden/indra/llcommon/CMakeLists.txt b/linden/indra/llcommon/CMakeLists.txt
index ff1d548..ed04ca6 100644
--- a/linden/indra/llcommon/CMakeLists.txt
+++ b/linden/indra/llcommon/CMakeLists.txt
@@ -13,11 +13,13 @@ include_directories(
13 ) 13 )
14 14
15set(llcommon_SOURCE_FILES 15set(llcommon_SOURCE_FILES
16 aiaprpool.cpp
16 imageids.cpp 17 imageids.cpp
17 indra_constants.cpp 18 indra_constants.cpp
18 llapp.cpp 19 llapp.cpp
19 llapr.cpp 20 llapr.cpp
20 llassettype.cpp 21 llassettype.cpp
22 llavatarname.cpp
21 llbase32.cpp 23 llbase32.cpp
22 llbase64.cpp 24 llbase64.cpp
23 llcommon.cpp 25 llcommon.cpp
@@ -74,6 +76,8 @@ set(llcommon_SOURCE_FILES
74set(llcommon_HEADER_FILES 76set(llcommon_HEADER_FILES
75 CMakeLists.txt 77 CMakeLists.txt
76 78
79 aiaprpool.h
80 aithreadsafe.h
77 bitpack.h 81 bitpack.h
78 ctype_workaround.h 82 ctype_workaround.h
79 doublelinkedlist.h 83 doublelinkedlist.h
@@ -87,6 +91,7 @@ set(llcommon_HEADER_FILES
87 llassettype.h 91 llassettype.h
88 llassoclist.h 92 llassoclist.h
89 llavatarconstants.h 93 llavatarconstants.h
94 llavatarname.h
90 llbase32.h 95 llbase32.h
91 llbase64.h 96 llbase64.h
92 llboost.h 97 llboost.h
@@ -147,6 +152,7 @@ set(llcommon_HEADER_FILES
147 llqueuedthread.h 152 llqueuedthread.h
148 llrand.h 153 llrand.h
149 llrun.h 154 llrun.h
155 llscopedvolatileaprpool.h
150 llsd.h 156 llsd.h
151 llsdserialize.h 157 llsdserialize.h
152 llsdserialize_xml.h 158 llsdserialize_xml.h
diff --git a/linden/indra/llcommon/aiaprpool.cpp b/linden/indra/llcommon/aiaprpool.cpp
new file mode 100644
index 0000000..d3748e9
--- /dev/null
+++ b/linden/indra/llcommon/aiaprpool.cpp
@@ -0,0 +1,198 @@
1/**
2 * @file aiaprpool.cpp
3 *
4 * Copyright (c) 2010, Aleric Inglewood.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * There are special exceptions to the terms and conditions of the GPL as
20 * it is applied to this Source Code. View the full text of the exception
21 * in the file doc/FLOSS-exception.txt in this software distribution.
22 *
23 * CHANGELOG
24 * and additional copyright holders.
25 *
26 * 04/04/2010
27 * - Initial version, written by Aleric Inglewood @ SL
28 *
29 * 10/11/2010
30 * - Changed filename, class names and license to a more
31 * company-neutral format.
32 * - Added APR_HAS_THREADS #if's to allow creation and destruction
33 * of subpools by threads other than the parent pool owner.
34 */
35
36#include "linden_common.h"
37
38#include "llerror.h"
39#include "aiaprpool.h"
40#include "llthread.h"
41
42// Create a subpool from parent.
43void AIAPRPool::create(AIAPRPool& parent)
44{
45 llassert(!mPool); // Must be non-initialized.
46 mParent = &parent;
47 if (!mParent) // Using the default parameter?
48 {
49 // By default use the root pool of the current thread.
50 mParent = &AIThreadLocalData::tldata().mRootPool;
51 }
52 llassert(mParent->mPool); // Parent must be initialized.
53#if APR_HAS_THREADS
54 // As per the documentation of APR (ie http://apr.apache.org/docs/apr/1.4/apr__pools_8h.html):
55 //
56 // Note that most operations on pools are not thread-safe: a single pool should only be
57 // accessed by a single thread at any given time. The one exception to this rule is creating
58 // a subpool of a given pool: one or more threads can safely create subpools at the same
59 // time that another thread accesses the parent pool.
60 //
61 // In other words, it's safe for any thread to create a (sub)pool, independent of who
62 // owns the parent pool.
63 mOwner = apr_os_thread_current();
64#else
65 mOwner = mParent->mOwner;
66 llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
67#endif
68 apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool);
69 llassert_always(apr_pool_create_status == APR_SUCCESS);
70 llassert(mPool); // Initialized.
71 apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
72}
73
74// Destroy the (sub)pool, if any.
75void AIAPRPool::destroy(void)
76{
77 // Only do anything if we are not already (being) destroyed.
78 if (mPool)
79 {
80#if !APR_HAS_THREADS
81 // If we are a root pool, then every thread may destruct us: in that case
82 // we have to assume that no other thread will use this pool concurrently,
83 // of course. Otherwise, if we are a subpool, only the thread that owns
84 // the parent may destruct us, since that is the pool that is still alive,
85 // possibly being used by others and being altered here.
86 llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current()));
87#endif
88 apr_pool_t* pool = mPool;
89 mPool = NULL; // Mark that we are BEING destructed.
90 apr_pool_cleanup_kill(pool, this, &s_plain_cleanup);
91 apr_pool_destroy(pool);
92 }
93}
94
95bool AIAPRPool::parent_is_being_destructed(void)
96{
97 return mParent && (!mParent->mPool || mParent->parent_is_being_destructed());
98}
99
100AIAPRInitialization::AIAPRInitialization(void)
101{
102 static bool apr_initialized = false;
103
104 if (!apr_initialized)
105 {
106 apr_initialize();
107 }
108
109 apr_initialized = true;
110}
111
112bool AIAPRRootPool::sCountInitialized = false;
113apr_uint32_t volatile AIAPRRootPool::sCount;
114
115extern apr_thread_mutex_t* gLogMutexp;
116extern apr_thread_mutex_t* gCallStacksLogMutexp;
117
118AIAPRRootPool::AIAPRRootPool(void) : AIAPRInitialization(), AIAPRPool(0)
119{
120 // sCountInitialized don't need locking because when we get here there is still only a single thread.
121 if (!sCountInitialized)
122 {
123 // Initialize the logging mutex
124 apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
125 apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
126
127 apr_status_t status = apr_atomic_init(mPool);
128 llassert_always(status == APR_SUCCESS);
129 apr_atomic_set32(&sCount, 1); // Set to 1 to account for the global root pool.
130 sCountInitialized = true;
131
132 // Initialize thread-local APR pool support.
133 // Because this recursively calls AIAPRRootPool::AIAPRRootPool(void)
134 // it must be done last, so that sCount is already initialized.
135 AIThreadLocalData::init();
136 }
137 apr_atomic_inc32(&sCount);
138}
139
140AIAPRRootPool::~AIAPRRootPool()
141{
142 if (!apr_atomic_dec32(&sCount))
143 {
144 // The last pool was destructed. Cleanup remainder of APR.
145 LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
146
147 if (gLogMutexp)
148 {
149 // Clean up the logging mutex
150
151 // All other threads NEED to be done before we clean up APR, so this is okay.
152 apr_thread_mutex_destroy(gLogMutexp);
153 gLogMutexp = NULL;
154 }
155 if (gCallStacksLogMutexp)
156 {
157 // Clean up the logging mutex
158
159 // All other threads NEED to be done before we clean up APR, so this is okay.
160 apr_thread_mutex_destroy(gCallStacksLogMutexp);
161 gCallStacksLogMutexp = NULL;
162 }
163
164 // Must destroy ALL, and therefore this last AIAPRRootPool, before terminating APR.
165 static_cast<AIAPRRootPool*>(this)->destroy();
166
167 apr_terminate();
168 }
169}
170
171//static
172AIAPRRootPool& AIAPRRootPool::get(void)
173{
174 static AIAPRRootPool global_APRpool(0); // This is what used to be gAPRPoolp.
175 return global_APRpool;
176}
177
178void AIVolatileAPRPool::clearVolatileAPRPool()
179{
180 llassert_always(mNumActiveRef > 0);
181 if (--mNumActiveRef == 0)
182 {
183 if (isOld())
184 {
185 destroy();
186 mNumTotalRef = 0 ;
187 }
188 else
189 {
190 // This does not actually free the memory,
191 // it just allows the pool to re-use this memory for the next allocation.
192 clear();
193 }
194 }
195
196 // Paranoia check if the pool is jammed.
197 llassert(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
198}
diff --git a/linden/indra/llcommon/aiaprpool.h b/linden/indra/llcommon/aiaprpool.h
new file mode 100644
index 0000000..ac523a9
--- /dev/null
+++ b/linden/indra/llcommon/aiaprpool.h
@@ -0,0 +1,240 @@
1/**
2 * @file aiaprpool.h
3 * @brief Implementation of AIAPRPool.
4 *
5 * Copyright (c) 2010, Aleric Inglewood.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * There are special exceptions to the terms and conditions of the GPL as
21 * it is applied to this Source Code. View the full text of the exception
22 * in the file doc/FLOSS-exception.txt in this software distribution.
23 *
24 * CHANGELOG
25 * and additional copyright holders.
26 *
27 * 04/04/2010
28 * - Initial version, written by Aleric Inglewood @ SL
29 *
30 * 10/11/2010
31 * - Changed filename, class names and license to a more
32 * company-neutral format.
33 * - Added APR_HAS_THREADS #if's to allow creation and destruction
34 * of subpools by threads other than the parent pool owner.
35 */
36
37#ifndef AIAPRPOOL_H
38#define AIAPRPOOL_H
39
40#ifdef LL_WINDOWS
41//#include <ws2tcpip.h>
42# define WIN32_LEAN_AND_MEAN
43# include <winsock2.h> // Needed before including apr_portable.h
44#endif
45
46#include "apr_portable.h"
47#include "apr_pools.h"
48#include "llerror.h"
49
50extern void ll_init_apr();
51
52/**
53 * @brief A wrapper around the APR memory pool API.
54 *
55 * Usage of this class should be restricted to passing it to libapr-1 function calls that need it.
56 *
57 */
58class LL_COMMON_API AIAPRPool
59{
60protected:
61 apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized.
62 AIAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero.
63 apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero.
64
65public:
66 //! Construct an uninitialized (destructed) pool.
67 AIAPRPool(void) : mPool(NULL) { }
68
69 //! Construct a subpool from an existing pool.
70 // This is not a copy-constructor, this class doesn't have one!
71 AIAPRPool(AIAPRPool& parent) : mPool(NULL) { create(parent); }
72
73 //! Destruct the memory pool (free all of it's subpools and allocated memory).
74 ~AIAPRPool() { destroy(); }
75
76protected:
77 // Create a pool that is allocated from the Operating System. Only used by AIAPRRootPool.
78 AIAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current())
79 {
80 apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL);
81 llassert_always(apr_pool_create_status == APR_SUCCESS);
82 llassert(mPool);
83 apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
84 }
85
86public:
87 //! Create a subpool from parent. May only be called for an uninitialized/destroyed pool.
88 // The default parameter causes the root pool of the current thread to be used.
89 void create(AIAPRPool& parent = *static_cast<AIAPRPool*>(NULL));
90
91 //! Destroy the (sub)pool, if any.
92 void destroy(void);
93
94 // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool.
95 typedef apr_pool_t* const AIAPRPool::* const bool_type;
96 //! Return true if the pool is initialized.
97 operator bool_type() const { return mPool ? &AIAPRPool::mPool : 0; }
98
99 // Painful, but we have to either provide access to this, or wrap
100 // every APR function call that needs a apr_pool_t* to be passed.
101 // NEVER destroy a pool that is returned by this function!
102 apr_pool_t* operator()(void) const
103 {
104 llassert(mPool);
105 llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
106 return mPool;
107 }
108
109 // Free all memory without destructing the pool.
110 void clear(void)
111 {
112 llassert(mPool);
113 llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
114 apr_pool_clear(mPool);
115 }
116
117// These methods would make this class 'complete' (as wrapper around the libapr
118// pool functions), but we don't use memory pools in the viewer (only when
119// we are forced to pass one to a libapr call), so don't define them in order
120// not to encourage people to use them.
121#if 0
122 void* palloc(size_t size)
123 {
124 llassert(mPool);
125 llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
126 return apr_palloc(mPool, size);
127 }
128 void* pcalloc(size_t size)
129 {
130 llassert(mPool);
131 llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
132 return apr_pcalloc(mPool, size);
133 }
134#endif
135
136private:
137 bool parent_is_being_destructed(void);
138 static apr_status_t s_plain_cleanup(void* userdata) { return static_cast<AIAPRPool*>(userdata)->plain_cleanup(); }
139
140 apr_status_t plain_cleanup(void)
141 {
142 if (mPool && // We are not being destructed,
143 parent_is_being_destructed()) // but our parent is.
144 // This means the pool is being destructed recursively by libapr
145 // because one of it's parents is being destructed.
146 {
147 mPool = NULL; // Stop destroy() from destructing the pool again.
148 }
149 return APR_SUCCESS;
150 }
151};
152
153class AIAPRInitialization
154{
155public:
156 AIAPRInitialization(void);
157};
158
159/**
160 * @brief Root memory pool (allocates memory from the operating system).
161 *
162 * This class should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase
163 * (and LLMutexRootPool when APR_HAS_THREADS isn't defined).
164 */
165class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool
166{
167private:
168 friend class AIThreadLocalData;
169 friend class AIThreadSafeSimpleDCRootPool_pbase;
170#if !APR_HAS_THREADS
171 friend class LLMutexRootPool;
172#endif
173 //! Construct a root memory pool.
174 // Should only be used by AIThreadLocalData and AIThreadSafeSimpleDCRootPool_pbase.
175 AIAPRRootPool(void);
176 ~AIAPRRootPool();
177
178private:
179 // Keep track of how many root pools exist and when the last one is destructed.
180 static bool sCountInitialized;
181 static apr_uint32_t volatile sCount;
182
183public:
184 // Return a global root pool that is independent of AIThreadLocalData.
185 // Normally you should not use this. Only use for early initialization
186 // (before main) and deinitialization (after main).
187 static AIAPRRootPool& get(void);
188
189#if APR_POOL_DEBUG
190 void grab_ownership(void)
191 {
192 // You need a patched libapr to use this.
193 // See http://web.archiveorange.com/archive/v/5XO9y2zoxUOMt6Gmi1OI
194 apr_pool_owner_set(mPool);
195 }
196#endif
197
198private:
199 // Used for constructing the Special Global Root Pool (returned by AIAPRRootPool::get).
200 // It is the same as the default constructor but omits to increment sCount. As a result,
201 // we must be sure that at least one other AIAPRRootPool is created before termination
202 // of the application (which is the case: we create one AIAPRRootPool per thread).
203 AIAPRRootPool(int) : AIAPRInitialization(), AIAPRPool(0) { }
204};
205
206//! Volatile memory pool
207//
208// 'Volatile' APR memory pool which normally only clears memory,
209// and does not destroy the pool (the same pool is reused) for
210// greater efficiency. However, as a safe guard the apr pool
211// is destructed every FULL_VOLATILE_APR_POOL uses to allow
212// the system memory to be allocated more efficiently and not
213// get scattered through RAM.
214//
215class LL_COMMON_API AIVolatileAPRPool : protected AIAPRPool
216{
217public:
218 AIVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { }
219
220 apr_pool_t* getVolatileAPRPool(void)
221 {
222 if (!mPool) create();
223 ++mNumActiveRef;
224 ++mNumTotalRef;
225 return AIAPRPool::operator()();
226 }
227 void clearVolatileAPRPool(void);
228
229 bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; }
230 bool isUnused() const { return mNumActiveRef == 0; }
231
232private:
233 S32 mNumActiveRef; // Number of active uses of the pool.
234 S32 mNumTotalRef; // Number of total uses of the pool since last creation.
235
236 // Maximum number of references to AIVolatileAPRPool until the pool is recreated.
237 static S32 const FULL_VOLATILE_APR_POOL = 1024;
238};
239
240#endif // AIAPRPOOL_H
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/llapp.cpp b/linden/indra/llcommon/llapp.cpp
index e269f59..eedc503 100644
--- a/linden/indra/llcommon/llapp.cpp
+++ b/linden/indra/llcommon/llapp.cpp
@@ -119,13 +119,8 @@ void LLApp::commonCtor()
119 mOptions.append(sd); 119 mOptions.append(sd);
120 } 120 }
121 121
122 // Make sure we clean up APR when we exit
123 // Don't need to do this if we're cleaning up APR in the destructor
124 //atexit(ll_cleanup_apr);
125
126 // Set the application to this instance. 122 // Set the application to this instance.
127 sApplication = this; 123 sApplication = this;
128
129} 124}
130 125
131LLApp::LLApp(LLErrorThread *error_thread) : 126LLApp::LLApp(LLErrorThread *error_thread) :
diff --git a/linden/indra/llcommon/llapr.cpp b/linden/indra/llcommon/llapr.cpp
index 7e3a26c..a013d9c 100644
--- a/linden/indra/llcommon/llapr.cpp
+++ b/linden/indra/llcommon/llapr.cpp
@@ -34,220 +34,7 @@
34 34
35#include "linden_common.h" 35#include "linden_common.h"
36#include "llapr.h" 36#include "llapr.h"
37 37#include "llscopedvolatileaprpool.h"
38apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
39apr_thread_mutex_t *gLogMutexp = NULL;
40apr_thread_mutex_t *gCallStacksLogMutexp = NULL;
41
42const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
43
44void ll_init_apr()
45{
46 if (!gAPRPoolp)
47 {
48 // Initialize APR and create the global pool
49 apr_initialize();
50 apr_pool_create(&gAPRPoolp, NULL);
51
52 // Initialize the logging mutex
53 apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
54 apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
55
56 // Initialize thread-local APR pool support.
57 LLVolatileAPRPool::initLocalAPRFilePool();
58 }
59}
60
61
62void ll_cleanup_apr()
63{
64 LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
65
66 if (gLogMutexp)
67 {
68 // Clean up the logging mutex
69
70 // All other threads NEED to be done before we clean up APR, so this is okay.
71 apr_thread_mutex_destroy(gLogMutexp);
72 gLogMutexp = NULL;
73 }
74 if (gCallStacksLogMutexp)
75 {
76 // Clean up the logging mutex
77
78 // All other threads NEED to be done before we clean up APR, so this is okay.
79 apr_thread_mutex_destroy(gCallStacksLogMutexp);
80 gCallStacksLogMutexp = NULL;
81 }
82 if (gAPRPoolp)
83 {
84 apr_pool_destroy(gAPRPoolp);
85 gAPRPoolp = NULL;
86 }
87 apr_terminate();
88}
89
90//
91//
92//LLAPRPool
93//
94LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
95{
96 mParent = parent ;
97 mReleasePoolFlag = releasePoolFlag ;
98 mMaxSize = size ;
99 mPool = NULL ;
100
101 createAPRPool() ;
102}
103
104LLAPRPool::~LLAPRPool()
105{
106 releaseAPRPool() ;
107}
108
109void LLAPRPool::createAPRPool()
110{
111 if(mPool)
112 {
113 return ;
114 }
115
116 mStatus = apr_pool_create(&mPool, mParent);
117 ll_apr_warn_status(mStatus) ;
118
119 if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes.
120 {
121 apr_allocator_t *allocator = apr_pool_allocator_get(mPool);
122 if (allocator)
123 {
124 apr_allocator_max_free_set(allocator, mMaxSize) ;
125 }
126 }
127}
128
129void LLAPRPool::releaseAPRPool()
130{
131 if(!mPool)
132 {
133 return ;
134 }
135
136 if(!mParent || mReleasePoolFlag)
137 {
138 apr_pool_destroy(mPool) ;
139 mPool = NULL ;
140 }
141}
142
143apr_pool_t* LLAPRPool::getAPRPool()
144{
145 if(!mPool)
146 {
147 createAPRPool() ;
148 }
149
150 return mPool ;
151}
152LLVolatileAPRPool::LLVolatileAPRPool(apr_pool_t *parent, apr_size_t size, BOOL releasePoolFlag)
153 : LLAPRPool(parent, size, releasePoolFlag)
154{
155 mNumActiveRef = 0 ;
156 mNumTotalRef = 0 ;
157}
158
159apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
160{
161 mNumTotalRef++ ;
162 mNumActiveRef++ ;
163 return getAPRPool() ;
164}
165
166void LLVolatileAPRPool::clearVolatileAPRPool()
167{
168 if(mNumActiveRef > 0)
169 {
170 mNumActiveRef--;
171 if(mNumActiveRef < 1)
172 {
173 if(isFull())
174 {
175 mNumTotalRef = 0 ;
176
177 //destroy the apr_pool.
178 releaseAPRPool() ;
179 }
180 else
181 {
182 //This does not actually free the memory,
183 //it just allows the pool to re-use this memory for the next allocation.
184 apr_pool_clear(mPool) ;
185 }
186 }
187 }
188 else
189 {
190 llassert_always(mNumActiveRef > 0) ;
191 }
192
193 //paranoia check if the pool is jammed.
194 //will remove the check before going to release.
195 llassert_always(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
196}
197
198BOOL LLVolatileAPRPool::isFull()
199{
200 return mNumTotalRef > FULL_VOLATILE_APR_POOL ;
201}
202
203#ifdef SHOW_ASSERT
204// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
205static void* gIsMainThread;
206bool is_main_thread() { return gIsMainThread == LLVolatileAPRPool::getLocalAPRFilePool(); }
207#endif
208
209// The thread private handle to access the LocalAPRFilePool.
210apr_threadkey_t* LLVolatileAPRPool::sLocalAPRFilePoolKey;
211
212// This should be called exactly once, before the first call to createLocalAPRFilePool.
213// static
214void LLVolatileAPRPool::initLocalAPRFilePool()
215{
216 apr_status_t status = apr_threadkey_private_create(&sLocalAPRFilePoolKey, &destroyLocalAPRFilePool, gAPRPoolp);
217 ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
218 // total number of keys per process {PTHREAD_KEYS_MAX}
219 // has been exceeded.
220 // Create the thread-local pool for the main thread (this function is called by the main thread).
221 createLocalAPRFilePool();
222#ifdef SHOW_ASSERT
223 gIsMainThread = getLocalAPRFilePool();
224#endif
225}
226
227// This should be called once for every thread, before it uses getLocalAPRFilePool.
228// static
229void LLVolatileAPRPool::createLocalAPRFilePool()
230{
231 void* thread_local_data = new LLVolatileAPRPool;
232 apr_status_t status = apr_threadkey_private_set(thread_local_data, sLocalAPRFilePoolKey);
233 llassert_always(status == APR_SUCCESS);
234}
235
236// This is called once for every thread when the thread is destructed.
237// static
238void LLVolatileAPRPool::destroyLocalAPRFilePool(void* thread_local_data)
239{
240 delete reinterpret_cast<LLVolatileAPRPool*>(thread_local_data);
241}
242
243// static
244LLVolatileAPRPool* LLVolatileAPRPool::getLocalAPRFilePool()
245{
246 void* thread_local_data;
247 apr_status_t status = apr_threadkey_private_get(&thread_local_data, sLocalAPRFilePoolKey);
248 llassert_always(status == APR_SUCCESS);
249 return reinterpret_cast<LLVolatileAPRPool*>(thread_local_data);
250}
251 38
252//--------------------------------------------------------------------- 39//---------------------------------------------------------------------
253// 40//
@@ -310,13 +97,15 @@ void ll_apr_assert_status(apr_status_t status)
310// 97//
311LLAPRFile::LLAPRFile() 98LLAPRFile::LLAPRFile()
312 : mFile(NULL), 99 : mFile(NULL),
313 mCurrentFilePoolp(NULL) 100 mVolatileFilePoolp(NULL),
101 mRegularFilePoolp(NULL)
314{ 102{
315} 103}
316 104
317LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type) 105LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type)
318 : mFile(NULL), 106 : mFile(NULL),
319 mCurrentFilePoolp(NULL) 107 mVolatileFilePoolp(NULL),
108 mRegularFilePoolp(NULL)
320{ 109{
321 open(filename, flags, access_type); 110 open(filename, flags, access_type);
322} 111}
@@ -335,10 +124,16 @@ apr_status_t LLAPRFile::close()
335 mFile = NULL ; 124 mFile = NULL ;
336 } 125 }
337 126
338 if(mCurrentFilePoolp) 127 if (mVolatileFilePoolp)
339 { 128 {
340 mCurrentFilePoolp->clearVolatileAPRPool() ; 129 mVolatileFilePoolp->clearVolatileAPRPool() ;
341 mCurrentFilePoolp = NULL ; 130 mVolatileFilePoolp = NULL ;
131 }
132
133 if (mRegularFilePoolp)
134 {
135 delete mRegularFilePoolp;
136 mRegularFilePoolp = NULL;
342 } 137 }
343 138
344 return ret ; 139 return ret ;
@@ -347,25 +142,28 @@ apr_status_t LLAPRFile::close()
347apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep) 142apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep)
348{ 143{
349 llassert_always(!mFile); 144 llassert_always(!mFile);
350 llassert_always(!mCurrentFilePoolp); 145 llassert_always(!mVolatileFilePoolp && !mRegularFilePoolp);
351 146
352 // Access the pool and increment it's reference count. 147 apr_status_t status;
353 // The reference count of LLVolatileAPRPool objects will be decremented
354 // again in LLAPRFile::close by calling mCurrentFilePoolp->clearVolatileAPRPool().
355 apr_pool_t* pool;
356 if (access_type == local)
357 {
358 // Use a "volatile" thread-local pool.
359 mCurrentFilePoolp = LLVolatileAPRPool::getLocalAPRFilePool();
360 pool = mCurrentFilePoolp->getVolatileAPRPool();
361 }
362 else
363 { 148 {
364 llassert(is_main_thread()); 149 apr_pool_t* apr_file_open_pool;
365 pool = gAPRPoolp; 150 if (access_type == local)
151 {
152 // Use a "volatile" thread-local pool.
153 mVolatileFilePoolp = &AIThreadLocalData::tldata().mVolatileAPRPool;
154 // Access the pool and increment it's reference count.
155 // The reference count of AIVolatileAPRPool objects will be decremented
156 // again in LLAPRFile::close by calling mVolatileFilePoolp->clearVolatileAPRPool().
157 apr_file_open_pool = mVolatileFilePoolp->getVolatileAPRPool();
158 }
159 else
160 {
161 mRegularFilePoolp = new AIAPRPool(AIThreadLocalData::tldata().mRootPool);
162 apr_file_open_pool = (*mRegularFilePoolp)();
163 }
164 status = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_file_open_pool);
366 } 165 }
367 apr_status_t s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, pool); 166 if (status != APR_SUCCESS || !mFile)
368 if (s != APR_SUCCESS || !mFile)
369 { 167 {
370 mFile = NULL ; 168 mFile = NULL ;
371 close() ; 169 close() ;
@@ -373,7 +171,7 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
373 { 171 {
374 *sizep = 0; 172 *sizep = 0;
375 } 173 }
376 return s; 174 return status;
377 } 175 }
378 176
379 if (sizep) 177 if (sizep)
@@ -390,7 +188,7 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
390 *sizep = file_size; 188 *sizep = file_size;
391 } 189 }
392 190
393 return s; 191 return status;
394} 192}
395 193
396// File I/O 194// File I/O
@@ -440,17 +238,6 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
440//static components of LLAPRFile 238//static components of LLAPRFile
441// 239//
442 240
443// Used in the static functions below.
444class LLScopedVolatileAPRFilePool {
445private:
446 LLVolatileAPRPool* mPool;
447 apr_pool_t* apr_pool;
448public:
449 LLScopedVolatileAPRFilePool() : mPool(LLVolatileAPRPool::getLocalAPRFilePool()), apr_pool(mPool->getVolatileAPRPool()) { }
450 ~LLScopedVolatileAPRFilePool() { mPool->clearVolatileAPRPool(); }
451 operator apr_pool_t*() const { return apr_pool; }
452};
453
454//static 241//static
455S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) 242S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
456{ 243{
@@ -487,7 +274,7 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
487S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes) 274S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes)
488{ 275{
489 apr_file_t* file_handle; 276 apr_file_t* file_handle;
490 LLScopedVolatileAPRFilePool pool; 277 LLScopedVolatileAPRPool pool;
491 apr_status_t s = apr_file_open(&file_handle, filename.c_str(), APR_READ|APR_BINARY, APR_OS_DEFAULT, pool); 278 apr_status_t s = apr_file_open(&file_handle, filename.c_str(), APR_READ|APR_BINARY, APR_OS_DEFAULT, pool);
492 if (s != APR_SUCCESS || !file_handle) 279 if (s != APR_SUCCESS || !file_handle)
493 { 280 {
@@ -539,7 +326,7 @@ S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 n
539 } 326 }
540 327
541 apr_file_t* file_handle; 328 apr_file_t* file_handle;
542 LLScopedVolatileAPRFilePool pool; 329 LLScopedVolatileAPRPool pool;
543 apr_status_t s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool); 330 apr_status_t s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
544 if (s != APR_SUCCESS || !file_handle) 331 if (s != APR_SUCCESS || !file_handle)
545 { 332 {
@@ -584,7 +371,7 @@ bool LLAPRFile::remove(const std::string& filename)
584{ 371{
585 apr_status_t s; 372 apr_status_t s;
586 373
587 LLScopedVolatileAPRFilePool pool; 374 LLScopedVolatileAPRPool pool;
588 s = apr_file_remove(filename.c_str(), pool); 375 s = apr_file_remove(filename.c_str(), pool);
589 376
590 if (s != APR_SUCCESS) 377 if (s != APR_SUCCESS)
@@ -601,7 +388,7 @@ bool LLAPRFile::rename(const std::string& filename, const std::string& newname)
601{ 388{
602 apr_status_t s; 389 apr_status_t s;
603 390
604 LLScopedVolatileAPRFilePool pool; 391 LLScopedVolatileAPRPool pool;
605 s = apr_file_rename(filename.c_str(), newname.c_str(), pool); 392 s = apr_file_rename(filename.c_str(), newname.c_str(), pool);
606 393
607 if (s != APR_SUCCESS) 394 if (s != APR_SUCCESS)
@@ -619,7 +406,7 @@ bool LLAPRFile::isExist(const std::string& filename, apr_int32_t flags)
619 apr_file_t* file_handle; 406 apr_file_t* file_handle;
620 apr_status_t s; 407 apr_status_t s;
621 408
622 LLScopedVolatileAPRFilePool pool; 409 LLScopedVolatileAPRPool pool;
623 s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool); 410 s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, pool);
624 411
625 if (s != APR_SUCCESS || !file_handle) 412 if (s != APR_SUCCESS || !file_handle)
@@ -640,7 +427,7 @@ S32 LLAPRFile::size(const std::string& filename)
640 apr_finfo_t info; 427 apr_finfo_t info;
641 apr_status_t s; 428 apr_status_t s;
642 429
643 LLScopedVolatileAPRFilePool pool; 430 LLScopedVolatileAPRPool pool;
644 s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool); 431 s = apr_file_open(&file_handle, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
645 432
646 if (s != APR_SUCCESS || !file_handle) 433 if (s != APR_SUCCESS || !file_handle)
@@ -669,7 +456,7 @@ bool LLAPRFile::makeDir(const std::string& dirname)
669{ 456{
670 apr_status_t s; 457 apr_status_t s;
671 458
672 LLScopedVolatileAPRFilePool pool; 459 LLScopedVolatileAPRPool pool;
673 s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool); 460 s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool);
674 461
675 if (s != APR_SUCCESS) 462 if (s != APR_SUCCESS)
@@ -686,7 +473,7 @@ bool LLAPRFile::removeDir(const std::string& dirname)
686{ 473{
687 apr_status_t s; 474 apr_status_t s;
688 475
689 LLScopedVolatileAPRFilePool pool; 476 LLScopedVolatileAPRPool pool;
690 s = apr_file_remove(dirname.c_str(), pool); 477 s = apr_file_remove(dirname.c_str(), pool);
691 478
692 if (s != APR_SUCCESS) 479 if (s != APR_SUCCESS)
diff --git a/linden/indra/llcommon/llapr.h b/linden/indra/llcommon/llapr.h
index 2aed515..ded15f5 100644
--- a/linden/indra/llcommon/llapr.h
+++ b/linden/indra/llcommon/llapr.h
@@ -48,73 +48,8 @@
48#include "apr_atomic.h" 48#include "apr_atomic.h"
49#include "llstring.h" 49#include "llstring.h"
50 50
51extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp; 51class AIAPRPool;
52 52class AIVolatileAPRPool;
53/**
54 * @brief initialize the common apr constructs -- apr itself, the
55 * global pool, and a mutex.
56 */
57void LL_COMMON_API ll_init_apr();
58
59/**
60 * @brief Cleanup those common apr constructs.
61 */
62void LL_COMMON_API ll_cleanup_apr();
63
64//
65//LL apr_pool
66//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
67//
68class LL_COMMON_API LLAPRPool
69{
70public:
71 LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
72 ~LLAPRPool() ;
73
74 apr_pool_t* getAPRPool() ;
75 apr_status_t getStatus() {return mStatus ; }
76
77protected:
78 void releaseAPRPool() ;
79 void createAPRPool() ;
80
81protected:
82 apr_pool_t* mPool ; //pointing to an apr_pool
83 apr_pool_t* mParent ; //parent pool
84 apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work.
85 apr_status_t mStatus ; //status when creating the pool
86 BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true.
87};
88
89//
90//volatile LL apr_pool
91//which clears memory automatically.
92//so it can not hold static data or data after memory is cleared
93//
94class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool
95{
96public:
97 LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
98 ~LLVolatileAPRPool(){}
99
100 apr_pool_t* getVolatileAPRPool() ;
101
102 void clearVolatileAPRPool() ;
103
104 BOOL isFull() ;
105 BOOL isEmpty() {return !mNumActiveRef ;}
106
107 static void initLocalAPRFilePool();
108 static void createLocalAPRFilePool();
109 static void destroyLocalAPRFilePool(void* thread_local_data);
110 static LLVolatileAPRPool* getLocalAPRFilePool();
111
112private:
113 S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
114 S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
115
116 static apr_threadkey_t* sLocalAPRFilePoolKey;
117} ;
118 53
119/** 54/**
120 * @class LLScopedLock 55 * @class LLScopedLock
@@ -205,7 +140,8 @@ class LL_COMMON_API LLAPRFile : boost::noncopyable
205 // make this non copyable since a copy closes the file 140 // make this non copyable since a copy closes the file
206private: 141private:
207 apr_file_t* mFile ; 142 apr_file_t* mFile ;
208 LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool. 143 AIVolatileAPRPool* mVolatileFilePoolp; // (Thread local) APR pool currently in use.
144 AIAPRPool* mRegularFilePoolp; // ...or a regular pool.
209 145
210public: 146public:
211 enum access_t { 147 enum access_t {
@@ -260,6 +196,4 @@ bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
260 196
261void LL_COMMON_API ll_apr_assert_status(apr_status_t status); 197void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
262 198
263extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
264
265#endif // LL_LLAPR_H 199#endif // LL_LLAPR_H
diff --git a/linden/indra/llcommon/llavatarname.cpp b/linden/indra/llcommon/llavatarname.cpp
new file mode 100644
index 0000000..ebe8c88
--- /dev/null
+++ b/linden/indra/llcommon/llavatarname.cpp
@@ -0,0 +1,150 @@
1/**
2 * @file llavatarname.cpp
3 * @brief Represents name-related data for an avatar, such as the
4 * username/SLID ("bobsmith123" or "james.linden") and the display
5 * name ("James Cook")
6 *
7 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
8 * Second Life Viewer Source Code
9 * Copyright (C) 2010, Linden Research, Inc.
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation;
14 * version 2.1 of the License only.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
26 * $/LicenseInfo$
27 */
28#include "linden_common.h"
29
30#include "llavatarname.h"
31
32#include "lldate.h"
33#include "llsd.h"
34
35bool LLAvatarName::sOmitResidentAsLastName = false;
36
37// Store these in pre-built std::strings to avoid memory allocations in
38// LLSD map lookups
39static const std::string USERNAME("username");
40static const std::string DISPLAY_NAME("display_name");
41static const std::string LEGACY_FIRST_NAME("legacy_first_name");
42static const std::string LEGACY_LAST_NAME("legacy_last_name");
43static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default");
44static const std::string DISPLAY_NAME_EXPIRES("display_name_expires");
45static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update");
46
47LLAvatarName::LLAvatarName()
48: mUsername(),
49 mDisplayName(),
50 mLegacyFirstName(),
51 mLegacyLastName(),
52 mIsDisplayNameDefault(false),
53 mIsDummy(false),
54 mExpires(F64_MAX),
55 mNextUpdate(0.0)
56{ }
57
58bool LLAvatarName::operator<(const LLAvatarName& rhs) const
59{
60 if (mUsername == rhs.mUsername)
61 return mDisplayName < rhs.mDisplayName;
62 else
63 return mUsername < rhs.mUsername;
64}
65
66LLSD LLAvatarName::asLLSD() const
67{
68 LLSD sd;
69 sd[USERNAME] = mUsername;
70 sd[DISPLAY_NAME] = mDisplayName;
71 sd[LEGACY_FIRST_NAME] = mLegacyFirstName;
72 sd[LEGACY_LAST_NAME] = mLegacyLastName;
73 sd[IS_DISPLAY_NAME_DEFAULT] = mIsDisplayNameDefault;
74 sd[DISPLAY_NAME_EXPIRES] = LLDate(mExpires);
75 sd[DISPLAY_NAME_NEXT_UPDATE] = LLDate(mNextUpdate);
76 return sd;
77}
78
79void LLAvatarName::fromLLSD(const LLSD& sd)
80{
81 mUsername = sd[USERNAME].asString();
82 mDisplayName = sd[DISPLAY_NAME].asString();
83 mLegacyFirstName = sd[LEGACY_FIRST_NAME].asString();
84 mLegacyLastName = sd[LEGACY_LAST_NAME].asString();
85 mIsDisplayNameDefault = sd[IS_DISPLAY_NAME_DEFAULT].asBoolean();
86 LLDate expires = sd[DISPLAY_NAME_EXPIRES];
87 mExpires = expires.secondsSinceEpoch();
88 LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE];
89 mNextUpdate = next_update.secondsSinceEpoch();
90}
91
92std::string LLAvatarName::getCompleteName() const
93{
94 std::string name;
95 if (!mUsername.empty())
96 {
97 name = mDisplayName + " (" + mUsername + ")";
98 }
99 else
100 {
101 // ...display names are off, legacy name is in mDisplayName
102 name = mDisplayName;
103 }
104 return name;
105}
106
107std::string LLAvatarName::getLegacyName() const
108{
109 std::string name;
110 name.reserve(mLegacyFirstName.size() + 1 + mLegacyLastName.size());
111 name = mLegacyFirstName;
112 if (!sOmitResidentAsLastName || mLegacyLastName != "Resident")
113 {
114 name += " ";
115 name += mLegacyLastName;
116 }
117 return name;
118}
119
120std::string LLAvatarName::getNames(bool linefeed) const
121{
122 std::string name;
123
124 if (mUsername.empty())
125 {
126 // ...display names are off, legacy name is in mDisplayName
127 name = mDisplayName;
128 if (sOmitResidentAsLastName)
129 {
130 LLStringUtil::replaceString(name, " Resident", "");
131 }
132 }
133 else
134 {
135 name = getLegacyName();
136 if (name != mDisplayName)
137 {
138 if (linefeed)
139 {
140 name = mDisplayName + "\n[" + name + "]";
141 }
142 else
143 {
144 name = mDisplayName + " [" + name + "]";
145 }
146 }
147 }
148
149 return name;
150}
diff --git a/linden/indra/llcommon/llavatarname.h b/linden/indra/llcommon/llavatarname.h
new file mode 100644
index 0000000..3b6c6ea
--- /dev/null
+++ b/linden/indra/llcommon/llavatarname.h
@@ -0,0 +1,105 @@
1/**
2 * @file llavatarname.h
3 * @brief Represents name-related data for an avatar, such as the
4 * username/SLID ("bobsmith123" or "james.linden") and the display
5 * name ("James Cook")
6 *
7 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
8 * Second Life Viewer Source Code
9 * Copyright (C) 2010, Linden Research, Inc.
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation;
14 * version 2.1 of the License only.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
26 * $/LicenseInfo$
27 */
28#ifndef LLAVATARNAME_H
29#define LLAVATARNAME_H
30
31#include <string>
32
33class LLSD;
34
35class LL_COMMON_API LLAvatarName
36{
37public:
38 LLAvatarName();
39
40 bool operator<(const LLAvatarName& rhs) const;
41
42 LLSD asLLSD() const;
43
44 void fromLLSD(const LLSD& sd);
45
46 // For normal names, returns "James Linden (james.linden)"
47 // When display names are disabled returns just "James Linden"
48 std::string getCompleteName() const;
49
50 // For normal names, returns "Whatever Display Name (John Doe)" when
51 // display name and legacy name are different, or just "John Doe"
52 // when they are equal or when display names are disabled.
53 // When linefeed == true, the space between the display name and
54 // the opening parenthesis for the legacy name is replaced with a
55 // line feed.
56 std::string getNames(bool linefeed = false) const;
57
58 // Returns "James Linden" or "bobsmith123 Resident" for backwards
59 // compatibility with systems like voice and muting
60 std::string getLegacyName() const;
61
62 // "bobsmith123" or "james.linden", US-ASCII only
63 std::string mUsername;
64
65 // "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode
66 // Contains data whether or not user has explicitly set
67 // a display name; may duplicate their username.
68 std::string mDisplayName;
69
70 // For "James Linden", "James"
71 // For "bobsmith123", "bobsmith123"
72 // Used to communicate with legacy systems like voice and muting which
73 // rely on old-style names.
74 std::string mLegacyFirstName;
75
76 // For "James Linden", "Linden"
77 // For "bobsmith123", "Resident"
78 // see above for rationale
79 std::string mLegacyLastName;
80
81 // If true, both display name and SLID were generated from
82 // a legacy first and last name, like "James Linden (james.linden)"
83 bool mIsDisplayNameDefault;
84
85 // Under error conditions, we may insert "dummy" records with
86 // names equal to legacy name into caches as placeholders.
87 // These can be shown in UI, but are not serialized.
88 bool mIsDummy;
89
90 // Names can change, so need to keep track of when name was
91 // last checked.
92 // Unix time-from-epoch seconds for efficiency
93 F64 mExpires;
94
95 // You can only change your name every N hours, so record
96 // when the next update is allowed
97 // Unix time-from-epoch seconds
98 F64 mNextUpdate;
99
100 // true to prevent the displaying of "Resident" as a last name
101 // in legacy names
102 static bool sOmitResidentAsLastName;
103};
104
105#endif
diff --git a/linden/indra/llcommon/llcommon.cpp b/linden/indra/llcommon/llcommon.cpp
index 2cbb718..298dd46 100644
--- a/linden/indra/llcommon/llcommon.cpp
+++ b/linden/indra/llcommon/llcommon.cpp
@@ -35,17 +35,9 @@
35#include "llthread.h" 35#include "llthread.h"
36 36
37//static 37//static
38BOOL LLCommon::sAprInitialized = FALSE;
39
40//static
41void LLCommon::initClass() 38void LLCommon::initClass()
42{ 39{
43 LLMemory::initClass(); 40 LLMemory::initClass();
44 if (!sAprInitialized)
45 {
46 ll_init_apr();
47 sAprInitialized = TRUE;
48 }
49 LLTimer::initClass(); 41 LLTimer::initClass();
50 LLThreadSafeRefCount::initThreadSafeRefCount(); 42 LLThreadSafeRefCount::initThreadSafeRefCount();
51// LLWorkerThread::initClass(); 43// LLWorkerThread::initClass();
@@ -59,10 +51,5 @@ void LLCommon::cleanupClass()
59// LLWorkerThread::cleanupClass(); 51// LLWorkerThread::cleanupClass();
60 LLThreadSafeRefCount::cleanupThreadSafeRefCount(); 52 LLThreadSafeRefCount::cleanupThreadSafeRefCount();
61 LLTimer::cleanupClass(); 53 LLTimer::cleanupClass();
62 if (sAprInitialized)
63 {
64 ll_cleanup_apr();
65 sAprInitialized = FALSE;
66 }
67 LLMemory::cleanupClass(); 54 LLMemory::cleanupClass();
68} 55}
diff --git a/linden/indra/llcommon/llcommon.h b/linden/indra/llcommon/llcommon.h
index 851d4ac..300ebe2 100644
--- a/linden/indra/llcommon/llcommon.h
+++ b/linden/indra/llcommon/llcommon.h
@@ -43,8 +43,6 @@ class LL_COMMON_API LLCommon
43public: 43public:
44 static void initClass(); 44 static void initClass();
45 static void cleanupClass(); 45 static void cleanupClass();
46private:
47 static BOOL sAprInitialized;
48}; 46};
49 47
50#endif 48#endif
diff --git a/linden/indra/llcommon/llerror.cpp b/linden/indra/llcommon/llerror.cpp
index edc570f..a9587c6 100644
--- a/linden/indra/llcommon/llerror.cpp
+++ b/linden/indra/llcommon/llerror.cpp
@@ -46,6 +46,8 @@
46# include <unistd.h> 46# include <unistd.h>
47#endif // !LL_WINDOWS 47#endif // !LL_WINDOWS
48#if LL_WINDOWS 48#if LL_WINDOWS
49# define WIN32_LEAN_AND_MEAN
50# include <winsock2.h>
49# include <windows.h> 51# include <windows.h>
50#endif // LL_WINDOWS 52#endif // LL_WINDOWS
51#include <vector> 53#include <vector>
@@ -871,6 +873,9 @@ You get:
871 873
872*/ 874*/
873 875
876apr_thread_mutex_t* gLogMutexp;
877apr_thread_mutex_t* gCallStacksLogMutexp;
878
874namespace { 879namespace {
875 bool checkLevelMap(const LevelMap& map, const std::string& key, 880 bool checkLevelMap(const LevelMap& map, const std::string& key,
876 LLError::ELevel& level) 881 LLError::ELevel& level)
diff --git a/linden/indra/llcommon/llfile.cpp b/linden/indra/llcommon/llfile.cpp
index 2a76f7f..6b68630 100644
--- a/linden/indra/llcommon/llfile.cpp
+++ b/linden/indra/llcommon/llfile.cpp
@@ -34,6 +34,8 @@
34 */ 34 */
35 35
36#if LL_WINDOWS 36#if LL_WINDOWS
37# define WIN32_LEAN_AND_MEAN
38# include <winsock2.h>
37#include <windows.h> 39#include <windows.h>
38#endif 40#endif
39 41
diff --git a/linden/indra/llcommon/llfindlocale.cpp b/linden/indra/llcommon/llfindlocale.cpp
index 505f5c5..71675af 100644
--- a/linden/indra/llcommon/llfindlocale.cpp
+++ b/linden/indra/llcommon/llfindlocale.cpp
@@ -39,6 +39,8 @@
39#include <ctype.h> 39#include <ctype.h>
40 40
41#ifdef WIN32 41#ifdef WIN32
42# define WIN32_LEAN_AND_MEAN
43# include <winsock2.h>
42#include <windows.h> 44#include <windows.h>
43#include <winnt.h> 45#include <winnt.h>
44#endif 46#endif
diff --git a/linden/indra/llcommon/llfixedbuffer.cpp b/linden/indra/llcommon/llfixedbuffer.cpp
index e9d6029..37a12ad 100644
--- a/linden/indra/llcommon/llfixedbuffer.cpp
+++ b/linden/indra/llcommon/llfixedbuffer.cpp
@@ -33,9 +33,8 @@
33#include "llfixedbuffer.h" 33#include "llfixedbuffer.h"
34 34
35LLFixedBuffer::LLFixedBuffer(const U32 max_lines) 35LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
36 : mMutex(NULL) 36 : mMaxLines(max_lines)
37{ 37{
38 mMaxLines = max_lines;
39 mTimer.reset(); 38 mTimer.reset();
40} 39}
41 40
diff --git a/linden/indra/llcommon/llmemory.cpp b/linden/indra/llcommon/llmemory.cpp
index 74004b0..2b01442 100644
--- a/linden/indra/llcommon/llmemory.cpp
+++ b/linden/indra/llcommon/llmemory.cpp
@@ -33,6 +33,8 @@
33#include "linden_common.h" 33#include "linden_common.h"
34 34
35#if defined(LL_WINDOWS) 35#if defined(LL_WINDOWS)
36# define WIN32_LEAN_AND_MEAN
37# include <winsock2.h>
36# include <windows.h> 38# include <windows.h>
37# include <psapi.h> 39# include <psapi.h>
38#elif defined(LL_DARWIN) 40#elif defined(LL_DARWIN)
diff --git a/linden/indra/llcommon/llprocesslauncher.h b/linden/indra/llcommon/llprocesslauncher.h
index b72be27..9833a13 100644
--- a/linden/indra/llcommon/llprocesslauncher.h
+++ b/linden/indra/llcommon/llprocesslauncher.h
@@ -34,6 +34,8 @@
34#define LL_LLPROCESSLAUNCHER_H 34#define LL_LLPROCESSLAUNCHER_H
35 35
36#if LL_WINDOWS 36#if LL_WINDOWS
37# define WIN32_LEAN_AND_MEAN
38# include <winsock2.h>
37#include <windows.h> 39#include <windows.h>
38#endif 40#endif
39 41
diff --git a/linden/indra/llcommon/llscopedvolatileaprpool.h b/linden/indra/llcommon/llscopedvolatileaprpool.h
new file mode 100644
index 0000000..724dc7f
--- /dev/null
+++ b/linden/indra/llcommon/llscopedvolatileaprpool.h
@@ -0,0 +1,58 @@
1/**
2 * @file llscopedvolatileaprpool.h
3 * @brief Implementation of LLScopedVolatileAPRPool
4 *
5 * $LicenseInfo:firstyear=2010&license=viewergpl$
6 *
7 * Copyright (c) 2010, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#ifndef LL_LLSCOPEDVOLATILEAPRPOOL_H
34#define LL_LLSCOPEDVOLATILEAPRPOOL_H
35
36#include "llthread.h"
37
38//! Scoped volatile memory pool.
39//
40// As the AIVolatileAPRPool should never keep allocations very
41// long, it's most common use is for allocations with a lifetime
42// equal to it's scope.
43//
44// This is a convenience class that makes just a little easier to type.
45//
46class LLScopedVolatileAPRPool
47{
48private:
49 AIVolatileAPRPool& mPool;
50 apr_pool_t* mScopedAPRpool;
51public:
52 LLScopedVolatileAPRPool() : mPool(AIThreadLocalData::tldata().mVolatileAPRPool), mScopedAPRpool(mPool.getVolatileAPRPool()) { }
53 ~LLScopedVolatileAPRPool() { mPool.clearVolatileAPRPool(); }
54 // Only use this to pass the pointer to a libapr-1 function that requires it.
55 operator apr_pool_t*() const { return mScopedAPRpool; }
56};
57
58#endif
diff --git a/linden/indra/llcommon/llthread.cpp b/linden/indra/llcommon/llthread.cpp
index 692d6c4..f7732cb 100644
--- a/linden/indra/llcommon/llthread.cpp
+++ b/linden/indra/llcommon/llthread.cpp
@@ -72,8 +72,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
72 // Set thread state to running 72 // Set thread state to running
73 threadp->mStatus = RUNNING; 73 threadp->mStatus = RUNNING;
74 74
75 // Create a thread local APRFile pool. 75 // Create a thread local data.
76 LLVolatileAPRPool::createLocalAPRFilePool(); 76 AIThreadLocalData::create(threadp);
77 77
78 // Run the user supplied function 78 // Run the user supplied function
79 threadp->run(); 79 threadp->run();
@@ -87,24 +87,14 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
87} 87}
88 88
89 89
90LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : 90LLThread::LLThread(std::string const& name) :
91 mPaused(false), 91 mPaused(false),
92 mName(name), 92 mName(name),
93 mAPRThreadp(NULL), 93 mAPRThreadp(NULL),
94 mStatus(STOPPED) 94 mStatus(STOPPED),
95 mThreadLocalData(NULL)
95{ 96{
96 // Thread creation probably CAN be paranoid about APR being initialized, if necessary 97 mRunCondition = new LLCondition;
97 if (poolp)
98 {
99 mIsLocalPool = false;
100 mAPRPoolp = poolp;
101 }
102 else
103 {
104 mIsLocalPool = true;
105 apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
106 }
107 mRunCondition = new LLCondition(mAPRPoolp);
108} 98}
109 99
110 100
@@ -147,24 +137,18 @@ void LLThread::shutdown()
147 if (!isStopped()) 137 if (!isStopped())
148 { 138 {
149 // This thread just wouldn't stop, even though we gave it time 139 // This thread just wouldn't stop, even though we gave it time
150 llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; 140 llwarns << "LLThread::shutdown() exiting thread before clean exit!" << llendl;
151 return; 141 return;
152 } 142 }
153 mAPRThreadp = NULL; 143 mAPRThreadp = NULL;
154 } 144 }
155 145
156 delete mRunCondition; 146 delete mRunCondition;
157
158 if (mIsLocalPool)
159 {
160 apr_pool_destroy(mAPRPoolp);
161 }
162} 147}
163 148
164
165void LLThread::start() 149void LLThread::start()
166{ 150{
167 apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); 151 apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool());
168 152
169 // We won't bother joining 153 // We won't bother joining
170 apr_thread_detach(mAPRThreadp); 154 apr_thread_detach(mAPRThreadp);
@@ -265,38 +249,72 @@ void LLThread::wakeLocked()
265 } 249 }
266} 250}
267 251
268//============================================================================ 252#ifdef SHOW_ASSERT
253// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread.
254static apr_os_thread_t main_thread_id;
255bool is_main_thread() { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); }
256#endif
269 257
270LLMutex::LLMutex(apr_pool_t *poolp) : 258// The thread private handle to access the AIThreadLocalData instance.
271 mAPRMutexp(NULL) 259apr_threadkey_t* AIThreadLocalData::sThreadLocalDataKey;
260
261//static
262void AIThreadLocalData::init(void)
272{ 263{
273 //if (poolp) 264 // Only do this once.
274 //{ 265 if (sThreadLocalDataKey)
275 // mIsLocalPool = false;
276 // mAPRPoolp = poolp;
277 //}
278 //else
279 { 266 {
280 mIsLocalPool = true; 267 return;
281 apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
282 } 268 }
283 apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp); 269
270 apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &AIThreadLocalData::destroy, AIAPRRootPool::get()());
271 ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the
272 // total number of keys per process {PTHREAD_KEYS_MAX}
273 // has been exceeded.
274
275 // Create the thread-local data for the main thread (this function is called by the main thread).
276 AIThreadLocalData::create(NULL);
277
278#ifdef SHOW_ASSERT
279 // This function is called by the main thread.
280 main_thread_id = apr_os_thread_current();
281#endif
284} 282}
285 283
286LLMutex::~LLMutex() 284// This is called once for every thread when the thread is destructed.
285//static
286void AIThreadLocalData::destroy(void* thread_local_data)
287{ 287{
288#if _DEBUG 288 delete reinterpret_cast<AIThreadLocalData*>(thread_local_data);
289 llassert(!isLocked()); // better not be locked! 289}
290#endif 290
291 apr_thread_mutex_destroy(mAPRMutexp); 291//static
292 mAPRMutexp = NULL; 292void AIThreadLocalData::create(LLThread* threadp)
293 if (mIsLocalPool) 293{
294 AIThreadLocalData* new_tld = new AIThreadLocalData;
295 if (threadp)
294 { 296 {
295 apr_pool_destroy(mAPRPoolp); 297 threadp->mThreadLocalData = new_tld;
296 } 298 }
299 apr_status_t status = apr_threadkey_private_set(new_tld, sThreadLocalDataKey);
300 llassert_always(status == APR_SUCCESS);
297} 301}
298 302
299bool LLMutex::isLocked() 303//static
304AIThreadLocalData& AIThreadLocalData::tldata(void)
305{
306 if (!sThreadLocalDataKey)
307 AIThreadLocalData::init();
308
309 void* data;
310 apr_status_t status = apr_threadkey_private_get(&data, sThreadLocalDataKey);
311 llassert_always(status == APR_SUCCESS);
312 return *static_cast<AIThreadLocalData*>(data);
313}
314
315//============================================================================
316
317bool LLMutexBase::isLocked()
300{ 318{
301 if (!tryLock()) 319 if (!tryLock())
302 { 320 {
@@ -308,12 +326,9 @@ bool LLMutex::isLocked()
308 326
309//============================================================================ 327//============================================================================
310 328
311LLCondition::LLCondition(apr_pool_t *poolp) : 329LLCondition::LLCondition(AIAPRPool& parent) : LLMutex(parent)
312 LLMutex(poolp)
313{ 330{
314 // base class (LLMutex) has already ensured that mAPRPoolp is set up. 331 apr_thread_cond_create(&mAPRCondp, mPool());
315
316 apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
317} 332}
318 333
319LLCondition::~LLCondition() 334LLCondition::~LLCondition()
@@ -349,7 +364,7 @@ void LLThreadSafeRefCount::initThreadSafeRefCount()
349{ 364{
350 if (!sMutex) 365 if (!sMutex)
351 { 366 {
352 sMutex = new LLMutex(0); 367 sMutex = new LLMutex;
353 } 368 }
354} 369}
355 370
diff --git a/linden/indra/llcommon/llthread.h b/linden/indra/llcommon/llthread.h
index 98d64ef..1e982cc 100644
--- a/linden/indra/llcommon/llthread.h
+++ b/linden/indra/llcommon/llthread.h
@@ -38,11 +38,28 @@
38#include "llmemory.h" 38#include "llmemory.h"
39 39
40#include "apr_thread_cond.h" 40#include "apr_thread_cond.h"
41#include "aiaprpool.h"
41 42
42class LLThread; 43class LLThread;
43class LLMutex; 44class LLMutex;
44class LLCondition; 45class LLCondition;
45 46
47class LL_COMMON_API AIThreadLocalData
48{
49private:
50 static apr_threadkey_t* sThreadLocalDataKey;
51
52public:
53 // Thread-local memory pool.
54 AIAPRRootPool mRootPool;
55 AIVolatileAPRPool mVolatileAPRPool;
56
57 static void init(void);
58 static void destroy(void* thread_local_data);
59 static void create(LLThread* pthread);
60 static AIThreadLocalData& tldata(void);
61};
62
46class LL_COMMON_API LLThread 63class LL_COMMON_API LLThread
47{ 64{
48public: 65public:
@@ -53,7 +70,7 @@ public:
53 QUITTING= 2 // Someone wants this thread to quit 70 QUITTING= 2 // Someone wants this thread to quit
54 } EThreadStatus; 71 } EThreadStatus;
55 72
56 LLThread(const std::string& name, apr_pool_t *poolp = NULL); 73 LLThread(std::string const& name);
57 virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. 74 virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state.
58 virtual void shutdown(); // stops the thread 75 virtual void shutdown(); // stops the thread
59 76
@@ -82,7 +99,8 @@ public:
82 // this kicks off the apr thread 99 // this kicks off the apr thread
83 void start(void); 100 void start(void);
84 101
85 apr_pool_t *getAPRPool() { return mAPRPoolp; } 102 // Return thread-local data for the current thread.
103 static AIThreadLocalData& tldata(void) { return AIThreadLocalData::tldata(); }
86 104
87private: 105private:
88 bool mPaused; 106 bool mPaused;
@@ -95,10 +113,11 @@ protected:
95 LLCondition* mRunCondition; 113 LLCondition* mRunCondition;
96 114
97 apr_thread_t *mAPRThreadp; 115 apr_thread_t *mAPRThreadp;
98 apr_pool_t *mAPRPoolp;
99 bool mIsLocalPool;
100 EThreadStatus mStatus; 116 EThreadStatus mStatus;
101 117
118 friend void AIThreadLocalData::create(LLThread* threadp);
119 AIThreadLocalData* mThreadLocalData;
120
102 void setQuitting(); 121 void setQuitting();
103 122
104 // virtual function overridden by subclass -- this will be called when the thread runs 123 // virtual function overridden by subclass -- this will be called when the thread runs
@@ -125,12 +144,9 @@ protected:
125 144
126//============================================================================ 145//============================================================================
127 146
128class LL_COMMON_API LLMutex 147class LL_COMMON_API LLMutexBase
129{ 148{
130public: 149public:
131 LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
132 ~LLMutex();
133
134 void lock() { apr_thread_mutex_lock(mAPRMutexp); } 150 void lock() { apr_thread_mutex_lock(mAPRMutexp); }
135 void unlock() { apr_thread_mutex_unlock(mAPRMutexp); } 151 void unlock() { apr_thread_mutex_unlock(mAPRMutexp); }
136 // Returns true if lock was obtained successfully. 152 // Returns true if lock was obtained successfully.
@@ -139,16 +155,60 @@ public:
139 bool isLocked(); // non-blocking, but does do a lock/unlock so not free 155 bool isLocked(); // non-blocking, but does do a lock/unlock so not free
140 156
141protected: 157protected:
142 apr_thread_mutex_t *mAPRMutexp; 158 // mAPRMutexp is initialized and uninitialized in the derived class.
143 apr_pool_t *mAPRPoolp; 159 apr_thread_mutex_t* mAPRMutexp;
144 bool mIsLocalPool; 160};
161
162class LL_COMMON_API LLMutex : public LLMutexBase
163{
164public:
165 LLMutex(AIAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent)
166 {
167 apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mPool());
168 }
169 ~LLMutex()
170 {
171 llassert(!isLocked()); // better not be locked!
172 apr_thread_mutex_destroy(mAPRMutexp);
173 mAPRMutexp = NULL;
174 }
175
176protected:
177 AIAPRPool mPool;
145}; 178};
146 179
180#if APR_HAS_THREADS
181// No need to use a root pool in this case.
182typedef LLMutex LLMutexRootPool;
183#else // APR_HAS_THREADS
184class LL_COMMON_API LLMutexRootPool : public LLMutexBase
185{
186public:
187 LLMutexRootPool(void)
188 {
189 apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mRootPool());
190 }
191 ~LLMutexRootPool()
192 {
193#if APR_POOL_DEBUG
194 // It is allowed to destruct root pools from a different thread.
195 mRootPool.grab_ownership();
196#endif
197 llassert(!isLocked()); // better not be locked!
198 apr_thread_mutex_destroy(mAPRMutexp);
199 mAPRMutexp = NULL;
200 }
201
202protected:
203 AIAPRRootPool mRootPool;
204};
205#endif // APR_HAS_THREADS
206
147// Actually a condition/mutex pair (since each condition needs to be associated with a mutex). 207// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
148class LL_COMMON_API LLCondition : public LLMutex 208class LL_COMMON_API LLCondition : public LLMutex
149{ 209{
150public: 210public:
151 LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well. 211 LLCondition(AIAPRPool& parent = LLThread::tldata().mRootPool);
152 ~LLCondition(); 212 ~LLCondition();
153 213
154 void wait(); // blocks 214 void wait(); // blocks
@@ -162,7 +222,7 @@ protected:
162class LL_COMMON_API LLMutexLock 222class LL_COMMON_API LLMutexLock
163{ 223{
164public: 224public:
165 LLMutexLock(LLMutex* mutex) 225 LLMutexLock(LLMutexBase* mutex)
166 { 226 {
167 mMutex = mutex; 227 mMutex = mutex;
168 mMutex->lock(); 228 mMutex->lock();
@@ -172,7 +232,102 @@ public:
172 mMutex->unlock(); 232 mMutex->unlock();
173 } 233 }
174private: 234private:
175 LLMutex* mMutex; 235 LLMutexBase* mMutex;
236};
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 }
176}; 331};
177 332
178//============================================================================ 333//============================================================================
@@ -187,7 +342,6 @@ void LLThread::unlockData()
187 mRunCondition->unlock(); 342 mRunCondition->unlock();
188} 343}
189 344
190
191//============================================================================ 345//============================================================================
192 346
193// see llmemory.h for LLPointer<> definition 347// see llmemory.h for LLPointer<> definition
diff --git a/linden/indra/llcommon/llworkerthread.cpp b/linden/indra/llcommon/llworkerthread.cpp
index 8195e1c..e238a89 100644
--- a/linden/indra/llcommon/llworkerthread.cpp
+++ b/linden/indra/llcommon/llworkerthread.cpp
@@ -43,7 +43,7 @@
43LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) : 43LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
44 LLQueuedThread(name, threaded) 44 LLQueuedThread(name, threaded)
45{ 45{
46 mDeleteMutex = new LLMutex(NULL); 46 mDeleteMutex = new LLMutex;
47} 47}
48 48
49LLWorkerThread::~LLWorkerThread() 49LLWorkerThread::~LLWorkerThread()
@@ -183,7 +183,6 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
183 : mWorkerThread(workerthread), 183 : mWorkerThread(workerthread),
184 mWorkerClassName(name), 184 mWorkerClassName(name),
185 mRequestHandle(LLWorkerThread::nullHandle()), 185 mRequestHandle(LLWorkerThread::nullHandle()),
186 mMutex(NULL),
187 mWorkFlags(0) 186 mWorkFlags(0)
188{ 187{
189 if (!mWorkerThread) 188 if (!mWorkerThread)
diff --git a/linden/indra/llcommon/llworkerthread.h b/linden/indra/llcommon/llworkerthread.h
index 33d4c40..8e0dd8a 100644
--- a/linden/indra/llcommon/llworkerthread.h
+++ b/linden/indra/llcommon/llworkerthread.h
@@ -194,7 +194,7 @@ protected:
194 U32 mRequestPriority; // last priority set 194 U32 mRequestPriority; // last priority set
195 195
196private: 196private:
197 LLMutex mMutex; 197 LLMutexRootPool mMutex; // Use LLMutexRootPool since this object is created and destructed by multiple threads.
198 LLAtomicU32 mWorkFlags; 198 LLAtomicU32 mWorkFlags;
199}; 199};
200 200