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