summaryrefslogtreecommitdiff
path: root/indra/llcommon/llaprpool.cpp
blob: 6f21b61b65e23df52654d4e3dd70a43828c4cc0d (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
/**
 * @file llaprpool.cpp
 *
 * $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.
 */

#include "linden_common.h"

#include "llerror.h"
#include "llaprpool.h"
#include "llthread.h"

// Create a subpool from parent.
void LLAPRPool::create(LLAPRPool& 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 = &LLThreadLocalData::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 LLAPRPool::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;	// The use of apr_pool_t is OK here.
									// Temporary store before destroying the pool.
		mPool = NULL;				// Mark that we are BEING destructed.
		apr_pool_cleanup_kill(pool, this, &s_plain_cleanup);
		apr_pool_destroy(pool);
	}
}

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

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

	if (!apr_initialized)
	{
		apr_initialize();
	}

	apr_initialized = true;
}

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

apr_thread_mutex_t* gLogMutexp;
apr_thread_mutex_t* gCallStacksLogMutexp;

LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(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 LLAPRRootPool::LLAPRRootPool(void)
		// it must be done last, so that sCount is already initialized.
		LLThreadLocalData::init();
	}
	apr_atomic_inc32(&sCount);
}

LLAPRRootPool::~LLAPRRootPool()
{
	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 LLAPRRootPool, before terminating APR.
		static_cast<LLAPRRootPool*>(this)->destroy();

		apr_terminate();
	}
}

//static
// 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).
LLAPRRootPool& LLAPRRootPool::get(void)
{
  static LLAPRRootPool global_APRpool(0);
  return global_APRpool;
}

void LLVolatileAPRPool::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)) ;
}