tsd.h revision a4f124f59fa5f702231432a7e5fa45140ba81e2a
1/******************************************************************************/
2#ifdef JEMALLOC_H_TYPES
3
4/* Maximum number of malloc_tsd users with cleanup functions. */
5#define	MALLOC_TSD_CLEANUPS_MAX	8
6
7typedef bool (*malloc_tsd_cleanup_t)(void);
8
9#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
10    !defined(_WIN32))
11typedef struct tsd_init_block_s tsd_init_block_t;
12typedef struct tsd_init_head_s tsd_init_head_t;
13#endif
14
15/*
16 * TLS/TSD-agnostic macro-based implementation of thread-specific data.  There
17 * are four macros that support (at least) three use cases: file-private,
18 * library-private, and library-private inlined.  Following is an example
19 * library-private tsd variable:
20 *
21 * In example.h:
22 *   typedef struct {
23 *           int x;
24 *           int y;
25 *   } example_t;
26 *   #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0})
27 *   malloc_tsd_protos(, example, example_t *)
28 *   malloc_tsd_externs(example, example_t *)
29 * In example.c:
30 *   malloc_tsd_data(, example, example_t *, EX_INITIALIZER)
31 *   malloc_tsd_funcs(, example, example_t *, EX_INITIALIZER,
32 *       example_tsd_cleanup)
33 *
34 * The result is a set of generated functions, e.g.:
35 *
36 *   bool example_tsd_boot(void) {...}
37 *   example_t **example_tsd_get() {...}
38 *   void example_tsd_set(example_t **val) {...}
39 *
40 * Note that all of the functions deal in terms of (a_type *) rather than
41 * (a_type)  so that it is possible to support non-pointer types (unlike
42 * pthreads TSD).  example_tsd_cleanup() is passed an (a_type *) pointer that is
43 * cast to (void *).  This means that the cleanup function needs to cast *and*
44 * dereference the function argument, e.g.:
45 *
46 *   void
47 *   example_tsd_cleanup(void *arg)
48 *   {
49 *           example_t *example = *(example_t **)arg;
50 *
51 *           [...]
52 *           if ([want the cleanup function to be called again]) {
53 *                   example_tsd_set(&example);
54 *           }
55 *   }
56 *
57 * If example_tsd_set() is called within example_tsd_cleanup(), it will be
58 * called again.  This is similar to how pthreads TSD destruction works, except
59 * that pthreads only calls the cleanup function again if the value was set to
60 * non-NULL.
61 */
62
63/* malloc_tsd_protos(). */
64#define	malloc_tsd_protos(a_attr, a_name, a_type)			\
65a_attr bool								\
66a_name##_tsd_boot(void);						\
67a_attr a_type *								\
68a_name##_tsd_get(void);							\
69a_attr void								\
70a_name##_tsd_set(a_type *val);
71
72/* malloc_tsd_externs(). */
73#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
74#define	malloc_tsd_externs(a_name, a_type)				\
75extern __thread a_type	a_name##_tls;					\
76extern __thread bool	a_name##_initialized;				\
77extern bool		a_name##_booted;
78#elif (defined(JEMALLOC_TLS))
79#define	malloc_tsd_externs(a_name, a_type)				\
80extern __thread a_type	a_name##_tls;					\
81extern pthread_key_t	a_name##_tsd;					\
82extern bool		a_name##_booted;
83#elif (defined(_WIN32))
84#define	malloc_tsd_externs(a_name, a_type)				\
85extern DWORD		a_name##_tsd;					\
86extern bool		a_name##_booted;
87#else
88#define	malloc_tsd_externs(a_name, a_type)				\
89extern pthread_key_t	a_name##_tsd;					\
90extern tsd_init_head_t	a_name##_tsd_init_head;				\
91extern bool		a_name##_booted;
92#endif
93
94/* malloc_tsd_data(). */
95#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
96#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
97a_attr __thread a_type JEMALLOC_TLS_MODEL				\
98    a_name##_tls = a_initializer;					\
99a_attr __thread bool JEMALLOC_TLS_MODEL					\
100    a_name##_initialized = false;					\
101a_attr bool		a_name##_booted = false;
102#elif (defined(JEMALLOC_TLS))
103#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
104a_attr __thread a_type JEMALLOC_TLS_MODEL				\
105    a_name##_tls = a_initializer;					\
106a_attr pthread_key_t	a_name##_tsd;					\
107a_attr bool		a_name##_booted = false;
108#elif (defined(_WIN32))
109#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
110a_attr DWORD		a_name##_tsd;					\
111a_attr bool		a_name##_booted = false;
112#else
113#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
114a_attr pthread_key_t	a_name##_tsd;					\
115a_attr tsd_init_head_t	a_name##_tsd_init_head = {			\
116	ql_head_initializer(blocks),					\
117	MALLOC_MUTEX_INITIALIZER					\
118};									\
119a_attr bool		a_name##_booted = false;
120#endif
121
122/* malloc_tsd_funcs(). */
123#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
124#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
125    a_cleanup)								\
126/* Initialization/cleanup. */						\
127a_attr bool								\
128a_name##_tsd_cleanup_wrapper(void)					\
129{									\
130									\
131	if (a_name##_initialized) {					\
132		a_name##_initialized = false;				\
133		a_cleanup(&a_name##_tls);				\
134	}								\
135	return (a_name##_initialized);					\
136}									\
137a_attr bool								\
138a_name##_tsd_boot(void)							\
139{									\
140									\
141	if (a_cleanup != malloc_tsd_no_cleanup) {			\
142		malloc_tsd_cleanup_register(				\
143		    &a_name##_tsd_cleanup_wrapper);			\
144	}								\
145	a_name##_booted = true;						\
146	return (false);							\
147}									\
148/* Get/set. */								\
149a_attr a_type *								\
150a_name##_tsd_get(void)							\
151{									\
152									\
153	assert(a_name##_booted);					\
154	return (&a_name##_tls);						\
155}									\
156a_attr void								\
157a_name##_tsd_set(a_type *val)						\
158{									\
159									\
160	assert(a_name##_booted);					\
161	a_name##_tls = (*val);						\
162	if (a_cleanup != malloc_tsd_no_cleanup)				\
163		a_name##_initialized = true;				\
164}
165#elif (defined(JEMALLOC_TLS))
166#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
167    a_cleanup)								\
168/* Initialization/cleanup. */						\
169a_attr bool								\
170a_name##_tsd_boot(void)							\
171{									\
172									\
173	if (a_cleanup != malloc_tsd_no_cleanup) {			\
174		if (pthread_key_create(&a_name##_tsd, a_cleanup) != 0)	\
175			return (true);					\
176	}								\
177	a_name##_booted = true;						\
178	return (false);							\
179}									\
180/* Get/set. */								\
181a_attr a_type *								\
182a_name##_tsd_get(void)							\
183{									\
184									\
185	assert(a_name##_booted);					\
186	return (&a_name##_tls);						\
187}									\
188a_attr void								\
189a_name##_tsd_set(a_type *val)						\
190{									\
191									\
192	assert(a_name##_booted);					\
193	a_name##_tls = (*val);						\
194	if (a_cleanup != malloc_tsd_no_cleanup) {			\
195		if (pthread_setspecific(a_name##_tsd,			\
196		    (void *)(&a_name##_tls))) {				\
197			malloc_write("<jemalloc>: Error"		\
198			    " setting TSD for "#a_name"\n");		\
199			if (opt_abort)					\
200				abort();				\
201		}							\
202	}								\
203}
204#elif (defined(_WIN32))
205#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
206    a_cleanup)								\
207/* Data structure. */							\
208typedef struct {							\
209	bool	initialized;						\
210	a_type	val;							\
211} a_name##_tsd_wrapper_t;						\
212/* Initialization/cleanup. */						\
213a_attr bool								\
214a_name##_tsd_cleanup_wrapper(void)					\
215{									\
216	a_name##_tsd_wrapper_t *wrapper;				\
217									\
218	wrapper = (a_name##_tsd_wrapper_t *) TlsGetValue(a_name##_tsd);	\
219	if (wrapper == NULL)						\
220		return (false);						\
221	if (a_cleanup != malloc_tsd_no_cleanup &&			\
222	    wrapper->initialized) {					\
223		a_type val = wrapper->val;				\
224		a_type tsd_static_data = a_initializer;			\
225		wrapper->initialized = false;				\
226		wrapper->val = tsd_static_data;				\
227		a_cleanup(&val);					\
228		if (wrapper->initialized) {				\
229			/* Trigger another cleanup round. */		\
230			return (true);					\
231		}							\
232	}								\
233	malloc_tsd_dalloc(wrapper);					\
234	return (false);							\
235}									\
236a_attr bool								\
237a_name##_tsd_boot(void)							\
238{									\
239									\
240	a_name##_tsd = TlsAlloc();					\
241	if (a_name##_tsd == TLS_OUT_OF_INDEXES)				\
242		return (true);						\
243	if (a_cleanup != malloc_tsd_no_cleanup) {			\
244		malloc_tsd_cleanup_register(				\
245		    &a_name##_tsd_cleanup_wrapper);			\
246	}								\
247	a_name##_booted = true;						\
248	return (false);							\
249}									\
250/* Get/set. */								\
251a_attr a_name##_tsd_wrapper_t *						\
252a_name##_tsd_get_wrapper(void)						\
253{									\
254	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)	\
255	    TlsGetValue(a_name##_tsd);					\
256									\
257	if (wrapper == NULL) {						\
258		wrapper = (a_name##_tsd_wrapper_t *)			\
259		    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));	\
260		if (wrapper == NULL) {					\
261			malloc_write("<jemalloc>: Error allocating"	\
262			    " TSD for "#a_name"\n");			\
263			abort();					\
264		} else {						\
265			static a_type tsd_static_data = a_initializer;	\
266			wrapper->initialized = false;			\
267			wrapper->val = tsd_static_data;			\
268		}							\
269		if (!TlsSetValue(a_name##_tsd, (void *)wrapper)) {	\
270			malloc_write("<jemalloc>: Error setting"	\
271			    " TSD for "#a_name"\n");			\
272			abort();					\
273		}							\
274	}								\
275	return (wrapper);						\
276}									\
277a_attr a_type *								\
278a_name##_tsd_get(void)							\
279{									\
280	a_name##_tsd_wrapper_t *wrapper;				\
281									\
282	assert(a_name##_booted);					\
283	wrapper = a_name##_tsd_get_wrapper();				\
284	return (&wrapper->val);						\
285}									\
286a_attr void								\
287a_name##_tsd_set(a_type *val)						\
288{									\
289	a_name##_tsd_wrapper_t *wrapper;				\
290									\
291	assert(a_name##_booted);					\
292	wrapper = a_name##_tsd_get_wrapper();				\
293	wrapper->val = *(val);						\
294	if (a_cleanup != malloc_tsd_no_cleanup)				\
295		wrapper->initialized = true;				\
296}
297#else
298#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
299    a_cleanup)								\
300/* Data structure. */							\
301typedef struct {							\
302	bool	initialized;						\
303	a_type	val;							\
304} a_name##_tsd_wrapper_t;						\
305/* Initialization/cleanup. */						\
306a_attr void								\
307a_name##_tsd_cleanup_wrapper(void *arg)					\
308{									\
309	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\
310									\
311	if (a_cleanup != malloc_tsd_no_cleanup &&			\
312	    wrapper->initialized) {					\
313		wrapper->initialized = false;				\
314		a_cleanup(&wrapper->val);				\
315		if (wrapper->initialized) {				\
316			/* Trigger another cleanup round. */		\
317			if (pthread_setspecific(a_name##_tsd,		\
318			    (void *)wrapper)) {				\
319				malloc_write("<jemalloc>: Error"	\
320				    " setting TSD for "#a_name"\n");	\
321				if (opt_abort)				\
322					abort();			\
323			}						\
324			return;						\
325		}							\
326	}								\
327	malloc_tsd_dalloc(wrapper);					\
328}									\
329a_attr bool								\
330a_name##_tsd_boot(void)							\
331{									\
332									\
333	if (pthread_key_create(&a_name##_tsd,				\
334	    a_name##_tsd_cleanup_wrapper) != 0)				\
335		return (true);						\
336	a_name##_booted = true;						\
337	return (false);							\
338}									\
339/* Get/set. */								\
340a_attr a_name##_tsd_wrapper_t *						\
341a_name##_tsd_get_wrapper(void)						\
342{									\
343	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)	\
344	    pthread_getspecific(a_name##_tsd);				\
345									\
346	if (wrapper == NULL) {						\
347		tsd_init_block_t block;					\
348		wrapper = tsd_init_check_recursion(			\
349		    &a_name##_tsd_init_head, &block);			\
350		if (wrapper)						\
351		    return wrapper;					\
352		wrapper = (a_name##_tsd_wrapper_t *)			\
353		    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));	\
354		block.data = wrapper;					\
355		if (wrapper == NULL) {					\
356			malloc_write("<jemalloc>: Error allocating"	\
357			    " TSD for "#a_name"\n");			\
358			abort();					\
359		} else {						\
360			static a_type tsd_static_data = a_initializer;	\
361			wrapper->initialized = false;			\
362			wrapper->val = tsd_static_data;			\
363		}							\
364		if (pthread_setspecific(a_name##_tsd,			\
365		    (void *)wrapper)) {					\
366			malloc_write("<jemalloc>: Error setting"	\
367			    " TSD for "#a_name"\n");			\
368			abort();					\
369		}							\
370		tsd_init_finish(&a_name##_tsd_init_head, &block);	\
371	}								\
372	return (wrapper);						\
373}									\
374a_attr a_type *								\
375a_name##_tsd_get(void)							\
376{									\
377	a_name##_tsd_wrapper_t *wrapper;				\
378									\
379	assert(a_name##_booted);					\
380	wrapper = a_name##_tsd_get_wrapper();				\
381	return (&wrapper->val);						\
382}									\
383a_attr void								\
384a_name##_tsd_set(a_type *val)						\
385{									\
386	a_name##_tsd_wrapper_t *wrapper;				\
387									\
388	assert(a_name##_booted);					\
389	wrapper = a_name##_tsd_get_wrapper();				\
390	wrapper->val = *(val);						\
391	if (a_cleanup != malloc_tsd_no_cleanup)				\
392		wrapper->initialized = true;				\
393}
394#endif
395
396#endif /* JEMALLOC_H_TYPES */
397/******************************************************************************/
398#ifdef JEMALLOC_H_STRUCTS
399
400#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
401    !defined(_WIN32))
402struct tsd_init_block_s {
403	ql_elm(tsd_init_block_t)	link;
404	pthread_t			thread;
405	void				*data;
406};
407struct tsd_init_head_s {
408	ql_head(tsd_init_block_t)	blocks;
409	malloc_mutex_t			lock;
410};
411#endif
412
413#endif /* JEMALLOC_H_STRUCTS */
414/******************************************************************************/
415#ifdef JEMALLOC_H_EXTERNS
416
417void	*malloc_tsd_malloc(size_t size);
418void	malloc_tsd_dalloc(void *wrapper);
419void	malloc_tsd_no_cleanup(void *);
420void	malloc_tsd_cleanup_register(bool (*f)(void));
421void	malloc_tsd_boot(void);
422#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
423    !defined(_WIN32))
424void	*tsd_init_check_recursion(tsd_init_head_t *head,
425    tsd_init_block_t *block);
426void	tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block);
427#endif
428
429#endif /* JEMALLOC_H_EXTERNS */
430/******************************************************************************/
431#ifdef JEMALLOC_H_INLINES
432
433#endif /* JEMALLOC_H_INLINES */
434/******************************************************************************/
435