xref: /netbsd-src/external/bsd/jemalloc.old/dist/src/tsd.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
1*8e33eff8Schristos #define JEMALLOC_TSD_C_
2*8e33eff8Schristos #include "jemalloc/internal/jemalloc_preamble.h"
3*8e33eff8Schristos #include "jemalloc/internal/jemalloc_internal_includes.h"
4*8e33eff8Schristos 
5*8e33eff8Schristos #include "jemalloc/internal/assert.h"
6*8e33eff8Schristos #include "jemalloc/internal/mutex.h"
7*8e33eff8Schristos #include "jemalloc/internal/rtree.h"
8*8e33eff8Schristos 
9*8e33eff8Schristos /******************************************************************************/
10*8e33eff8Schristos /* Data. */
11*8e33eff8Schristos 
12*8e33eff8Schristos static unsigned ncleanups;
13*8e33eff8Schristos static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];
14*8e33eff8Schristos 
15*8e33eff8Schristos #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
16*8e33eff8Schristos __thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;
17*8e33eff8Schristos __thread bool JEMALLOC_TLS_MODEL tsd_initialized = false;
18*8e33eff8Schristos bool tsd_booted = false;
19*8e33eff8Schristos #elif (defined(JEMALLOC_TLS))
20*8e33eff8Schristos __thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;
21*8e33eff8Schristos pthread_key_t tsd_tsd;
22*8e33eff8Schristos bool tsd_booted = false;
23*8e33eff8Schristos #elif (defined(_WIN32))
24*8e33eff8Schristos DWORD tsd_tsd;
25*8e33eff8Schristos tsd_wrapper_t tsd_boot_wrapper = {false, TSD_INITIALIZER};
26*8e33eff8Schristos bool tsd_booted = false;
27*8e33eff8Schristos #else
28*8e33eff8Schristos 
29*8e33eff8Schristos /*
30*8e33eff8Schristos  * This contains a mutex, but it's pretty convenient to allow the mutex code to
31*8e33eff8Schristos  * have a dependency on tsd.  So we define the struct here, and only refer to it
32*8e33eff8Schristos  * by pointer in the header.
33*8e33eff8Schristos  */
34*8e33eff8Schristos struct tsd_init_head_s {
35*8e33eff8Schristos 	ql_head(tsd_init_block_t) blocks;
36*8e33eff8Schristos 	malloc_mutex_t lock;
37*8e33eff8Schristos };
38*8e33eff8Schristos 
39*8e33eff8Schristos pthread_key_t tsd_tsd;
40*8e33eff8Schristos tsd_init_head_t	tsd_init_head = {
41*8e33eff8Schristos 	ql_head_initializer(blocks),
42*8e33eff8Schristos #ifndef __lint__
43*8e33eff8Schristos 	// XXX: broken lint
44*8e33eff8Schristos 	MALLOC_MUTEX_INITIALIZER
45*8e33eff8Schristos #endif
46*8e33eff8Schristos };
47*8e33eff8Schristos tsd_wrapper_t tsd_boot_wrapper = {
48*8e33eff8Schristos 	false,
49*8e33eff8Schristos 	TSD_INITIALIZER
50*8e33eff8Schristos };
51*8e33eff8Schristos bool tsd_booted = false;
52*8e33eff8Schristos #endif
53*8e33eff8Schristos 
54*8e33eff8Schristos 
55*8e33eff8Schristos /******************************************************************************/
56*8e33eff8Schristos 
57*8e33eff8Schristos void
58*8e33eff8Schristos tsd_slow_update(tsd_t *tsd) {
59*8e33eff8Schristos 	if (tsd_nominal(tsd)) {
60*8e33eff8Schristos 		if (malloc_slow || !tsd_tcache_enabled_get(tsd) ||
61*8e33eff8Schristos 		    tsd_reentrancy_level_get(tsd) > 0) {
62*8e33eff8Schristos 			tsd->state = tsd_state_nominal_slow;
63*8e33eff8Schristos 		} else {
64*8e33eff8Schristos 			tsd->state = tsd_state_nominal;
65*8e33eff8Schristos 		}
66*8e33eff8Schristos 	}
67*8e33eff8Schristos }
68*8e33eff8Schristos 
69*8e33eff8Schristos static bool
70*8e33eff8Schristos tsd_data_init(tsd_t *tsd) {
71*8e33eff8Schristos 	/*
72*8e33eff8Schristos 	 * We initialize the rtree context first (before the tcache), since the
73*8e33eff8Schristos 	 * tcache initialization depends on it.
74*8e33eff8Schristos 	 */
75*8e33eff8Schristos 	rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));
76*8e33eff8Schristos 
77*8e33eff8Schristos 	/*
78*8e33eff8Schristos 	 * A nondeterministic seed based on the address of tsd reduces
79*8e33eff8Schristos 	 * the likelihood of lockstep non-uniform cache index
80*8e33eff8Schristos 	 * utilization among identical concurrent processes, but at the
81*8e33eff8Schristos 	 * cost of test repeatability.  For debug builds, instead use a
82*8e33eff8Schristos 	 * deterministic seed.
83*8e33eff8Schristos 	 */
84*8e33eff8Schristos 	*tsd_offset_statep_get(tsd) = config_debug ? 0 :
85*8e33eff8Schristos 	    (uint64_t)(uintptr_t)tsd;
86*8e33eff8Schristos 
87*8e33eff8Schristos 	return tsd_tcache_enabled_data_init(tsd);
88*8e33eff8Schristos }
89*8e33eff8Schristos 
90*8e33eff8Schristos static void
91*8e33eff8Schristos assert_tsd_data_cleanup_done(tsd_t *tsd) {
92*8e33eff8Schristos 	assert(!tsd_nominal(tsd));
93*8e33eff8Schristos 	assert(*tsd_arenap_get_unsafe(tsd) == NULL);
94*8e33eff8Schristos 	assert(*tsd_iarenap_get_unsafe(tsd) == NULL);
95*8e33eff8Schristos 	assert(*tsd_arenas_tdata_bypassp_get_unsafe(tsd) == true);
96*8e33eff8Schristos 	assert(*tsd_arenas_tdatap_get_unsafe(tsd) == NULL);
97*8e33eff8Schristos 	assert(*tsd_tcache_enabledp_get_unsafe(tsd) == false);
98*8e33eff8Schristos 	assert(*tsd_prof_tdatap_get_unsafe(tsd) == NULL);
99*8e33eff8Schristos }
100*8e33eff8Schristos 
101*8e33eff8Schristos static bool
102*8e33eff8Schristos tsd_data_init_nocleanup(tsd_t *tsd) {
103*8e33eff8Schristos 	assert(tsd->state == tsd_state_reincarnated ||
104*8e33eff8Schristos 	    tsd->state == tsd_state_minimal_initialized);
105*8e33eff8Schristos 	/*
106*8e33eff8Schristos 	 * During reincarnation, there is no guarantee that the cleanup function
107*8e33eff8Schristos 	 * will be called (deallocation may happen after all tsd destructors).
108*8e33eff8Schristos 	 * We set up tsd in a way that no cleanup is needed.
109*8e33eff8Schristos 	 */
110*8e33eff8Schristos 	rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));
111*8e33eff8Schristos 	*tsd_arenas_tdata_bypassp_get(tsd) = true;
112*8e33eff8Schristos 	*tsd_tcache_enabledp_get_unsafe(tsd) = false;
113*8e33eff8Schristos 	*tsd_reentrancy_levelp_get(tsd) = 1;
114*8e33eff8Schristos 	assert_tsd_data_cleanup_done(tsd);
115*8e33eff8Schristos 
116*8e33eff8Schristos 	return false;
117*8e33eff8Schristos }
118*8e33eff8Schristos 
119*8e33eff8Schristos tsd_t *
120*8e33eff8Schristos tsd_fetch_slow(tsd_t *tsd, bool minimal) {
121*8e33eff8Schristos 	assert(!tsd_fast(tsd));
122*8e33eff8Schristos 
123*8e33eff8Schristos 	if (tsd->state == tsd_state_nominal_slow) {
124*8e33eff8Schristos 		/* On slow path but no work needed. */
125*8e33eff8Schristos 		assert(malloc_slow || !tsd_tcache_enabled_get(tsd) ||
126*8e33eff8Schristos 		    tsd_reentrancy_level_get(tsd) > 0 ||
127*8e33eff8Schristos 		    *tsd_arenas_tdata_bypassp_get(tsd));
128*8e33eff8Schristos 	} else if (tsd->state == tsd_state_uninitialized) {
129*8e33eff8Schristos 		if (!minimal) {
130*8e33eff8Schristos 			tsd->state = tsd_state_nominal;
131*8e33eff8Schristos 			tsd_slow_update(tsd);
132*8e33eff8Schristos 			/* Trigger cleanup handler registration. */
133*8e33eff8Schristos 			tsd_set(tsd);
134*8e33eff8Schristos 			tsd_data_init(tsd);
135*8e33eff8Schristos 		} else {
136*8e33eff8Schristos 			tsd->state = tsd_state_minimal_initialized;
137*8e33eff8Schristos 			tsd_set(tsd);
138*8e33eff8Schristos 			tsd_data_init_nocleanup(tsd);
139*8e33eff8Schristos 		}
140*8e33eff8Schristos 	} else if (tsd->state == tsd_state_minimal_initialized) {
141*8e33eff8Schristos 		if (!minimal) {
142*8e33eff8Schristos 			/* Switch to fully initialized. */
143*8e33eff8Schristos 			tsd->state = tsd_state_nominal;
144*8e33eff8Schristos 			assert(*tsd_reentrancy_levelp_get(tsd) >= 1);
145*8e33eff8Schristos 			(*tsd_reentrancy_levelp_get(tsd))--;
146*8e33eff8Schristos 			tsd_slow_update(tsd);
147*8e33eff8Schristos 			tsd_data_init(tsd);
148*8e33eff8Schristos 		} else {
149*8e33eff8Schristos 			assert_tsd_data_cleanup_done(tsd);
150*8e33eff8Schristos 		}
151*8e33eff8Schristos 	} else if (tsd->state == tsd_state_purgatory) {
152*8e33eff8Schristos 		tsd->state = tsd_state_reincarnated;
153*8e33eff8Schristos 		tsd_set(tsd);
154*8e33eff8Schristos 		tsd_data_init_nocleanup(tsd);
155*8e33eff8Schristos 	} else {
156*8e33eff8Schristos 		assert(tsd->state == tsd_state_reincarnated);
157*8e33eff8Schristos 	}
158*8e33eff8Schristos 
159*8e33eff8Schristos 	return tsd;
160*8e33eff8Schristos }
161*8e33eff8Schristos 
162*8e33eff8Schristos void *
163*8e33eff8Schristos malloc_tsd_malloc(size_t size) {
164*8e33eff8Schristos 	return a0malloc(CACHELINE_CEILING(size));
165*8e33eff8Schristos }
166*8e33eff8Schristos 
167*8e33eff8Schristos void
168*8e33eff8Schristos malloc_tsd_dalloc(void *wrapper) {
169*8e33eff8Schristos 	a0dalloc(wrapper);
170*8e33eff8Schristos }
171*8e33eff8Schristos 
172*8e33eff8Schristos __BEGIN_DECLS
173*8e33eff8Schristos void _malloc_thread_cleanup(void);
174*8e33eff8Schristos __END_DECLS
175*8e33eff8Schristos 
176*8e33eff8Schristos #if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32)
177*8e33eff8Schristos #ifndef _WIN32
178*8e33eff8Schristos JEMALLOC_EXPORT
179*8e33eff8Schristos #endif
180*8e33eff8Schristos void
181*8e33eff8Schristos _malloc_thread_cleanup(void) {
182*8e33eff8Schristos 	bool pending[MALLOC_TSD_CLEANUPS_MAX], again;
183*8e33eff8Schristos 	unsigned i;
184*8e33eff8Schristos 
185*8e33eff8Schristos 	for (i = 0; i < ncleanups; i++) {
186*8e33eff8Schristos 		pending[i] = true;
187*8e33eff8Schristos 	}
188*8e33eff8Schristos 
189*8e33eff8Schristos 	do {
190*8e33eff8Schristos 		again = false;
191*8e33eff8Schristos 		for (i = 0; i < ncleanups; i++) {
192*8e33eff8Schristos 			if (pending[i]) {
193*8e33eff8Schristos 				pending[i] = cleanups[i]();
194*8e33eff8Schristos 				if (pending[i]) {
195*8e33eff8Schristos 					again = true;
196*8e33eff8Schristos 				}
197*8e33eff8Schristos 			}
198*8e33eff8Schristos 		}
199*8e33eff8Schristos 	} while (again);
200*8e33eff8Schristos }
201*8e33eff8Schristos #endif
202*8e33eff8Schristos 
203*8e33eff8Schristos void
204*8e33eff8Schristos malloc_tsd_cleanup_register(bool (*f)(void)) {
205*8e33eff8Schristos 	assert(ncleanups < MALLOC_TSD_CLEANUPS_MAX);
206*8e33eff8Schristos 	cleanups[ncleanups] = f;
207*8e33eff8Schristos 	ncleanups++;
208*8e33eff8Schristos }
209*8e33eff8Schristos 
210*8e33eff8Schristos static void
211*8e33eff8Schristos tsd_do_data_cleanup(tsd_t *tsd) {
212*8e33eff8Schristos 	prof_tdata_cleanup(tsd);
213*8e33eff8Schristos 	iarena_cleanup(tsd);
214*8e33eff8Schristos 	arena_cleanup(tsd);
215*8e33eff8Schristos 	arenas_tdata_cleanup(tsd);
216*8e33eff8Schristos 	tcache_cleanup(tsd);
217*8e33eff8Schristos 	witnesses_cleanup(tsd_witness_tsdp_get_unsafe(tsd));
218*8e33eff8Schristos }
219*8e33eff8Schristos 
220*8e33eff8Schristos void
221*8e33eff8Schristos tsd_cleanup(void *arg) {
222*8e33eff8Schristos 	tsd_t *tsd = (tsd_t *)arg;
223*8e33eff8Schristos 
224*8e33eff8Schristos 	switch (tsd->state) {
225*8e33eff8Schristos 	case tsd_state_uninitialized:
226*8e33eff8Schristos 		/* Do nothing. */
227*8e33eff8Schristos 		break;
228*8e33eff8Schristos 	case tsd_state_minimal_initialized:
229*8e33eff8Schristos 		/* This implies the thread only did free() in its life time. */
230*8e33eff8Schristos 		/* Fall through. */
231*8e33eff8Schristos 	case tsd_state_reincarnated:
232*8e33eff8Schristos 		/*
233*8e33eff8Schristos 		 * Reincarnated means another destructor deallocated memory
234*8e33eff8Schristos 		 * after the destructor was called.  Cleanup isn't required but
235*8e33eff8Schristos 		 * is still called for testing and completeness.
236*8e33eff8Schristos 		 */
237*8e33eff8Schristos 		assert_tsd_data_cleanup_done(tsd);
238*8e33eff8Schristos 		/* Fall through. */
239*8e33eff8Schristos 	case tsd_state_nominal:
240*8e33eff8Schristos 	case tsd_state_nominal_slow:
241*8e33eff8Schristos 		tsd_do_data_cleanup(tsd);
242*8e33eff8Schristos 		tsd->state = tsd_state_purgatory;
243*8e33eff8Schristos 		tsd_set(tsd);
244*8e33eff8Schristos 		break;
245*8e33eff8Schristos 	case tsd_state_purgatory:
246*8e33eff8Schristos 		/*
247*8e33eff8Schristos 		 * The previous time this destructor was called, we set the
248*8e33eff8Schristos 		 * state to tsd_state_purgatory so that other destructors
249*8e33eff8Schristos 		 * wouldn't cause re-creation of the tsd.  This time, do
250*8e33eff8Schristos 		 * nothing, and do not request another callback.
251*8e33eff8Schristos 		 */
252*8e33eff8Schristos 		break;
253*8e33eff8Schristos 	default:
254*8e33eff8Schristos 		not_reached();
255*8e33eff8Schristos 	}
256*8e33eff8Schristos #ifdef JEMALLOC_JET
257*8e33eff8Schristos 	test_callback_t test_callback = *tsd_test_callbackp_get_unsafe(tsd);
258*8e33eff8Schristos 	int *data = tsd_test_datap_get_unsafe(tsd);
259*8e33eff8Schristos 	if (test_callback != NULL) {
260*8e33eff8Schristos 		test_callback(data);
261*8e33eff8Schristos 	}
262*8e33eff8Schristos #endif
263*8e33eff8Schristos }
264*8e33eff8Schristos 
265*8e33eff8Schristos tsd_t *
266*8e33eff8Schristos malloc_tsd_boot0(void) {
267*8e33eff8Schristos 	tsd_t *tsd;
268*8e33eff8Schristos 
269*8e33eff8Schristos 	ncleanups = 0;
270*8e33eff8Schristos 	if (tsd_boot0()) {
271*8e33eff8Schristos 		return NULL;
272*8e33eff8Schristos 	}
273*8e33eff8Schristos 	tsd = tsd_fetch();
274*8e33eff8Schristos 	*tsd_arenas_tdata_bypassp_get(tsd) = true;
275*8e33eff8Schristos 	return tsd;
276*8e33eff8Schristos }
277*8e33eff8Schristos 
278*8e33eff8Schristos void
279*8e33eff8Schristos malloc_tsd_boot1(void) {
280*8e33eff8Schristos 	tsd_boot1();
281*8e33eff8Schristos 	tsd_t *tsd = tsd_fetch();
282*8e33eff8Schristos 	/* malloc_slow has been set properly.  Update tsd_slow. */
283*8e33eff8Schristos 	tsd_slow_update(tsd);
284*8e33eff8Schristos 	*tsd_arenas_tdata_bypassp_get(tsd) = false;
285*8e33eff8Schristos }
286*8e33eff8Schristos 
287*8e33eff8Schristos #ifdef _WIN32
288*8e33eff8Schristos static BOOL WINAPI
289*8e33eff8Schristos _tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
290*8e33eff8Schristos 	switch (fdwReason) {
291*8e33eff8Schristos #ifdef JEMALLOC_LAZY_LOCK
292*8e33eff8Schristos 	case DLL_THREAD_ATTACH:
293*8e33eff8Schristos 		isthreaded = true;
294*8e33eff8Schristos 		break;
295*8e33eff8Schristos #endif
296*8e33eff8Schristos 	case DLL_THREAD_DETACH:
297*8e33eff8Schristos 		_malloc_thread_cleanup();
298*8e33eff8Schristos 		break;
299*8e33eff8Schristos 	default:
300*8e33eff8Schristos 		break;
301*8e33eff8Schristos 	}
302*8e33eff8Schristos 	return true;
303*8e33eff8Schristos }
304*8e33eff8Schristos 
305*8e33eff8Schristos /*
306*8e33eff8Schristos  * We need to be able to say "read" here (in the "pragma section"), but have
307*8e33eff8Schristos  * hooked "read". We won't read for the rest of the file, so we can get away
308*8e33eff8Schristos  * with unhooking.
309*8e33eff8Schristos  */
310*8e33eff8Schristos #ifdef read
311*8e33eff8Schristos #  undef read
312*8e33eff8Schristos #endif
313*8e33eff8Schristos 
314*8e33eff8Schristos #ifdef _MSC_VER
315*8e33eff8Schristos #  ifdef _M_IX86
316*8e33eff8Schristos #    pragma comment(linker, "/INCLUDE:__tls_used")
317*8e33eff8Schristos #    pragma comment(linker, "/INCLUDE:_tls_callback")
318*8e33eff8Schristos #  else
319*8e33eff8Schristos #    pragma comment(linker, "/INCLUDE:_tls_used")
320*8e33eff8Schristos #    pragma comment(linker, "/INCLUDE:tls_callback")
321*8e33eff8Schristos #  endif
322*8e33eff8Schristos #  pragma section(".CRT$XLY",long,read)
323*8e33eff8Schristos #endif
324*8e33eff8Schristos JEMALLOC_SECTION(".CRT$XLY") JEMALLOC_ATTR(used)
325*8e33eff8Schristos BOOL	(WINAPI *const tls_callback)(HINSTANCE hinstDLL,
326*8e33eff8Schristos     DWORD fdwReason, LPVOID lpvReserved) = _tls_callback;
327*8e33eff8Schristos #endif
328*8e33eff8Schristos 
329*8e33eff8Schristos #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
330*8e33eff8Schristos     !defined(_WIN32))
331*8e33eff8Schristos void *
332*8e33eff8Schristos tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block) {
333*8e33eff8Schristos 	pthread_t self = pthread_self();
334*8e33eff8Schristos 	tsd_init_block_t *iter;
335*8e33eff8Schristos 
336*8e33eff8Schristos 	/* Check whether this thread has already inserted into the list. */
337*8e33eff8Schristos 	malloc_mutex_lock(TSDN_NULL, &head->lock);
338*8e33eff8Schristos 	ql_foreach(iter, &head->blocks, link) {
339*8e33eff8Schristos 		if (iter->thread == self) {
340*8e33eff8Schristos 			malloc_mutex_unlock(TSDN_NULL, &head->lock);
341*8e33eff8Schristos 			return iter->data;
342*8e33eff8Schristos 		}
343*8e33eff8Schristos 	}
344*8e33eff8Schristos 	/* Insert block into list. */
345*8e33eff8Schristos 	ql_elm_new(block, link);
346*8e33eff8Schristos 	block->thread = self;
347*8e33eff8Schristos 	ql_tail_insert(&head->blocks, block, link);
348*8e33eff8Schristos 	malloc_mutex_unlock(TSDN_NULL, &head->lock);
349*8e33eff8Schristos 	return NULL;
350*8e33eff8Schristos }
351*8e33eff8Schristos 
352*8e33eff8Schristos void
353*8e33eff8Schristos tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) {
354*8e33eff8Schristos 	malloc_mutex_lock(TSDN_NULL, &head->lock);
355*8e33eff8Schristos 	ql_remove(&head->blocks, block, link);
356*8e33eff8Schristos 	malloc_mutex_unlock(TSDN_NULL, &head->lock);
357*8e33eff8Schristos }
358*8e33eff8Schristos #endif
359