summaryrefslogtreecommitdiff
path: root/indra/llcommon/llaprpool.h
blob: bf4102c5849e98f1e78095ee22131b8263d5b744 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/**
 * @file llaprpool.h
 * @brief Implementation of LLAPRPool
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2011, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 *
 * CHANGELOG
 *   and additional copyright holders.
 *
 *   04/04/2010
 *   - Initial version, written by Aleric Inglewood @ SL
 *
 *   10/11/2010
 *   - Added APR_HAS_THREADS #if's to allow creation and destruction
 *     of subpools by threads other than the parent pool owner.
 *
 *   05/02/2011
 *   - Fixed compilation on windows: Suppress compile warning 4996
 *     and include <winsock2.h> before including <ws2tcpip.h>,
 *     by Merov Linden @ SL.
 */

#ifndef LL_LLAPRPOOL_H
#define LL_LLAPRPOOL_H

#ifdef LL_WINDOWS
#pragma warning(push)
#pragma warning(disable:4996)
#include <winsock2.h>
#include <ws2tcpip.h>		// Needed before including apr_portable.h
#pragma warning(pop)
#endif

#include "apr_portable.h"
#include "apr_pools.h"
#include "llerror.h"

extern void ll_init_apr();

/**
 * @brief A wrapper around the APR memory pool API.
 *
 * Usage of this class should be restricted to passing it to libapr-1 function calls that need it.
 *
 */
class LL_COMMON_API LLAPRPool
{
protected:
	//! Pointer to the underlaying pool. NULL if not initialized.
	apr_pool_t* mPool;		// The use of apr_pool_t is OK here.
							// This is the wrapped pointer that it is all about!
	//! Pointer to the parent pool, if any. Only valid when mPool is non-zero.
	LLAPRPool* mParent;
	//! The thread that owns this memory pool. Only valid when mPool is non-zero.
	apr_os_thread_t mOwner;

public:
	/// Construct an uninitialized (destructed) pool.
	LLAPRPool(void) : mPool(NULL) { }

	/// Construct a subpool from an existing pool.
	/// This is not a copy-constructor, this class doesn't have one!
	LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); }

	/// Destruct the memory pool (free all of its subpools and allocated memory).
	~LLAPRPool() { destroy(); }

protected:
	/// Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool.
	LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current())
	{
		apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL);
		llassert_always(apr_pool_create_status == APR_SUCCESS);
		llassert(mPool);
		apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
	}

public:
	/// Create a subpool from parent. May only be called for an uninitialized/destroyed pool.
	/// The default parameter causes the root pool of the current thread to be used.
	void create(LLAPRPool& parent = *static_cast<LLAPRPool*>(NULL));

	/// Destroy the (sub)pool, if any.
	void destroy(void);

	// Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool.
	typedef LLAPRPool* const LLAPRPool::* const bool_type;
	/// Return true if the pool is initialized.
	operator bool_type() const { return mPool ? &LLAPRPool::mParent : 0; }

	/// Painful, but we have to either provide access to this, or wrap
	/// every APR function call that needs an apr pool as argument.
	/// NEVER destroy a pool that is returned by this function!
	apr_pool_t* operator()(void) const		// The use of apr_pool_t is OK here.
	  										// This is the accessor for passing the pool to libapr-1 functions.
	{
		llassert(mPool);
		llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
		return mPool;
	}

	/// Free all memory without destructing the pool.
	void clear(void)
	{
		llassert(mPool);
		llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
		apr_pool_clear(mPool);
	}

// These methods would make this class 'complete' (as wrapper around the libapr
// pool functions), but we don't use memory pools in the viewer (only when
// we are forced to pass one to a libapr call), so don't define them in order
// not to encourage people to use them.
#if 0
	void* palloc(size_t size)
	{
		llassert(mPool);
		llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
		return apr_palloc(mPool, size);
	}
	void* pcalloc(size_t size)
	{
		llassert(mPool);
		llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
		return apr_pcalloc(mPool, size);
	}
