tsd.h revision cd9a1346e96f71bdecdc654ea50fc62d76371e74
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