Home | History | Annotate | Download | only in internal
      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 
      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 /*
     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)			\
     65 a_attr bool								\
     66 a_name##_tsd_boot(void);						\
     67 a_attr a_type *								\
     68 a_name##_tsd_get(void);							\
     69 a_attr void								\
     70 a_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)				\
     75 extern __thread a_type	a_name##_tls;					\
     76 extern __thread bool	a_name##_initialized;				\
     77 extern bool		a_name##_booted;
     78 #elif (defined(JEMALLOC_TLS))
     79 #define	malloc_tsd_externs(a_name, a_type)				\
     80 extern __thread a_type	a_name##_tls;					\
     81 extern pthread_key_t	a_name##_tsd;					\
     82 extern bool		a_name##_booted;
     83 #elif (defined(_WIN32))
     84 #define	malloc_tsd_externs(a_name, a_type)				\
     85 extern DWORD		a_name##_tsd;					\
     86 extern bool		a_name##_booted;
     87 #else
     88 #define	malloc_tsd_externs(a_name, a_type)				\
     89 extern pthread_key_t	a_name##_tsd;					\
     90 extern tsd_init_head_t	a_name##_tsd_init_head;				\
     91 extern 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)		\
     97 a_attr __thread a_type JEMALLOC_TLS_MODEL				\
     98     a_name##_tls = a_initializer;					\
     99 a_attr __thread bool JEMALLOC_TLS_MODEL					\
    100     a_name##_initialized = false;					\
    101 a_attr bool		a_name##_booted = false;
    102 #elif (defined(JEMALLOC_TLS))
    103 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
    104 a_attr __thread a_type JEMALLOC_TLS_MODEL				\
    105     a_name##_tls = a_initializer;					\
    106 a_attr pthread_key_t	a_name##_tsd;					\
    107 a_attr bool		a_name##_booted = false;
    108 #elif (defined(_WIN32))
    109 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
    110 a_attr DWORD		a_name##_tsd;					\
    111 a_attr bool		a_name##_booted = false;
    112 #else
    113 #define	malloc_tsd_data(a_attr, a_name, a_type, a_initializer)		\
    114 a_attr pthread_key_t	a_name##_tsd;					\
    115 a_attr tsd_init_head_t	a_name##_tsd_init_head = {			\
    116 	ql_head_initializer(blocks),					\
    117 	MALLOC_MUTEX_INITIALIZER					\
    118 };									\
    119 a_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. */						\
    127 a_attr bool								\
    128 a_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 }									\
    137 a_attr bool								\
    138 a_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. */								\
    149 a_attr a_type *								\
    150 a_name##_tsd_get(void)							\
    151 {									\
    152 									\
    153 	assert(a_name##_booted);					\
    154 	return (&a_name##_tls);						\
    155 }									\
    156 a_attr void								\
    157 a_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. */						\
    169 a_attr bool								\
    170 a_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. */								\
    181 a_attr a_type *								\
    182 a_name##_tsd_get(void)							\
    183 {									\
    184 									\
    185 	assert(a_name##_booted);					\
    186 	return (&a_name##_tls);						\
    187 }									\
    188 a_attr void								\
    189 a_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. */							\
    208 typedef struct {							\
    209 	bool	initialized;						\
    210 	a_type	val;							\
    211 } a_name##_tsd_wrapper_t;						\
    212 /* Initialization/cleanup. */						\
    213 a_attr bool								\
    214 a_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 }									\
    236 a_attr bool								\
    237 a_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. */								\
    251 a_attr a_name##_tsd_wrapper_t *						\
    252 a_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 }									\
    277 a_attr a_type *								\
    278 a_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 }									\
    286 a_attr void								\
    287 a_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. */							\
    301 typedef struct {							\
    302 	bool	initialized;						\
    303 	a_type	val;							\
    304 } a_name##_tsd_wrapper_t;						\
    305 /* Initialization/cleanup. */						\
    306 a_attr void								\
    307 a_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 }									\
    329 a_attr bool								\
    330 a_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. */								\
    340 a_attr a_name##_tsd_wrapper_t *						\
    341 a_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 }									\
    374 a_attr a_type *								\
    375 a_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 }									\
    383 a_attr void								\
    384 a_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))
    402 struct tsd_init_block_s {
    403 	ql_elm(tsd_init_block_t)	link;
    404 	pthread_t			thread;
    405 	void				*data;
    406 };
    407 struct 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 
    417 void	*malloc_tsd_malloc(size_t size);
    418 void	malloc_tsd_dalloc(void *wrapper);
    419 void	malloc_tsd_no_cleanup(void *);
    420 void	malloc_tsd_cleanup_register(bool (*f)(void));
    421 void	malloc_tsd_boot(void);
    422 #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
    423     !defined(_WIN32))
    424 void	*tsd_init_check_recursion(tsd_init_head_t *head,
    425     tsd_init_block_t *block);
    426 void	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