tsd.h revision 261071
1234370Sjasone/******************************************************************************/
2234370Sjasone#ifdef JEMALLOC_H_TYPES
3234370Sjasone
4234370Sjasone/* Maximum number of malloc_tsd users with cleanup functions. */
5234370Sjasone#define	MALLOC_TSD_CLEANUPS_MAX	8
6234370Sjasone
7234543Sjasonetypedef bool (*malloc_tsd_cleanup_t)(void);
8234370Sjasone
9261071Sjasone#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
10261071Sjasone    !defined(_WIN32))
11261071Sjasonetypedef struct tsd_init_block_s tsd_init_block_t;
12261071Sjasonetypedef struct tsd_init_head_s tsd_init_head_t;
13261071Sjasone#endif
14261071Sjasone
15234370Sjasone/*
16234370Sjasone * TLS/TSD-agnostic macro-based implementation of thread-specific data.  There
17234370Sjasone * are four macros that support (at least) three use cases: file-private,
18234370Sjasone * library-private, and library-private inlined.  Following is an example
19234370Sjasone * library-private tsd variable:
20234370Sjasone *
21234370Sjasone * In example.h:
22234370Sjasone *   typedef struct {
23234370Sjasone *           int x;
24234370Sjasone *           int y;
25234370Sjasone *   } example_t;
26234370Sjasone *   #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0})
27234370Sjasone *   malloc_tsd_protos(, example, example_t *)
28234370Sjasone *   malloc_tsd_externs(example, example_t *)
29234370Sjasone * In example.c:
30234370Sjasone *   malloc_tsd_data(, example, example_t *, EX_INITIALIZER)
31234370Sjasone *   malloc_tsd_funcs(, example, example_t *, EX_INITIALIZER,
32234370Sjasone *       example_tsd_cleanup)
33234370Sjasone *
34234370Sjasone * The result is a set of generated functions, e.g.:
35234370Sjasone *
36234370Sjasone *   bool example_tsd_boot(void) {...}
37234370Sjasone *   example_t **example_tsd_get() {...}
38234370Sjasone *   void example_tsd_set(example_t **val) {...}
39234370Sjasone *
40234370Sjasone * Note that all of the functions deal in terms of (a_type *) rather than
41234370Sjasone * (a_type)  so that it is possible to support non-pointer types (unlike
42234370Sjasone * pthreads TSD).  example_tsd_cleanup() is passed an (a_type *) pointer that is
43234370Sjasone * cast to (void *).  This means that the cleanup function needs to cast *and*
44234370Sjasone * dereference the function argument, e.g.:
45234370Sjasone *
46234370Sjasone *   void
47234370Sjasone *   example_tsd_cleanup(void *arg)
48234370Sjasone *   {
49234370Sjasone *           example_t *example = *(example_t **)arg;
50234370Sjasone *
51234370Sjasone *           [...]
52234370Sjasone *           if ([want the cleanup function to be called again]) {
53234370Sjasone *                   example_tsd_set(&example);
54234370Sjasone *           }
55234370Sjasone *   }
56234370Sjasone *
57234370Sjasone * If example_tsd_set() is called within example_tsd_cleanup(), it will be
58234370Sjasone * called again.  This is similar to how pthreads TSD destruction works, except
59234370Sjasone * that pthreads only calls the cleanup function again if the value was set to
60234370Sjasone * non-NULL.
61234370Sjasone */
62234370Sjasone
63234370Sjasone/* malloc_tsd_protos(). */
64234370Sjasone#define	malloc_tsd_protos(a_attr, a_name, a_type)			\
65234370Sjasonea_attr bool								\
66234370Sjasonea_name##_tsd_boot(void);						\
67234370Sjasonea_attr a_type *								\
68234370Sjasonea_name##_tsd_get(void);							\
69234370Sjasonea_attr void								\
70234370Sjasonea_name##_tsd_set(a_type *val);
71234370Sjasone
72234370Sjasone/* malloc_tsd_externs(). */
73234370Sjasone#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
74234370Sjasone#define	malloc_tsd_externs(a_name, a_type)				\
75234370Sjasoneextern __thread a_type	a_name##_tls;					\
76234370Sjasoneextern __thread bool	a_name##_initialized;				\
77234370Sjasoneextern bool		a_name##_booted;
78234370Sjasone#elif (defined(JEMALLOC_TLS))
79234370Sjasone#define	malloc_tsd_externs(a_name, a_type)				\
80234370Sjasoneextern __thread a_type	a_name##_tls;					\
81234370Sjasoneextern pthread_key_t	a_name##_tsd;					\
82234370Sjasoneextern bool		a_name##_booted;
83235238Sjasone#elif (defined(_WIN32))
84261071Sjasone#define	malloc_tsd_externs(a_name, a_type)				\
85235238Sjasoneextern DWORD		a_name##_tsd;					\
86235238Sjasoneextern bool		a_name##_booted;
87234370Sjasone#else
88234370Sjasone#define	malloc_tsd_externs(a_name, a_type)				\
89234370Sjasoneextern pthread_key_t	a_name##_tsd;					\
90261071Sjasoneextern tsd_init_head_t	a_name##_tsd_init_head;				\
91234370Sjasoneextern bool		a_name##_booted;
92234370Sjasone#endif
93234370Sjasone
94234370Sjasone/* malloc_tsd_data(). */
95234370Sjasone#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
96234370Sjasone#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
97234370Sjasonea_attr __thread a_type JEMALLOC_TLS_MODEL				\
98234370Sjasone    a_name##_tls = a_initializer;					\
99234370Sjasonea_attr __thread bool JEMALLOC_TLS_MODEL					\
100234370Sjasone    a_name##_initialized = false;					\
101234370Sjasonea_attr bool		a_name##_booted = false;
102234370Sjasone#elif (defined(JEMALLOC_TLS))
103234370Sjasone#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
104234370Sjasonea_attr __thread a_type JEMALLOC_TLS_MODEL				\
105234370Sjasone    a_name##_tls = a_initializer;					\
106234370Sjasonea_attr pthread_key_t	a_name##_tsd;					\
107234370Sjasonea_attr bool		a_name##_booted = false;
108235238Sjasone#elif (defined(_WIN32))
109235238Sjasone#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
110235238Sjasonea_attr DWORD		a_name##_tsd;					\
111235238Sjasonea_attr bool		a_name##_booted = false;
112234370Sjasone#else
113234370Sjasone#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
114234370Sjasonea_attr pthread_key_t	a_name##_tsd;					\
115261071Sjasonea_attr tsd_init_head_t	a_name##_tsd_init_head = {			\
116261071Sjasone	ql_head_initializer(blocks),					\
117261071Sjasone	MALLOC_MUTEX_INITIALIZER					\
118261071Sjasone};									\
119234370Sjasonea_attr bool		a_name##_booted = false;
120234370Sjasone#endif
121234370Sjasone
122234370Sjasone/* malloc_tsd_funcs(). */
123234370Sjasone#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
124234370Sjasone#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
125234370Sjasone    a_cleanup)								\
126234370Sjasone/* Initialization/cleanup. */						\
127234370Sjasonea_attr bool								\
128234543Sjasonea_name##_tsd_cleanup_wrapper(void)					\
129234370Sjasone{									\
130234370Sjasone									\
131234370Sjasone	if (a_name##_initialized) {					\
132234370Sjasone		a_name##_initialized = false;				\
133234569Sjasone		a_cleanup(&a_name##_tls);				\
134234370Sjasone	}								\
135234370Sjasone	return (a_name##_initialized);					\
136234370Sjasone}									\
137234370Sjasonea_attr bool								\
138234370Sjasonea_name##_tsd_boot(void)							\
139234370Sjasone{									\
140234370Sjasone									\
141234370Sjasone	if (a_cleanup != malloc_tsd_no_cleanup) {			\
142234370Sjasone		malloc_tsd_cleanup_register(				\
143234543Sjasone		    &a_name##_tsd_cleanup_wrapper);			\
144234370Sjasone	}								\
145234370Sjasone	a_name##_booted = true;						\
146234370Sjasone	return (false);							\
147234370Sjasone}									\
148234370Sjasone/* Get/set. */								\
149234370Sjasonea_attr a_type *								\
150234370Sjasonea_name##_tsd_get(void)							\
151234370Sjasone{									\
152234370Sjasone									\
153234370Sjasone	assert(a_name##_booted);					\
154234370Sjasone	return (&a_name##_tls);						\
155234370Sjasone}									\
156234370Sjasonea_attr void								\
157234370Sjasonea_name##_tsd_set(a_type *val)						\
158234370Sjasone{									\
159234370Sjasone									\
160234370Sjasone	assert(a_name##_booted);					\
161234370Sjasone	a_name##_tls = (*val);						\
162234370Sjasone	if (a_cleanup != malloc_tsd_no_cleanup)				\
163234370Sjasone		a_name##_initialized = true;				\
164234370Sjasone}
165234370Sjasone#elif (defined(JEMALLOC_TLS))
166234370Sjasone#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
167234370Sjasone    a_cleanup)								\
168234370Sjasone/* Initialization/cleanup. */						\
169234370Sjasonea_attr bool								\
170234370Sjasonea_name##_tsd_boot(void)							\
171234370Sjasone{									\
172234370Sjasone									\
173234370Sjasone	if (a_cleanup != malloc_tsd_no_cleanup) {			\
174234370Sjasone		if (pthread_key_create(&a_name##_tsd, a_cleanup) != 0)	\
175234370Sjasone			return (true);					\
176234370Sjasone	}								\
177234370Sjasone	a_name##_booted = true;						\
178234370Sjasone	return (false);							\
179234370Sjasone}									\
180234370Sjasone/* Get/set. */								\
181234370Sjasonea_attr a_type *								\
182234370Sjasonea_name##_tsd_get(void)							\
183234370Sjasone{									\
184234370Sjasone									\
185234370Sjasone	assert(a_name##_booted);					\
186234370Sjasone	return (&a_name##_tls);						\
187234370Sjasone}									\
188234370Sjasonea_attr void								\
189234370Sjasonea_name##_tsd_set(a_type *val)						\
190234370Sjasone{									\
191234370Sjasone									\
192234370Sjasone	assert(a_name##_booted);					\
193234370Sjasone	a_name##_tls = (*val);						\
194234370Sjasone	if (a_cleanup != malloc_tsd_no_cleanup) {			\
195234370Sjasone		if (pthread_setspecific(a_name##_tsd,			\
196234370Sjasone		    (void *)(&a_name##_tls))) {				\
197234370Sjasone			malloc_write("<jemalloc>: Error"		\
198234370Sjasone			    " setting TSD for "#a_name"\n");		\
199234370Sjasone			if (opt_abort)					\
200234370Sjasone				abort();				\
201234370Sjasone		}							\
202234370Sjasone	}								\
203234370Sjasone}
204235238Sjasone#elif (defined(_WIN32))
205235238Sjasone#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
206235238Sjasone    a_cleanup)								\
207235238Sjasone/* Data structure. */							\
208235238Sjasonetypedef struct {							\
209235238Sjasone	bool	initialized;						\
210235238Sjasone	a_type	val;							\
211235238Sjasone} a_name##_tsd_wrapper_t;						\
212235238Sjasone/* Initialization/cleanup. */						\
213235238Sjasonea_attr bool								\
214235238Sjasonea_name##_tsd_cleanup_wrapper(void)					\
215235238Sjasone{									\
216235238Sjasone	a_name##_tsd_wrapper_t *wrapper;				\
217235238Sjasone									\
218235238Sjasone	wrapper = (a_name##_tsd_wrapper_t *) TlsGetValue(a_name##_tsd);	\
219235238Sjasone	if (wrapper == NULL)						\
220235238Sjasone		return (false);						\
221235238Sjasone	if (a_cleanup != malloc_tsd_no_cleanup &&			\
222235238Sjasone	    wrapper->initialized) {					\
223235238Sjasone		a_type val = wrapper->val;				\
224235238Sjasone		a_type tsd_static_data = a_initializer;			\
225235238Sjasone		wrapper->initialized = false;				\
226235238Sjasone		wrapper->val = tsd_static_data;				\
227235238Sjasone		a_cleanup(&val);					\
228235238Sjasone		if (wrapper->initialized) {				\
229235238Sjasone			/* Trigger another cleanup round. */		\
230235238Sjasone			return (true);					\
231235238Sjasone		}							\
232235238Sjasone	}								\
233235238Sjasone	malloc_tsd_dalloc(wrapper);					\
234235238Sjasone	return (false);							\
235235238Sjasone}									\
236235238Sjasonea_attr bool								\
237235238Sjasonea_name##_tsd_boot(void)							\
238235238Sjasone{									\
239235238Sjasone									\
240235238Sjasone	a_name##_tsd = TlsAlloc();					\
241235238Sjasone	if (a_name##_tsd == TLS_OUT_OF_INDEXES)				\
242235238Sjasone		return (true);						\
243235238Sjasone	if (a_cleanup != malloc_tsd_no_cleanup) {			\
244235238Sjasone		malloc_tsd_cleanup_register(				\
245235238Sjasone		    &a_name##_tsd_cleanup_wrapper);			\
246235238Sjasone	}								\
247235238Sjasone	a_name##_booted = true;						\
248235238Sjasone	return (false);							\
249235238Sjasone}									\
250235238Sjasone/* Get/set. */								\
251235238Sjasonea_attr a_name##_tsd_wrapper_t *						\
252235238Sjasonea_name##_tsd_get_wrapper(void)						\
253235238Sjasone{									\
254235238Sjasone	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)	\
255235238Sjasone	    TlsGetValue(a_name##_tsd);					\
256235238Sjasone									\
257235238Sjasone	if (wrapper == NULL) {						\
258235238Sjasone		wrapper = (a_name##_tsd_wrapper_t *)			\
259235238Sjasone		    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));	\
260235238Sjasone		if (wrapper == NULL) {					\
261235238Sjasone			malloc_write("<jemalloc>: Error allocating"	\
262235238Sjasone			    " TSD for "#a_name"\n");			\
263235238Sjasone			abort();					\
264235238Sjasone		} else {						\
265235238Sjasone			static a_type tsd_static_data = a_initializer;	\
266235238Sjasone			wrapper->initialized = false;			\
267235238Sjasone			wrapper->val = tsd_static_data;			\
268235238Sjasone		}							\
269235238Sjasone		if (!TlsSetValue(a_name##_tsd, (void *)wrapper)) {	\
270235238Sjasone			malloc_write("<jemalloc>: Error setting"	\
271235238Sjasone			    " TSD for "#a_name"\n");			\
272235238Sjasone			abort();					\
273235238Sjasone		}							\
274235238Sjasone	}								\
275235238Sjasone	return (wrapper);						\
276235238Sjasone}									\
277235238Sjasonea_attr a_type *								\
278235238Sjasonea_name##_tsd_get(void)							\
279235238Sjasone{									\
280235238Sjasone	a_name##_tsd_wrapper_t *wrapper;				\
281235238Sjasone									\
282235238Sjasone	assert(a_name##_booted);					\
283235238Sjasone	wrapper = a_name##_tsd_get_wrapper();				\
284235238Sjasone	return (&wrapper->val);						\
285235238Sjasone}									\
286235238Sjasonea_attr void								\
287235238Sjasonea_name##_tsd_set(a_type *val)						\
288235238Sjasone{									\
289235238Sjasone	a_name##_tsd_wrapper_t *wrapper;				\
290235238Sjasone									\
291235238Sjasone	assert(a_name##_booted);					\
292235238Sjasone	wrapper = a_name##_tsd_get_wrapper();				\
293235238Sjasone	wrapper->val = *(val);						\
294235238Sjasone	if (a_cleanup != malloc_tsd_no_cleanup)				\
295235238Sjasone		wrapper->initialized = true;				\
296235238Sjasone}
297234370Sjasone#else
298234370Sjasone#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
299234370Sjasone    a_cleanup)								\
300234370Sjasone/* Data structure. */							\
301234370Sjasonetypedef struct {							\
302234370Sjasone	bool	initialized;						\
303234370Sjasone	a_type	val;							\
304234370Sjasone} a_name##_tsd_wrapper_t;						\
305234370Sjasone/* Initialization/cleanup. */						\
306234370Sjasonea_attr void								\
307234370Sjasonea_name##_tsd_cleanup_wrapper(void *arg)					\
308234370Sjasone{									\
309234370Sjasone	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\
310234370Sjasone									\
311234370Sjasone	if (a_cleanup != malloc_tsd_no_cleanup &&			\
312234370Sjasone	    wrapper->initialized) {					\
313234370Sjasone		wrapper->initialized = false;				\
314234370Sjasone		a_cleanup(&wrapper->val);				\
315234370Sjasone		if (wrapper->initialized) {				\
316234370Sjasone			/* Trigger another cleanup round. */		\
317234370Sjasone			if (pthread_setspecific(a_name##_tsd,		\
318234370Sjasone			    (void *)wrapper)) {				\
319234370Sjasone				malloc_write("<jemalloc>: Error"	\
320234370Sjasone				    " setting TSD for "#a_name"\n");	\
321234370Sjasone				if (opt_abort)				\
322234370Sjasone					abort();			\
323234370Sjasone			}						\
324234370Sjasone			return;						\
325234370Sjasone		}							\
326234370Sjasone	}								\
327234543Sjasone	malloc_tsd_dalloc(wrapper);					\
328234370Sjasone}									\
329234370Sjasonea_attr bool								\
330234370Sjasonea_name##_tsd_boot(void)							\
331234370Sjasone{									\
332234370Sjasone									\
333234370Sjasone	if (pthread_key_create(&a_name##_tsd,				\
334234370Sjasone	    a_name##_tsd_cleanup_wrapper) != 0)				\
335234370Sjasone		return (true);						\
336234370Sjasone	a_name##_booted = true;						\
337234370Sjasone	return (false);							\
338234370Sjasone}									\
339234370Sjasone/* Get/set. */								\
340234370Sjasonea_attr a_name##_tsd_wrapper_t *						\
341234370Sjasonea_name##_tsd_get_wrapper(void)						\
342234370Sjasone{									\
343234370Sjasone	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)	\
344234370Sjasone	    pthread_getspecific(a_name##_tsd);				\
345234370Sjasone									\
346234370Sjasone	if (wrapper == NULL) {						\
347261071Sjasone		tsd_init_block_t block;					\
348261071Sjasone		wrapper = tsd_init_check_recursion(			\
349261071Sjasone		    &a_name##_tsd_init_head, &block);			\
350261071Sjasone		if (wrapper)						\
351261071Sjasone		    return (wrapper);					\
352234370Sjasone		wrapper = (a_name##_tsd_wrapper_t *)			\
353234370Sjasone		    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));	\
354261071Sjasone		block.data = wrapper;					\
355234370Sjasone		if (wrapper == NULL) {					\
356234370Sjasone			malloc_write("<jemalloc>: Error allocating"	\
357234370Sjasone			    " TSD for "#a_name"\n");			\
358234543Sjasone			abort();					\
359234370Sjasone		} else {						\
360234370Sjasone			static a_type tsd_static_data = a_initializer;	\
361234543Sjasone			wrapper->initialized = false;			\
362234370Sjasone			wrapper->val = tsd_static_data;			\
363234370Sjasone		}							\
364234370Sjasone		if (pthread_setspecific(a_name##_tsd,			\
365234370Sjasone		    (void *)wrapper)) {					\
366234370Sjasone			malloc_write("<jemalloc>: Error setting"	\
367234370Sjasone			    " TSD for "#a_name"\n");			\
368234543Sjasone			abort();					\
369234370Sjasone		}							\
370261071Sjasone		tsd_init_finish(&a_name##_tsd_init_head, &block);	\
371234370Sjasone	}								\
372234370Sjasone	return (wrapper);						\
373234370Sjasone}									\
374234370Sjasonea_attr a_type *								\
375234370Sjasonea_name##_tsd_get(void)							\
376234370Sjasone{									\
377234370Sjasone	a_name##_tsd_wrapper_t *wrapper;				\
378234370Sjasone									\
379234370Sjasone	assert(a_name##_booted);					\
380234370Sjasone	wrapper = a_name##_tsd_get_wrapper();				\
381234370Sjasone	return (&wrapper->val);						\
382234370Sjasone}									\
383234370Sjasonea_attr void								\
384234370Sjasonea_name##_tsd_set(a_type *val)						\
385234370Sjasone{									\
386234370Sjasone	a_name##_tsd_wrapper_t *wrapper;				\
387234370Sjasone									\
388234370Sjasone	assert(a_name##_booted);					\
389234370Sjasone	wrapper = a_name##_tsd_get_wrapper();				\
390234370Sjasone	wrapper->val = *(val);						\
391234370Sjasone	if (a_cleanup != malloc_tsd_no_cleanup)				\
392234370Sjasone		wrapper->initialized = true;				\
393234370Sjasone}
394234370Sjasone#endif
395234370Sjasone
396234370Sjasone#endif /* JEMALLOC_H_TYPES */
397234370Sjasone/******************************************************************************/
398234370Sjasone#ifdef JEMALLOC_H_STRUCTS
399234370Sjasone
400261071Sjasone#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
401261071Sjasone    !defined(_WIN32))
402261071Sjasonestruct tsd_init_block_s {
403261071Sjasone	ql_elm(tsd_init_block_t)	link;
404261071Sjasone	pthread_t			thread;
405261071Sjasone	void				*data;
406261071Sjasone};
407261071Sjasonestruct tsd_init_head_s {
408261071Sjasone	ql_head(tsd_init_block_t)	blocks;
409261071Sjasone	malloc_mutex_t			lock;
410261071Sjasone};
411261071Sjasone#endif
412261071Sjasone
413234370Sjasone#endif /* JEMALLOC_H_STRUCTS */
414234370Sjasone/******************************************************************************/
415234370Sjasone#ifdef JEMALLOC_H_EXTERNS
416234370Sjasone
417234370Sjasonevoid	*malloc_tsd_malloc(size_t size);
418234370Sjasonevoid	malloc_tsd_dalloc(void *wrapper);
419234370Sjasonevoid	malloc_tsd_no_cleanup(void *);
420234543Sjasonevoid	malloc_tsd_cleanup_register(bool (*f)(void));
421234370Sjasonevoid	malloc_tsd_boot(void);
422261071Sjasone#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
423261071Sjasone    !defined(_WIN32))
424261071Sjasonevoid	*tsd_init_check_recursion(tsd_init_head_t *head,
425261071Sjasone    tsd_init_block_t *block);
426261071Sjasonevoid	tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block);
427261071Sjasone#endif
428234370Sjasone
429234370Sjasone#endif /* JEMALLOC_H_EXTERNS */
430234370Sjasone/******************************************************************************/
431234370Sjasone#ifdef JEMALLOC_H_INLINES
432234370Sjasone
433234370Sjasone#endif /* JEMALLOC_H_INLINES */
434234370Sjasone/******************************************************************************/
435