diff options
Diffstat (limited to 'linden/indra/llcommon')
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 | ||
15 | set(llcommon_SOURCE_FILES | 15 | set(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 | |||
74 | set(llcommon_HEADER_FILES | 76 | set(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. | ||
43 | void 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. | ||
75 | void 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 | |||
95 | bool AIAPRPool::parent_is_being_destructed(void) | ||
96 | { | ||
97 | return mParent && (!mParent->mPool || mParent->parent_is_being_destructed()); | ||
98 | } | ||
99 | |||
100 | AIAPRInitialization::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 | |||
112 | bool AIAPRRootPool::sCountInitialized = false; | ||
113 | apr_uint32_t volatile AIAPRRootPool::sCount; | ||
114 | |||
115 | extern apr_thread_mutex_t* gLogMutexp; | ||
116 | extern apr_thread_mutex_t* gCallStacksLogMutexp; | ||
117 | |||
118 | AIAPRRootPool::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 | |||
140 | AIAPRRootPool::~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 | ||
172 | AIAPRRootPool& AIAPRRootPool::get(void) | ||
173 | { | ||
174 | static AIAPRRootPool global_APRpool(0); // This is what used to be gAPRPoolp. | ||
175 | return global_APRpool; | ||
176 | } | ||
177 | |||
178 | void 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 | |||
50 | extern 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 | */ | ||
58 | class LL_COMMON_API AIAPRPool | ||
59 | { | ||
60 | protected: | ||
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 | |||
65 | public: | ||
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 | |||
76 | protected: | ||
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 | |||
86 | public: | ||
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 | |||
136 | private: | ||
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 | |||
153 | class AIAPRInitialization | ||
154 | { | ||
155 | public: | ||
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 | */ | ||
165 | class LL_COMMON_API AIAPRRootPool : public AIAPRInitialization, public AIAPRPool | ||
166 | { | ||
167 | private: | ||
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 | |||
178 | private: | ||
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 | |||
183 | public: | ||
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 | |||
198 | private: | ||
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 | // | ||
215 | class LL_COMMON_API AIVolatileAPRPool : protected AIAPRPool | ||
216 | { | ||
217 | public: | ||
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 | |||
232 | private: | ||
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 | |||
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/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 | ||
131 | LLApp::LLApp(LLErrorThread *error_thread) : | 126 | LLApp::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" | |
38 | apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool | ||
39 | apr_thread_mutex_t *gLogMutexp = NULL; | ||
40 | apr_thread_mutex_t *gCallStacksLogMutexp = NULL; | ||
41 | |||
42 | const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool | ||
43 | |||
44 | void 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 | |||
62 | void 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 | // | ||
94 | LLAPRPool::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 | |||
104 | LLAPRPool::~LLAPRPool() | ||
105 | { | ||
106 | releaseAPRPool() ; | ||
107 | } | ||
108 | |||
109 | void 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 | |||
129 | void 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 | |||
143 | apr_pool_t* LLAPRPool::getAPRPool() | ||
144 | { | ||
145 | if(!mPool) | ||
146 | { | ||
147 | createAPRPool() ; | ||
148 | } | ||
149 | |||
150 | return mPool ; | ||
151 | } | ||
152 | LLVolatileAPRPool::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 | |||
159 | apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool() | ||
160 | { | ||
161 | mNumTotalRef++ ; | ||
162 | mNumActiveRef++ ; | ||
163 | return getAPRPool() ; | ||
164 | } | ||
165 | |||
166 | void 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 | |||
198 | BOOL 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. | ||
205 | static void* gIsMainThread; | ||
206 | bool is_main_thread() { return gIsMainThread == LLVolatileAPRPool::getLocalAPRFilePool(); } | ||
207 | #endif | ||
208 | |||
209 | // The thread private handle to access the LocalAPRFilePool. | ||
210 | apr_threadkey_t* LLVolatileAPRPool::sLocalAPRFilePoolKey; | ||
211 | |||
212 | // This should be called exactly once, before the first call to createLocalAPRFilePool. | ||
213 | // static | ||
214 | void 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 | ||
229 | void 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 | ||
238 | void LLVolatileAPRPool::destroyLocalAPRFilePool(void* thread_local_data) | ||
239 | { | ||
240 | delete reinterpret_cast<LLVolatileAPRPool*>(thread_local_data); | ||
241 | } | ||
242 | |||
243 | // static | ||
244 | LLVolatileAPRPool* 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 | // |
311 | LLAPRFile::LLAPRFile() | 98 | LLAPRFile::LLAPRFile() |
312 | : mFile(NULL), | 99 | : mFile(NULL), |
313 | mCurrentFilePoolp(NULL) | 100 | mVolatileFilePoolp(NULL), |
101 | mRegularFilePoolp(NULL) | ||
314 | { | 102 | { |
315 | } | 103 | } |
316 | 104 | ||
317 | LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type) | 105 | LLAPRFile::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() | |||
347 | apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, access_t access_type, S32* sizep) | 142 | apr_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. | ||
444 | class LLScopedVolatileAPRFilePool { | ||
445 | private: | ||
446 | LLVolatileAPRPool* mPool; | ||
447 | apr_pool_t* apr_pool; | ||
448 | public: | ||
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 |
455 | S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) | 242 | S32 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) | |||
487 | S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes) | 274 | S32 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 | ||
51 | extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp; | 51 | class AIAPRPool; |
52 | 52 | class AIVolatileAPRPool; | |
53 | /** | ||
54 | * @brief initialize the common apr constructs -- apr itself, the | ||
55 | * global pool, and a mutex. | ||
56 | */ | ||
57 | void LL_COMMON_API ll_init_apr(); | ||
58 | |||
59 | /** | ||
60 | * @brief Cleanup those common apr constructs. | ||
61 | */ | ||
62 | void 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 | // | ||
68 | class LL_COMMON_API LLAPRPool | ||
69 | { | ||
70 | public: | ||
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 | |||
77 | protected: | ||
78 | void releaseAPRPool() ; | ||
79 | void createAPRPool() ; | ||
80 | |||
81 | protected: | ||
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 | // | ||
94 | class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool | ||
95 | { | ||
96 | public: | ||
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 | |||
112 | private: | ||
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 |
206 | private: | 141 | private: |
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 | ||
210 | public: | 146 | public: |
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 | ||
261 | void LL_COMMON_API ll_apr_assert_status(apr_status_t status); | 197 | void LL_COMMON_API ll_apr_assert_status(apr_status_t status); |
262 | 198 | ||
263 | extern "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 | |||
35 | bool LLAvatarName::sOmitResidentAsLastName = false; | ||
36 | |||
37 | // Store these in pre-built std::strings to avoid memory allocations in | ||
38 | // LLSD map lookups | ||
39 | static const std::string USERNAME("username"); | ||
40 | static const std::string DISPLAY_NAME("display_name"); | ||
41 | static const std::string LEGACY_FIRST_NAME("legacy_first_name"); | ||
42 | static const std::string LEGACY_LAST_NAME("legacy_last_name"); | ||
43 | static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default"); | ||
44 | static const std::string DISPLAY_NAME_EXPIRES("display_name_expires"); | ||
45 | static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update"); | ||
46 | |||
47 | LLAvatarName::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 | |||
58 | bool 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 | |||
66 | LLSD 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 | |||
79 | void 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 | |||
92 | std::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 | |||
107 | std::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 | |||
120 | std::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 | |||
33 | class LLSD; | ||
34 | |||
35 | class LL_COMMON_API LLAvatarName | ||
36 | { | ||
37 | public: | ||
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 |
38 | BOOL LLCommon::sAprInitialized = FALSE; | ||
39 | |||
40 | //static | ||
41 | void LLCommon::initClass() | 38 | void 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 | |||
43 | public: | 43 | public: |
44 | static void initClass(); | 44 | static void initClass(); |
45 | static void cleanupClass(); | 45 | static void cleanupClass(); |
46 | private: | ||
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 | ||
876 | apr_thread_mutex_t* gLogMutexp; | ||
877 | apr_thread_mutex_t* gCallStacksLogMutexp; | ||
878 | |||
874 | namespace { | 879 | namespace { |
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 | ||
35 | LLFixedBuffer::LLFixedBuffer(const U32 max_lines) | 35 | LLFixedBuffer::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 | // | ||
46 | class LLScopedVolatileAPRPool | ||
47 | { | ||
48 | private: | ||
49 | AIVolatileAPRPool& mPool; | ||
50 | apr_pool_t* mScopedAPRpool; | ||
51 | public: | ||
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 | ||
90 | LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : | 90 | LLThread::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 | |||
165 | void LLThread::start() | 149 | void 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. | ||
254 | static apr_os_thread_t main_thread_id; | ||
255 | bool is_main_thread() { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); } | ||
256 | #endif | ||
269 | 257 | ||
270 | LLMutex::LLMutex(apr_pool_t *poolp) : | 258 | // The thread private handle to access the AIThreadLocalData instance. |
271 | mAPRMutexp(NULL) | 259 | apr_threadkey_t* AIThreadLocalData::sThreadLocalDataKey; |
260 | |||
261 | //static | ||
262 | void 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 | ||
286 | LLMutex::~LLMutex() | 284 | // This is called once for every thread when the thread is destructed. |
285 | //static | ||
286 | void 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; | 292 | void 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 | ||
299 | bool LLMutex::isLocked() | 303 | //static |
304 | AIThreadLocalData& 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 | |||
317 | bool 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 | ||
311 | LLCondition::LLCondition(apr_pool_t *poolp) : | 329 | LLCondition::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 | ||
319 | LLCondition::~LLCondition() | 334 | LLCondition::~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 | ||
42 | class LLThread; | 43 | class LLThread; |
43 | class LLMutex; | 44 | class LLMutex; |
44 | class LLCondition; | 45 | class LLCondition; |
45 | 46 | ||
47 | class LL_COMMON_API AIThreadLocalData | ||
48 | { | ||
49 | private: | ||
50 | static apr_threadkey_t* sThreadLocalDataKey; | ||
51 | |||
52 | public: | ||
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 | |||
46 | class LL_COMMON_API LLThread | 63 | class LL_COMMON_API LLThread |
47 | { | 64 | { |
48 | public: | 65 | public: |
@@ -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 | ||
87 | private: | 105 | private: |
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 | ||
128 | class LL_COMMON_API LLMutex | 147 | class LL_COMMON_API LLMutexBase |
129 | { | 148 | { |
130 | public: | 149 | public: |
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 | ||
141 | protected: | 157 | protected: |
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 | |||
162 | class LL_COMMON_API LLMutex : public LLMutexBase | ||
163 | { | ||
164 | public: | ||
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 | |||
176 | protected: | ||
177 | AIAPRPool mPool; | ||
145 | }; | 178 | }; |
146 | 179 | ||
180 | #if APR_HAS_THREADS | ||
181 | // No need to use a root pool in this case. | ||
182 | typedef LLMutex LLMutexRootPool; | ||
183 | #else // APR_HAS_THREADS | ||
184 | class LL_COMMON_API LLMutexRootPool : public LLMutexBase | ||
185 | { | ||
186 | public: | ||
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 | |||
202 | protected: | ||
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). |
148 | class LL_COMMON_API LLCondition : public LLMutex | 208 | class LL_COMMON_API LLCondition : public LLMutex |
149 | { | 209 | { |
150 | public: | 210 | public: |
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: | |||
162 | class LL_COMMON_API LLMutexLock | 222 | class LL_COMMON_API LLMutexLock |
163 | { | 223 | { |
164 | public: | 224 | public: |
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 | } |
174 | private: | 234 | private: |
175 | LLMutex* mMutex; | 235 | LLMutexBase* mMutex; |
236 | }; | ||
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 | } | ||
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 @@ | |||
43 | LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) : | 43 | LLWorkerThread::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 | ||
49 | LLWorkerThread::~LLWorkerThread() | 49 | LLWorkerThread::~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 | ||
196 | private: | 196 | private: |
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 | ||