#endif

private:
	bool parent_is_being_destructed(void);
	static apr_status_t s_plain_cleanup(void* userdata) { return static_cast<LLAPRPool*>(userdata)->plain_cleanup(); }

	apr_status_t plain_cleanup(void)
	{
		if (mPool && 						// We are not being destructed,
			parent_is_being_destructed())	// but our parent is.
		  // This means the pool is being destructed recursively by libapr
		  // because one of its parents is being destructed.
		{
			mPool = NULL;	// Stop destroy() from destructing the pool again.
		}
		return APR_SUCCESS;
	}
};

class LLAPRInitialization
{
public:
	LLAPRInitialization(void);
};

/**
 * @brief Root memory pool (allocates memory from the operating system).
 *
 * This class should only be used by LLThreadLocalData
 * (and LLMutexRootPool when APR_HAS_THREADS isn't defined).
 */
class LL_COMMON_API LLAPRRootPool : public LLAPRInitialization, public LLAPRPool
{
private:
	/// Construct a root memory pool. Should only be used by LLThreadLocalData and LLMutexRootPool.
	friend class LLThreadLocalData;
#if !APR_HAS_THREADS
	friend class LLMutexRootPool;
#endif
	/// Construct a root memory pool.
	/// Should only be used by LLThreadLocalData.
	LLAPRRootPool(void);
	~LLAPRRootPool();

private:
	// Keep track of how many root pools exist and when the last one is destructed.
	static bool sCountInitialized;
	static apr_uint32_t volatile sCount;

public:
	// Return a global root pool that is independent of LLThreadLocalData.
	// Normally you should not use this. Only use for early initialization
	// (before main) and deinitialization (after main).
	static LLAPRRootPool& get(void);

#if APR_POOL_DEBUG
	void grab_ownership(void)
	{
		// You need a patched libapr to use this.
		// See http://web.archiveorange.com/archive/v/5XO9y2zoxUOMt6Gmi1OI
		apr_pool_owner_set(mPool);
	}
#endif

private:
	// Used for constructing the Special Global Root Pool (returned by LLAPRRootPool::get).
	// It is the same as the default constructor but omits to increment sCount. As a result,
	// we must be sure that at least one other LLAPRRootPool is created before termination
	// of the application (which is the case: we create one LLAPRRootPool per thread).
	LLAPRRootPool(int) : LLAPRInitialization(), LLAPRPool(0) { }
};

/** Volatile memory pool
 *
 * 'Volatile' APR memory pool which normally only clears memory,
 * and does not destroy the pool (the same pool is reused) for
 * greater efficiency. However, as a safe guard the apr pool
 * is destructed every FULL_VOLATILE_APR_POOL uses to allow
 * the system memory to be allocated more efficiently and not
 * get scattered through RAM.
 */
class LL_COMMON_API LLVolatileAPRPool : protected LLAPRPool
{
public:
	LLVolatileAPRPool(void) : mNumActiveRef(0), mNumTotalRef(0) { }

	void clearVolatileAPRPool(void);

	bool isOld(void) const { return mNumTotalRef > FULL_VOLATILE_APR_POOL; }
	bool isUnused() const { return mNumActiveRef == 0; }

private:
	friend class LLScopedVolatileAPRPool;
	friend class LLAPRFile;
	apr_pool_t* getVolatileAPRPool(void)	// The use of apr_pool_t is OK here.
	{
		if (!mPool) create();
		++mNumActiveRef;
		++mNumTotalRef;
		return LLAPRPool::operator()();
	}

private:
	S32 mNumActiveRef;	// Number of active uses of the pool.
	S32 mNumTotalRef;	// Number of total uses of the pool since last creation.

	// Maximum number of references to LLVolatileAPRPool until the pool is recreated.
	static S32 const FULL_VOLATILE_APR_POOL = 1024;
};

#endif // LL_LLAPRPOOL_H