• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************/
2 #ifdef JEMALLOC_H_TYPES
3 
4 /* Maximum number of malloc_tsd users with cleanup functions. */
5 #define	MALLOC_TSD_CLEANUPS_MAX	2
6 
7 typedef bool (*malloc_tsd_cleanup_t)(void);
8 
9 #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
10     !defined(_WIN32))
11 typedef struct tsd_init_block_s tsd_init_block_t;
12 typedef struct tsd_init_head_s tsd_init_head_t;
13 #endif
14 
15 typedef struct tsd_s tsd_t;
16 
17 typedef enum {
18 	tsd_state_uninitialized,
19 	tsd_state_nominal,
20 	tsd_state_purgatory,
21 	tsd_state_reincarnated
22 } tsd_state_t;
23 
24 /*
25  * TLS/TSD-agnostic macro-based implementation of thread-specific data.  There
26  * are five macros that support (at least) three use cases: file-private,
27  * library-private, and library-private inlined.  Following is an example
28  * library-private tsd variable:
29  *
30  * In example.h:
31  *   typedef struct {
32  *           int x;
33  *           int y;
34  *   } example_t;
35  *   #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0})
36  *   malloc_tsd_types(example_, example_t)
37  *   malloc_tsd_protos(, example_, example_t)
38  *   malloc_tsd_externs(example_, example_t)
39  * In example.c:
40  *   malloc_tsd_data(, example_, example_t, EX_INITIALIZER)
41  *   malloc_tsd_funcs(, example_, example_t, EX_INITIALIZER,
42  *       example_tsd_cleanup)
43  *
44  * The result is a set of generated functions, e.g.:
45  *
46  *   bool example_tsd_boot(void) {...}
47  *   example_t *example_tsd_get() {...}
48  *   void example_tsd_set(example_t *val) {...}
49  *
50  * Note that all of the functions deal in terms of (a_type *) rather than
51  * (a_type) so that it is possible to support non-pointer types (unlike
52  * pthreads TSD).  example_tsd_cleanup() is passed an (a_type *) pointer that is
53  * cast to (void *).  This means that the cleanup function needs to cast the
54  * function argument to (a_type *), then dereference the resulting pointer to
55  * access fields, e.g.
56  *
57  *   void
58  *   example_tsd_cleanup(void *arg)
59  *   {
60  *           example_t *example = (example_t *)arg;
61  *
62  *           example->x = 42;
63  *           [...]
64  *           if ([want the cleanup function to be called again])
65  *                   example_tsd_set(example);
66  *   }
67  *
68  * If example_tsd_set() is called within example_tsd_cleanup(), it will be
69  * called again.  This is similar to how pthreads TSD destruction works, except
70  * that pthreads only calls the cleanup function again if the value was set to
71  * non-NULL.
72  */
73 
74 /* malloc_tsd_types(). */
75 #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
76 #define	malloc_tsd_types(a_name, a_type)
77 #elif (defined(JEMALLOC_TLS))
78 #define	malloc_tsd_types(a_name, a_type)
79 #elif (defined(_WIN32))
80 #define	malloc_tsd_types(a_name, a_type)				\
81 typedef struct {							\
82 	bool	initialized;						\
83 	a_type	val;							\
84 } a_name##tsd_wrapper_t;
85 #else
86 #define	malloc_tsd_types(a_name, a_type)				\
87 typedef struct {							\
88 	bool	initialized;						\
89 	a_type	val;							\
90 } a_name##tsd_wrapper_t;
91 #endif
92 
93 /* malloc_tsd_protos(). */
94 #define	malloc_tsd_protos(a_attr, a_name, a_type)			\
95 a_attr bool								\
96 a_name##tsd_boot0(void);						\
97 a_attr void								\
98 a_name##tsd_boot1(void);						\
99 a_attr bool								\
100 a_name##tsd_boot(void);							\
101 a_attr a_type *								\
102 a_name##tsd_get(void);							\
103 a_attr void								\
104 a_name##tsd_set(a_type *val);
105 
106 /* malloc_tsd_externs(). */
107 #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
108 #define	malloc_tsd_externs(a_name, a_type)				\
109 extern __thread a_type	a_name##tsd_tls;				\
110 extern __thread bool	a_name##tsd_initialized;			\
111 extern bool		a_name##tsd_booted;
112 #elif (defined(JEMALLOC_TLS))
113 #define	malloc_tsd_externs(a_name, a_type)				\
114 extern __thread a_type	a_name##tsd_tls;				\
115 extern pthread_key_t	a_name##tsd_tsd;				\
116 extern bool		a_name##tsd_booted;
117 #elif (defined(_WIN32))
118 #define	malloc_tsd_externs(a_name, a_type)				\
119 extern DWORD		a_name##tsd_tsd;				\
120 extern a_name##tsd_wrapper_t	a_name##tsd_boot_wrapper;		\
121 extern bool		a_name##tsd_booted;
122 #else
123 #define	malloc_tsd_externs(a_name, a_type)				\
124 extern pthread_key_t	a_name##tsd_tsd;				\
125 extern tsd_init_head_t	a_name##tsd_init_head;				\
126 extern a_name##tsd_wrapper_t	a_name##tsd_boot_wrapper;		\
127 extern bool		a_name##tsd_booted;
128 #endif
129 
130 /* malloc_tsd_data(). */
131 #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
132 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
133 a_attr __thread a_type JEMALLOC_TLS_MODEL				\
134     a_name##tsd_tls = a_initializer;					\
135 a_attr __thread bool JEMALLOC_TLS_MODEL					\
136     a_name##tsd_initialized = false;					\
137 a_attr bool		a_name##tsd_booted = false;
138 #elif (defined(JEMALLOC_TLS))
139 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
140 a_attr __thread a_type JEMALLOC_TLS_MODEL				\
141     a_name##tsd_tls = a_initializer;					\
142 a_attr pthread_key_t	a_name##tsd_tsd;				\
143 a_attr bool		a_name##tsd_booted = false;
144 #elif (defined(_WIN32))
145 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
146 a_attr DWORD		a_name##tsd_tsd;				\
147 a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = {		\
148 	false,								\
149 	a_initializer							\
150 };									\
151 a_attr bool		a_name##tsd_booted = false;
152 #else
153 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
154 a_attr pthread_key_t	a_name##tsd_tsd;				\
155 a_attr tsd_init_head_t	a_name##tsd_init_head = {			\
156 	ql_head_initializer(blocks),					\
157 	MALLOC_MUTEX_INITIALIZER					\
158 };									\
159 a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = {		\
160 	false,								\
161 	a_initializer							\
162 };									\
163 a_attr bool		a_name##tsd_booted = false;
164 #endif
165 
166 /* malloc_tsd_funcs(). */
167 #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
168 #define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
169     a_cleanup)								\
170 /* Initialization/cleanup. */						\
171 a_attr bool								\
172 a_name##tsd_cleanup_wrapper(void)					\
173 {									\
174 									\
175 	if (a_name##tsd_initialized) {					\
176 		a_name##tsd_initialized = false;			\
177 		a_cleanup(&a_name##tsd_tls);				\
178 	}								\
179 	return (a_name##tsd_initialized);				\
180 }									\
181 a_attr bool								\
182 a_name##tsd_boot0(void)							\
183 {									\
184 									\
185 	if (a_cleanup != malloc_tsd_no_cleanup) {			\
186 		malloc_tsd_cleanup_register(				\
187 		    &a_name##tsd_cleanup_wrapper);			\
188 	}								\
189 	a_name##tsd_booted = true;					\
190 	return (false);							\
191 }									\
192 a_attr void								\
193 a_name##tsd_boot1(void)							\
194 {									\
195 									\
196 	/* Do nothing. */						\
197 }									\
198 a_attr bool								\
199 a_name##tsd_boot(void)							\
200 {									\
201 									\
202 	return (a_name##tsd_boot0());					\
203 }									\
204 /* Get/set. */								\
205 a_attr a_type *								\
206 a_name##tsd_get(void)							\
207 {									\
208 									\
209 	assert(a_name##tsd_booted);					\
210 	return (&a_name##tsd_tls);					\
211 }									\
212 a_attr void								\
213 a_name##tsd_set(a_type *val)						\
214 {									\
215 									\
216 	assert(a_name##tsd_booted);					\
217 	a_name##tsd_tls = (*val);					\
218 	if (a_cleanup != malloc_tsd_no_cleanup)				\
219 		a_name##tsd_initialized = true;				\
220 }
221 #elif (defined(JEMALLOC_TLS))
222 #define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
223     a_cleanup)								\
224 /* Initialization/cleanup. */						\
225 a_attr bool								\
226 a_name##tsd_boot0(void)							\
227 {									\
228 									\
229 	if (a_cleanup != malloc_tsd_no_cleanup) {			\
230 		if (pthread_key_create(&a_name##tsd_tsd, a_cleanup) !=	\
231 		    0)							\
232 			return (true);					\
233 	}								\
234 	a_name##tsd_booted = true;					\
235 	return (false);							\
236 }									\
237 a_attr void								\
238 a_name##tsd_boot1(void)							\
239 {									\
240 									\
241 	/* Do nothing. */						\
242 }									\
243 a_attr bool								\
244 a_name##tsd_boot(void)							\
245 {									\
246 									\
247 	return (a_name##tsd_boot0());					\
248 }									\
249 /* Get/set. */								\
250 a_attr a_type *								\
251 a_name##tsd_get(void)							\
252 {									\
253 									\
254 	assert(a_name##tsd_booted);					\
255 	return (&a_name##tsd_tls);					\
256 }									\
257 a_attr void								\
258 a_name##tsd_set(a_type *val)						\
259 {									\
260 									\
261 	assert(a_name##tsd_booted);					\
262 	a_name##tsd_tls = (*val);					\
263 	if (a_cleanup != malloc_tsd_no_cleanup) {			\
264 		if (pthread_setspecific(a_name##tsd_tsd,		\
265 		    (void *)(&a_name##tsd_tls))) {			\
266 			malloc_write("<jemalloc>: Error"		\
267 			    " setting TSD for "#a_name"\n");		\
268 			if (opt_abort)					\
269 				abort();				\
270 		}							\
271 	}								\
272 }
273 #elif (defined(_WIN32))
274 #define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
275     a_cleanup)								\
276 /* Initialization/cleanup. */						\
277 a_attr bool								\
278 a_name##tsd_cleanup_wrapper(void)					\
279 {									\
280 	DWORD error = GetLastError();					\
281 	a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)	\
282 	    TlsGetValue(a_name##tsd_tsd);				\
283 	SetLastError(error);						\
284 									\
285 	if (wrapper == NULL)						\
286 		return (false);						\
287 	if (a_cleanup != malloc_tsd_no_cleanup &&			\
288 	    wrapper->initialized) {					\
289 		wrapper->initialized = false;				\
290 		a_cleanup(&wrapper->val);				\
291 		if (wrapper->initialized) {				\
292 			/* Trigger another cleanup round. */		\
293 			return (true);					\
294 		}							\
295 	}								\
296 	malloc_tsd_dalloc(wrapper);					\
297 	return (false);							\
298 }									\
299 a_attr void								\
300 a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper)			\
301 {									\
302 									\
303 	if (!TlsSetValue(a_name##tsd_tsd, (void *)wrapper)) {		\
304 		malloc_write("<jemalloc>: Error setting"		\
305 		    " TSD for "#a_name"\n");				\
306 		abort();						\
307 	}								\
308 }									\
309 a_attr a_name##tsd_wrapper_t *						\
310 a_name##tsd_wrapper_get(void)						\
311 {									\
312 	DWORD error = GetLastError();					\
313 	a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)	\
314 	    TlsGetValue(a_name##tsd_tsd);				\
315 	SetLastError(error);						\
316 									\
317 	if (unlikely(wrapper == NULL)) {				\
318 		wrapper = (a_name##tsd_wrapper_t *)			\
319 		    malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t));	\
320 		if (wrapper == NULL) {					\
321 			malloc_write("<jemalloc>: Error allocating"	\
322 			    " TSD for "#a_name"\n");			\
323 			abort();					\
324 		} else {						\
325 			wrapper->initialized = false;			\
326 			wrapper->val = a_initializer;			\
327 		}							\
328 		a_name##tsd_wrapper_set(wrapper);			\
329 	}								\
330 	return (wrapper);						\
331 }									\
332 a_attr bool								\
333 a_name##tsd_boot0(void)							\
334 {									\
335 									\
336 	a_name##tsd_tsd = TlsAlloc();					\
337 	if (a_name##tsd_tsd == TLS_OUT_OF_INDEXES)			\
338 		return (true);						\
339 	if (a_cleanup != malloc_tsd_no_cleanup) {			\
340 		malloc_tsd_cleanup_register(				\
341 		    &a_name##tsd_cleanup_wrapper);			\
342 	}								\
343 	a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper);		\
344 	a_name##tsd_booted = true;					\
345 	return (false);							\
346 }									\
347 a_attr void								\
348 a_name##tsd_boot1(void)							\
349 {									\
350 	a_name##tsd_wrapper_t *wrapper;					\
351 	wrapper = (a_name##tsd_wrapper_t *)				\
352 	    malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t));		\
353 	if (wrapper == NULL) {						\
354 		malloc_write("<jemalloc>: Error allocating"		\
355 		    " TSD for "#a_name"\n");				\
356 		abort();						\
357 	}								\
358 	memcpy(wrapper, &a_name##tsd_boot_wrapper,			\
359 	    sizeof(a_name##tsd_wrapper_t));				\
360 	a_name##tsd_wrapper_set(wrapper);				\
361 }									\
362 a_attr bool								\
363 a_name##tsd_boot(void)							\
364 {									\
365 									\
366 	if (a_name##tsd_boot0())					\
367 		return (true);						\
368 	a_name##tsd_boot1();						\
369 	return (false);							\
370 }									\
371 /* Get/set. */								\
372 a_attr a_type *								\
373 a_name##tsd_get(void)							\
374 {									\
375 	a_name##tsd_wrapper_t *wrapper;					\
376 									\
377 	assert(a_name##tsd_booted);					\
378 	wrapper = a_name##tsd_wrapper_get();				\
379 	return (&wrapper->val);						\
380 }									\
381 a_attr void								\
382 a_name##tsd_set(a_type *val)						\
383 {									\
384 	a_name##tsd_wrapper_t *wrapper;					\
385 									\
386 	assert(a_name##tsd_booted);					\
387 	wrapper = a_name##tsd_wrapper_get();				\
388 	wrapper->val = *(val);						\
389 	if (a_cleanup != malloc_tsd_no_cleanup)				\
390 		wrapper->initialized = true;				\
391 }
392 #else
393 #define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
394     a_cleanup)								\
395 /* Initialization/cleanup. */						\
396 a_attr void								\
397 a_name##tsd_cleanup_wrapper(void *arg)					\
398 {									\
399 	a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)arg;	\
400 									\
401 	if (a_cleanup != malloc_tsd_no_cleanup &&			\
402 	    wrapper->initialized) {					\
403 		wrapper->initialized = false;				\
404 		a_cleanup(&wrapper->val);				\
405 		if (wrapper->initialized) {				\
406 			/* Trigger another cleanup round. */		\
407 			if (pthread_setspecific(a_name##tsd_tsd,	\
408 			    (void *)wrapper)) {				\
409 				malloc_write("<jemalloc>: Error"	\
410 				    " setting TSD for "#a_name"\n");	\
411 				if (opt_abort)				\
412 					abort();			\
413 			}						\
414 			return;						\
415 		}							\
416 	}								\
417 	malloc_tsd_dalloc(wrapper);					\
418 }									\
419 a_attr void								\
420 a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper)			\
421 {									\
422 									\
423 	if (pthread_setspecific(a_name##tsd_tsd,			\
424 	    (void *)wrapper)) {						\
425 		malloc_write("<jemalloc>: Error setting"		\
426 		    " TSD for "#a_name"\n");				\
427 		abort();						\
428 	}								\
429 }									\
430 a_attr a_name##tsd_wrapper_t *						\
431 a_name##tsd_wrapper_get(void)						\
432 {									\
433 	a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)	\
434 	    pthread_getspecific(a_name##tsd_tsd);			\
435 									\
436 	if (unlikely(wrapper == NULL)) {				\
437 		tsd_init_block_t block;					\
438 		wrapper = tsd_init_check_recursion(			\
439 		    &a_name##tsd_init_head, &block);			\
440 		if (wrapper)						\
441 		    return (wrapper);					\
442 		wrapper = (a_name##tsd_wrapper_t *)			\
443 		    malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t));	\
444 		block.data = wrapper;					\
445 		if (wrapper == NULL) {					\
446 			malloc_write("<jemalloc>: Error allocating"	\
447 			    " TSD for "#a_name"\n");			\
448 			abort();					\
449 		} else {						\
450 			wrapper->initialized = false;			\
451 			wrapper->val = a_initializer;			\
452 		}							\
453 		a_name##tsd_wrapper_set(wrapper);			\
454 		tsd_init_finish(&a_name##tsd_init_head, &block);	\
455 	}								\
456 	return (wrapper);						\
457 }									\
458 a_attr bool								\
459 a_name##tsd_boot0(void)							\
460 {									\
461 									\
462 	if (pthread_key_create(&a_name##tsd_tsd,			\
463 	    a_name##tsd_cleanup_wrapper) != 0)				\
464 		return (true);						\
465 	a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper);		\
466 	a_name##tsd_booted = true;					\
467 	return (false);							\
468 }									\
469 a_attr void								\
470 a_name##tsd_boot1(void)							\
471 {									\
472 	a_name##tsd_wrapper_t *wrapper;					\
473 	wrapper = (a_name##tsd_wrapper_t *)				\
474 	    malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t));		\
475 	if (wrapper == NULL) {						\
476 		malloc_write("<jemalloc>: Error allocating"		\
477 		    " TSD for "#a_name"\n");				\
478 		abort();						\
479 	}								\
480 	memcpy(wrapper, &a_name##tsd_boot_wrapper,			\
481 	    sizeof(a_name##tsd_wrapper_t));				\
482 	a_name##tsd_wrapper_set(wrapper);				\
483 }									\
484 a_attr bool								\
485 a_name##tsd_boot(void)							\
486 {									\
487 									\
488 	if (a_name##tsd_boot0())					\
489 		return (true);						\
490 	a_name##tsd_boot1();						\
491 	return (false);							\
492 }									\
493 /* Get/set. */								\
494 a_attr a_type *								\
495 a_name##tsd_get(void)							\
496 {									\
497 	a_name##tsd_wrapper_t *wrapper;					\
498 									\
499 	assert(a_name##tsd_booted);					\
500 	wrapper = a_name##tsd_wrapper_get();				\
501 	return (&wrapper->val);						\
502 }									\
503 a_attr void								\
504 a_name##tsd_set(a_type *val)						\
505 {									\
506 	a_name##tsd_wrapper_t *wrapper;					\
507 									\
508 	assert(a_name##tsd_booted);					\
509 	wrapper = a_name##tsd_wrapper_get();				\
510 	wrapper->val = *(val);						\
511 	if (a_cleanup != malloc_tsd_no_cleanup)				\
512 		wrapper->initialized = true;				\
513 }
514 #endif
515 
516 #endif /* JEMALLOC_H_TYPES */
517 /******************************************************************************/
518 #ifdef JEMALLOC_H_STRUCTS
519 
520 #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
521     !defined(_WIN32))
522 struct tsd_init_block_s {
523 	ql_elm(tsd_init_block_t)	link;
524 	pthread_t			thread;
525 	void				*data;
526 };
527 struct tsd_init_head_s {
528 	ql_head(tsd_init_block_t)	blocks;
529 	malloc_mutex_t			lock;
530 };
531 #endif
532 
533 #define	MALLOC_TSD							\
534 /*  O(name,			type) */				\
535     O(tcache,			tcache_t *)				\
536     O(thread_allocated,		uint64_t)				\
537     O(thread_deallocated,	uint64_t)				\
538     O(prof_tdata,		prof_tdata_t *)				\
539     O(arena,			arena_t *)				\
540     O(arenas_tdata,		arena_tdata_t *)			\
541     O(narenas_tdata,		unsigned)				\
542     O(arenas_tdata_bypass,	bool)					\
543     O(tcache_enabled,		tcache_enabled_t)			\
544     O(quarantine,		quarantine_t *)				\
545 
546 #define	TSD_INITIALIZER {						\
547     tsd_state_uninitialized,						\
548     NULL,								\
549     0,									\
550     0,									\
551     NULL,								\
552     NULL,								\
553     NULL,								\
554     0,									\
555     false,								\
556     tcache_enabled_default,						\
557     NULL								\
558 }
559 
560 struct tsd_s {
561 	tsd_state_t	state;
562 #define	O(n, t)								\
563 	t		n;
564 MALLOC_TSD
565 #undef O
566 };
567 
568 static const tsd_t tsd_initializer = TSD_INITIALIZER;
569 
570 malloc_tsd_types(, tsd_t)
571 
572 #endif /* JEMALLOC_H_STRUCTS */
573 /******************************************************************************/
574 #ifdef JEMALLOC_H_EXTERNS
575 
576 void	*malloc_tsd_malloc(size_t size);
577 void	malloc_tsd_dalloc(void *wrapper);
578 void	malloc_tsd_no_cleanup(void *arg);
579 void	malloc_tsd_cleanup_register(bool (*f)(void));
580 bool	malloc_tsd_boot0(void);
581 void	malloc_tsd_boot1(void);
582 #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
583     !defined(_WIN32))
584 void	*tsd_init_check_recursion(tsd_init_head_t *head,
585     tsd_init_block_t *block);
586 void	tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block);
587 #endif
588 void	tsd_cleanup(void *arg);
589 
590 #endif /* JEMALLOC_H_EXTERNS */
591 /******************************************************************************/
592 #ifdef JEMALLOC_H_INLINES
593 
594 #ifndef JEMALLOC_ENABLE_INLINE
595 malloc_tsd_protos(JEMALLOC_ATTR(unused), , tsd_t)
596 
597 tsd_t	*tsd_fetch(void);
598 bool	tsd_nominal(tsd_t *tsd);
599 #define	O(n, t)								\
600 t	*tsd_##n##p_get(tsd_t *tsd);					\
601 t	tsd_##n##_get(tsd_t *tsd);					\
602 void	tsd_##n##_set(tsd_t *tsd, t n);
603 MALLOC_TSD
604 #undef O
605 #endif
606 
607 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TSD_C_))
608 malloc_tsd_externs(, tsd_t)
609 malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, , tsd_t, tsd_initializer, tsd_cleanup)
610 
611 JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_fetch(void)612 tsd_fetch(void)
613 {
614 	tsd_t *tsd = tsd_get();
615 
616 	if (unlikely(tsd->state != tsd_state_nominal)) {
617 		if (tsd->state == tsd_state_uninitialized) {
618 			tsd->state = tsd_state_nominal;
619 			/* Trigger cleanup handler registration. */
620 			tsd_set(tsd);
621 		} else if (tsd->state == tsd_state_purgatory) {
622 			tsd->state = tsd_state_reincarnated;
623 			tsd_set(tsd);
624 		} else
625 			assert(tsd->state == tsd_state_reincarnated);
626 	}
627 
628 	return (tsd);
629 }
630 
631 JEMALLOC_INLINE bool
tsd_nominal(tsd_t * tsd)632 tsd_nominal(tsd_t *tsd)
633 {
634 
635 	return (tsd->state == tsd_state_nominal);
636 }
637 
638 #define	O(n, t)								\
639 JEMALLOC_ALWAYS_INLINE t *						\
640 tsd_##n##p_get(tsd_t *tsd)						\
641 {									\
642 									\
643 	return (&tsd->n);						\
644 }									\
645 									\
646 JEMALLOC_ALWAYS_INLINE t						\
647 tsd_##n##_get(tsd_t *tsd)						\
648 {									\
649 									\
650 	return (*tsd_##n##p_get(tsd));					\
651 }									\
652 									\
653 JEMALLOC_ALWAYS_INLINE void						\
654 tsd_##n##_set(tsd_t *tsd, t n)						\
655 {									\
656 									\
657 	assert(tsd->state == tsd_state_nominal);			\
658 	tsd->n = n;							\
659 }
660 MALLOC_TSD
661 #undef O
662 #endif
663 
664 #endif /* JEMALLOC_H_INLINES */
665 /******************************************************************************/
666