tsd.h revision 13067ec8350f213c3accc2e5fb70ca5a503e0e17
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/*
10 * TLS/TSD-agnostic macro-based implementation of thread-specific data.  There
11 * are four macros that support (at least) three use cases: file-private,
12 * library-private, and library-private inlined.  Following is an example
13 * library-private tsd variable:
14 *
15 * In example.h:
16 *   typedef struct {
17 *           int x;
18 *           int y;
19 *   } example_t;
20 *   #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0})
21 *   malloc_tsd_protos(, example, example_t *)
22 *   malloc_tsd_externs(example, example_t *)
23 * In example.c:
24 *   malloc_tsd_data(, example, example_t *, EX_INITIALIZER)
25 *   malloc_tsd_funcs(, example, example_t *, EX_INITIALIZER,
26 *       example_tsd_cleanup)
27 *
28 * The result is a set of generated functions, e.g.:
29 *
30 *   bool example_tsd_boot(void) {...}
31 *   example_t **example_tsd_get() {...}
32 *   void example_tsd_set(example_t **val) {...}
33 *
34 * Note that all of the functions deal in terms of (a_type *) rather than
35 * (a_type)  so that it is possible to support non-pointer types (unlike
36 * pthreads TSD).  example_tsd_cleanup() is passed an (a_type *) pointer that is
37 * cast to (void *).  This means that the cleanup function needs to cast *and*
38 * dereference the function argument, e.g.:
39 *
40 *   void
41 *   example_tsd_cleanup(void *arg)
42 *   {
43 *           example_t *example = *(example_t **)arg;
44 *
45 *           [...]
46 *           if ([want the cleanup function to be called again]) {
47 *                   example_tsd_set(&example);
48 *           }
49 *   }
50 *
51 * If example_tsd_set() is called within example_tsd_cleanup(), it will be
52 * called again.  This is similar to how pthreads TSD destruction works, except
53 * that pthreads only calls the cleanup function again if the value was set to
54 * non-NULL.
55 */
56
57/* malloc_tsd_protos(). */
58#define	malloc_tsd_protos(a_attr, a_name, a_type)			\
59a_attr bool								\
60a_name##_tsd_boot(void);						\
61a_attr a_type *								\
62a_name##_tsd_get(void);							\
63a_attr void								\
64a_name##_tsd_set(a_type *val);
65
66/* malloc_tsd_externs(). */
67#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
68#define	malloc_tsd_externs(a_name, a_type)				\
69extern __thread a_type	a_name##_tls;					\
70extern __thread bool	a_name##_initialized;				\
71extern bool		a_name##_booted;
72#elif (defined(JEMALLOC_TLS))
73#define	malloc_tsd_externs(a_name, a_type)				\
74extern __thread a_type	a_name##_tls;					\
75extern pthread_key_t	a_name##_tsd;					\
76extern bool		a_name##_booted;
77#else
78#define	malloc_tsd_externs(a_name, a_type)				\
79extern pthread_key_t	a_name##_tsd;					\
80extern bool		a_name##_booted;
81#endif
82
83/* malloc_tsd_data(). */
84#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
85#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
86a_attr __thread a_type JEMALLOC_TLS_MODEL				\
87    a_name##_tls = a_initializer;					\
88a_attr __thread bool JEMALLOC_TLS_MODEL					\
89    a_name##_initialized = false;					\
90a_attr bool		a_name##_booted = false;
91#elif (defined(JEMALLOC_TLS))
92#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
93a_attr __thread a_type JEMALLOC_TLS_MODEL				\
94    a_name##_tls = a_initializer;					\
95a_attr pthread_key_t	a_name##_tsd;					\
96a_attr bool		a_name##_booted = false;
97#else
98#define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
99a_attr pthread_key_t	a_name##_tsd;					\
100a_attr bool		a_name##_booted = false;
101#endif
102
103/* malloc_tsd_funcs(). */
104#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
105#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
106    a_cleanup)								\
107/* Initialization/cleanup. */						\
108a_attr bool								\
109a_name##_tsd_cleanup_wrapper(void)					\
110{									\
111									\
112	if (a_name##_initialized) {					\
113		a_name##_initialized = false;				\
114		a_cleanup(&a_name##_tls);					\
115	}								\
116	return (a_name##_initialized);					\
117}									\
118a_attr bool								\
119a_name##_tsd_boot(void)							\
120{									\
121									\
122	if (a_cleanup != malloc_tsd_no_cleanup) {			\
123		malloc_tsd_cleanup_register(				\
124		    &a_name##_tsd_cleanup_wrapper);			\
125	}								\
126	a_name##_booted = true;						\
127	return (false);							\
128}									\
129/* Get/set. */								\
130a_attr a_type *								\
131a_name##_tsd_get(void)							\
132{									\
133									\
134	assert(a_name##_booted);					\
135	return (&a_name##_tls);						\
136}									\
137a_attr void								\
138a_name##_tsd_set(a_type *val)						\
139{									\
140									\
141	assert(a_name##_booted);					\
142	a_name##_tls = (*val);						\
143	if (a_cleanup != malloc_tsd_no_cleanup)				\
144		a_name##_initialized = true;				\
145}
146#elif (defined(JEMALLOC_TLS))
147#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
148    a_cleanup)								\
149/* Initialization/cleanup. */						\
150a_attr bool								\
151a_name##_tsd_boot(void)							\
152{									\
153									\
154	if (a_cleanup != malloc_tsd_no_cleanup) {			\
155		if (pthread_key_create(&a_name##_tsd, a_cleanup) != 0)	\
156			return (true);					\
157	}								\
158	a_name##_booted = true;						\
159	return (false);							\
160}									\
161/* Get/set. */								\
162a_attr a_type *								\
163a_name##_tsd_get(void)							\
164{									\
165									\
166	assert(a_name##_booted);					\
167	return (&a_name##_tls);						\
168}									\
169a_attr void								\
170a_name##_tsd_set(a_type *val)						\
171{									\
172									\
173	assert(a_name##_booted);					\
174	a_name##_tls = (*val);						\
175	if (a_cleanup != malloc_tsd_no_cleanup) {			\
176		if (pthread_setspecific(a_name##_tsd,			\
177		    (void *)(&a_name##_tls))) {				\
178			malloc_write("<jemalloc>: Error"		\
179			    " setting TSD for "#a_name"\n");		\
180			if (opt_abort)					\
181				abort();				\
182		}							\
183	}								\
184}
185#else
186#define	malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,		\
187    a_cleanup)								\
188/* Data structure. */							\
189typedef struct {							\
190	bool	initialized;						\
191	a_type	val;							\
192} a_name##_tsd_wrapper_t;						\
193/* Initialization/cleanup. */						\
194a_attr void								\
195a_name##_tsd_cleanup_wrapper(void *arg)					\
196{									\
197	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\
198									\
199	if (a_cleanup != malloc_tsd_no_cleanup &&			\
200	    wrapper->initialized) {					\
201		wrapper->initialized = false;				\
202		a_cleanup(&wrapper->val);				\
203		if (wrapper->initialized) {				\
204			/* Trigger another cleanup round. */		\
205			if (pthread_setspecific(a_name##_tsd,		\
206			    (void *)wrapper)) {				\
207				malloc_write("<jemalloc>: Error"	\
208				    " setting TSD for "#a_name"\n");	\
209				if (opt_abort)				\
210					abort();			\
211			}						\
212			return;						\
213		}							\
214	}								\
215	malloc_tsd_dalloc(wrapper);					\
216}									\
217a_attr bool								\
218a_name##_tsd_boot(void)							\
219{									\
220									\
221	if (pthread_key_create(&a_name##_tsd,				\
222	    a_name##_tsd_cleanup_wrapper) != 0)				\
223		return (true);						\
224	a_name##_booted = true;						\
225	return (false);							\
226}									\
227/* Get/set. */								\
228a_attr a_name##_tsd_wrapper_t *						\
229a_name##_tsd_get_wrapper(void)						\
230{									\
231	a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)	\
232	    pthread_getspecific(a_name##_tsd);				\
233									\
234	if (wrapper == NULL) {						\
235		wrapper = (a_name##_tsd_wrapper_t *)			\
236		    malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t));	\
237		if (wrapper == NULL) {					\
238			malloc_write("<jemalloc>: Error allocating"	\
239			    " TSD for "#a_name"\n");			\
240			abort();					\
241		} else {						\
242			static a_type tsd_static_data = a_initializer;	\
243			wrapper->initialized = false;			\
244			wrapper->val = tsd_static_data;			\
245		}							\
246		if (pthread_setspecific(a_name##_tsd,			\
247		    (void *)wrapper)) {					\
248			malloc_write("<jemalloc>: Error setting"	\
249			    " TSD for "#a_name"\n");			\
250			abort();					\
251		}							\
252	}								\
253	return (wrapper);						\
254}									\
255a_attr a_type *								\
256a_name##_tsd_get(void)							\
257{									\
258	a_name##_tsd_wrapper_t *wrapper;				\
259									\
260	assert(a_name##_booted);					\
261	wrapper = a_name##_tsd_get_wrapper();				\
262	return (&wrapper->val);						\
263}									\
264a_attr void								\
265a_name##_tsd_set(a_type *val)						\
266{									\
267	a_name##_tsd_wrapper_t *wrapper;				\
268									\
269	assert(a_name##_booted);					\
270	wrapper = a_name##_tsd_get_wrapper();				\
271	wrapper->val = *(val);						\
272	if (a_cleanup != malloc_tsd_no_cleanup)				\
273		wrapper->initialized = true;				\
274}
275#endif
276
277#endif /* JEMALLOC_H_TYPES */
278/******************************************************************************/
279#ifdef JEMALLOC_H_STRUCTS
280
281#endif /* JEMALLOC_H_STRUCTS */
282/******************************************************************************/
283#ifdef JEMALLOC_H_EXTERNS
284
285void	*malloc_tsd_malloc(size_t size);
286void	malloc_tsd_dalloc(void *wrapper);
287void	malloc_tsd_no_cleanup(void *);
288void	malloc_tsd_cleanup_register(bool (*f)(void));
289void	malloc_tsd_boot(void);
290
291#endif /* JEMALLOC_H_EXTERNS */
292/******************************************************************************/
293#ifdef JEMALLOC_H_INLINES
294
295#endif /* JEMALLOC_H_INLINES */
296/******************************************************************************/
297