aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llcommon/aiaprpool.cpp
blob: d3748e983052dd74b4b65e3af02de57d9f87f113 (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
/**
 * @file aiaprpool.cpp
 *
 * Copyright (c) 2010, Aleric Inglewood.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution.
 *
 * CHANGELOG
 *   and additional copyright holders.
 *
 *   04/04/2010
 *   - Initial version, written by Aleric Inglewood @ SL
 *
 *   10/11/2010
 *   - Changed filename, class names and license to a more
 *     company-neutral format.
 *   - Added APR_HAS_THREADS #if's to allow creation and destruction
 *     of subpools by threads other than the parent pool owner.
 */

#include "linden_common.h"

#include "llerror.h"
#include "aiaprpool.h"
#include "llthread.h"

// Create a subpool from parent.
void AIAPRPool::create(AIAPRPool& parent)
{
	llassert(!mPool);			// Must be non-initialized.
	mParent = &parent;
	if (!mParent)				// Using the default parameter?
	{
		// By default use the root pool of the current thread.
		mParent = &AIThreadLocalData::tldata().mRootPool;
	}
	llassert(mParent->mPool);	// Parent must be initialized.
#if APR_HAS_THREADS
	// As per the documentation of APR (ie http://apr.apache.org/docs/apr/1.4/apr__pools_8h.html):
	//
	// Note that most operations on pools are not thread-safe: a single pool should only be
	// accessed by a single thread at any given time. The one exception to this rule is creating
	// a subpool of a given pool: one or more threads can safely create subpools at the same
	// time that another thread accesses the parent pool.
	//
	// In other words, it's safe for any thread to create a (sub)pool, independent of who
	// owns the parent pool.
	mOwner = apr_os_thread_current();
#else
	mOwner = mParent->mOwner;
	llassert(apr_os_thread_equal(mOwner, apr_os_thread_current()));
#endif
	apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool);
	llassert_always(apr_pool_create_status == APR_SUCCESS);
	llassert(mPool);			// Initialized.
	apr_pool_cleanup_register(mPool, this, &s_plain_cleanup, &apr_pool_cleanup_null);
}

// Destroy the (sub)pool, if any.
void AIAPRPool::destroy(void)
{
	// Only do anything if we are not already (being) destroyed.
	if (mPool)
	{
#if !APR_HAS_THREADS
		// If we are a root pool, then every thread may destruct us: in that case
		// we have to assume that no other thread will use this pool concurrently,
		// of course. Otherwise, if we are a subpool, only the thread that owns
		// the parent may destruct us, since that is the pool that is still alive,
		// possibly being used by others and being altered here.
		llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current()));
#endif
		apr_pool_t* pool = mPool;
		mPool = NULL;				// Mark that we are BEING destructed.
		apr_pool_cleanup_kill(pool, this, &s_plain_cleanup);
		apr_pool_destroy(pool);
	}
}

bool AIAPRPool::parent_is_being_destructed(void)
{
	return mParent && (!mParent->mPool || mParent->parent_is_being_destructed());
}

AIAPRInitialization::AIAPRInitialization(void)
{
	static bool apr_initialized = false;

	if (!apr_initialized)
	{
		apr_initialize();
	}

	apr_initialized = true;
}

bool AIAPRRootPool::sCountInitialized = false;
apr_uint32_t volatile AIAPRRootPool::sCount;

extern apr_thread_mutex_t* gLogMutexp;
extern apr_thread_mutex_t* gCallStacksLogMutexp;

AIAPRRootPool::AIAPRRootPool(void) : AIAPRInitialization(), AIAPRPool(0)
{
	// sCountInitialized don't need locking because when we get here there is still only a single thread.
	if (!sCountInitialized)
	{
		// Initialize the logging mutex
		apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
		apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);

		apr_status_t status = apr_atomic_init(mPool);
		llassert_always(status == APR_SUCCESS);
		apr_atomic_set32(&sCount, 1);	// Set to 1 to account for the global root pool.
		sCountInitialized = true;

		// Initialize thread-local APR pool support.
		// Because this recursively calls AIAPRRootPool::AIAPRRootPool(void)
		// it must be done last, so that sCount is already initialized.
		AIThreadLocalData::init();
	}
	apr_atomic_inc32(&sCount);
}

AIAPRRootPool::~AIAPRRootPool()
{
	if (!apr_atomic_dec32(&sCount))
	{
		// The last pool was destructed. Cleanup remainder of APR.
		LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;

		if (gLogMutexp)
		{
			// Clean up the logging mutex

			// All other threads NEED to be done before we clean up APR, so this is okay.
			apr_thread_mutex_destroy(gLogMutexp);
			gLogMutexp = NULL;
		}
		if (gCallStacksLogMutexp)
		{
			// Clean up the logging mutex

			// All other threads NEED to be done before we clean up APR, so this is okay.
			apr_thread_mutex_destroy(gCallStacksLogMutexp);
			gCallStacksLogMutexp = NULL;
		}

		// Must destroy ALL, and therefore this last AIAPRRootPool, before terminating APR.
		static_cast<AIAPRRootPool*>(this)->destroy();

		apr_terminate();
	}
}

//static
AIAPRRootPool& AIAPRRootPool::get(void)
{
  static AIAPRRootPool global_APRpool(0);		// This is what used to be gAPRPoolp.
  return global_APRpool;
}

void AIVolatileAPRPool::clearVolatileAPRPool()
{
	llassert_always(mNumActiveRef > 0);
	if (--mNumActiveRef == 0)
	{
		if (isOld())
		{
			destroy();
			mNumTotalRef = 0 ;
		}
		else
		{
			// This does not actually free the memory,
			// it just allows the pool to re-use this memory for the next allocation.
			clear();
		}
	}

	// Paranoia check if the pool is jammed.
	llassert(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
}