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#elif (defined(_WIN32)) 78#define malloc_tsd_externs(a_name, a_type) \ 79extern DWORD 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_TLS_MODEL \ 91 a_name##_tls = a_initializer; \ 92a_attr __thread bool JEMALLOC_TLS_MODEL \ 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_TLS_MODEL \ 98 a_name##_tls = a_initializer; \ 99a_attr pthread_key_t a_name##_tsd; \ 100a_attr bool a_name##_booted = false; 101#elif (defined(_WIN32)) 102#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 103a_attr DWORD a_name##_tsd; \ 104a_attr bool a_name##_booted = false; 105#else 106#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 107a_attr pthread_key_t a_name##_tsd; \ 108a_attr bool a_name##_booted = false; 109#endif 110 111/* malloc_tsd_funcs(). */ 112#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 113#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 114 a_cleanup) \ 115/* Initialization/cleanup. */ \ 116a_attr bool \ 117a_name##_tsd_cleanup_wrapper(void) \ 118{ \ 119 \ 120 if (a_name##_initialized) { \ 121 a_name##_initialized = false; \ 122 a_cleanup(&a_name##_tls); \ 123 } \ 124 return (a_name##_initialized); \ 125} \ 126a_attr bool \ 127a_name##_tsd_boot(void) \ 128{ \ 129 \ 130 if (a_cleanup != malloc_tsd_no_cleanup) { \ 131 malloc_tsd_cleanup_register( \ 132 &a_name##_tsd_cleanup_wrapper); \ 133 } \ 134 a_name##_booted = true; \ 135 return (false); \ 136} \ 137/* Get/set. */ \ 138a_attr a_type * \ 139a_name##_tsd_get(void) \ 140{ \ 141 \ 142 assert(a_name##_booted); \ 143 return (&a_name##_tls); \ 144} \ 145a_attr void \ 146a_name##_tsd_set(a_type *val) \ 147{ \ 148 \ 149 assert(a_name##_booted); \ 150 a_name##_tls = (*val); \ 151 if (a_cleanup != malloc_tsd_no_cleanup) \ 152 a_name##_initialized = true; \ 153} 154#elif (defined(JEMALLOC_TLS)) 155#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 156 a_cleanup) \ 157/* Initialization/cleanup. */ \ 158a_attr bool \ 159a_name##_tsd_boot(void) \ 160{ \ 161 \ 162 if (a_cleanup != malloc_tsd_no_cleanup) { \ 163 if (pthread_key_create(&a_name##_tsd, a_cleanup) != 0) \ 164 return (true); \ 165 } \ 166 a_name##_booted = true; \ 167 return (false); \ 168} \ 169/* Get/set. */ \ 170a_attr a_type * \ 171a_name##_tsd_get(void) \ 172{ \ 173 \ 174 assert(a_name##_booted); \ 175 return (&a_name##_tls); \ 176} \ 177a_attr void \ 178a_name##_tsd_set(a_type *val) \ 179{ \ 180 \ 181 assert(a_name##_booted); \ 182 a_name##_tls = (*val); \ 183 if (a_cleanup != malloc_tsd_no_cleanup) { \ 184 if (pthread_setspecific(a_name##_tsd, \ 185 (void *)(&a_name##_tls))) { \ 186 malloc_write("<jemalloc>: Error" \ 187 " setting TSD for "#a_name"\n"); \ 188 if (opt_abort) \ 189 abort(); \ 190 } \ 191 } \ 192} 193#elif (defined(_WIN32)) 194#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 195 a_cleanup) \ 196/* Data structure. */ \ 197typedef struct { \ 198 bool initialized; \ 199 a_type val; \ 200} a_name##_tsd_wrapper_t; \ 201/* Initialization/cleanup. */ \ 202a_attr bool \ 203a_name##_tsd_cleanup_wrapper(void) \ 204{ \ 205 a_name##_tsd_wrapper_t *wrapper; \ 206 \ 207 wrapper = (a_name##_tsd_wrapper_t *) TlsGetValue(a_name##_tsd); \ 208 if (wrapper == NULL) \ 209 return (false); \ 210 if (a_cleanup != malloc_tsd_no_cleanup && \ 211 wrapper->initialized) { \ 212 a_type val = wrapper->val; \ 213 a_type tsd_static_data = a_initializer; \ 214 wrapper->initialized = false; \ 215 wrapper->val = tsd_static_data; \ 216 a_cleanup(&val); \ 217 if (wrapper->initialized) { \ 218 /* Trigger another cleanup round. */ \ 219 return (true); \ 220 } \ 221 } \ 222 malloc_tsd_dalloc(wrapper); \ 223 return (false); \ 224} \ 225a_attr bool \ 226a_name##_tsd_boot(void) \ 227{ \ 228 \ 229 a_name##_tsd = TlsAlloc(); \ 230 if (a_name##_tsd == TLS_OUT_OF_INDEXES) \ 231 return (true); \ 232 if (a_cleanup != malloc_tsd_no_cleanup) { \ 233 malloc_tsd_cleanup_register( \ 234 &a_name##_tsd_cleanup_wrapper); \ 235 } \ 236 a_name##_booted = true; \ 237 return (false); \ 238} \ 239/* Get/set. */ \ 240a_attr a_name##_tsd_wrapper_t * \ 241a_name##_tsd_get_wrapper(void) \ 242{ \ 243 a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *) \ 244 TlsGetValue(a_name##_tsd); \ 245 \ 246 if (wrapper == NULL) { \ 247 wrapper = (a_name##_tsd_wrapper_t *) \ 248 malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ 249 if (wrapper == NULL) { \ 250 malloc_write("<jemalloc>: Error allocating" \ 251 " TSD for "#a_name"\n"); \ 252 abort(); \ 253 } else { \ 254 static a_type tsd_static_data = a_initializer; \ 255 wrapper->initialized = false; \ 256 wrapper->val = tsd_static_data; \ 257 } \ 258 if (!TlsSetValue(a_name##_tsd, (void *)wrapper)) { \ 259 malloc_write("<jemalloc>: Error setting" \ 260 " TSD for "#a_name"\n"); \ 261 abort(); \ 262 } \ 263 } \ 264 return (wrapper); \ 265} \ 266a_attr a_type * \ 267a_name##_tsd_get(void) \ 268{ \ 269 a_name##_tsd_wrapper_t *wrapper; \ 270 \ 271 assert(a_name##_booted); \ 272 wrapper = a_name##_tsd_get_wrapper(); \ 273 return (&wrapper->val); \ 274} \ 275a_attr void \ 276a_name##_tsd_set(a_type *val) \ 277{ \ 278 a_name##_tsd_wrapper_t *wrapper; \ 279 \ 280 assert(a_name##_booted); \ 281 wrapper = a_name##_tsd_get_wrapper(); \ 282 wrapper->val = *(val); \ 283 if (a_cleanup != malloc_tsd_no_cleanup) \ 284 wrapper->initialized = true; \ 285} 286#else 287#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 288 a_cleanup) \ 289/* Data structure. */ \ 290typedef struct { \ 291 bool initialized; \ 292 a_type val; \ 293} a_name##_tsd_wrapper_t; \ 294/* Initialization/cleanup. */ \ 295a_attr void \ 296a_name##_tsd_cleanup_wrapper(void *arg) \ 297{ \ 298 a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\ 299 \ 300 if (a_cleanup != malloc_tsd_no_cleanup && \ 301 wrapper->initialized) { \ 302 wrapper->initialized = false; \ 303 a_cleanup(&wrapper->val); \ 304 if (wrapper->initialized) { \ 305 /* Trigger another cleanup round. */ \ 306 if (pthread_setspecific(a_name##_tsd, \ 307 (void *)wrapper)) { \ 308 malloc_write("<jemalloc>: Error" \ 309 " setting TSD for "#a_name"\n"); \ 310 if (opt_abort) \ 311 abort(); \ 312 } \ 313 return; \ 314 } \ 315 } \ 316 malloc_tsd_dalloc(wrapper); \ 317} \ 318a_attr bool \ 319a_name##_tsd_boot(void) \ 320{ \ 321 \ 322 if (pthread_key_create(&a_name##_tsd, \ 323 a_name##_tsd_cleanup_wrapper) != 0) \ 324 return (true); \ 325 a_name##_booted = true; \ 326 return (false); \ 327} \ 328/* Get/set. */ \ 329a_attr a_name##_tsd_wrapper_t * \ 330a_name##_tsd_get_wrapper(void) \ 331{ \ 332 a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *) \ 333 pthread_getspecific(a_name##_tsd); \ 334 \ 335 if (wrapper == NULL) { \ 336 wrapper = (a_name##_tsd_wrapper_t *) \ 337 malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ 338 if (wrapper == NULL) { \ 339 malloc_write("<jemalloc>: Error allocating" \ 340 " TSD for "#a_name"\n"); \ 341 abort(); \ 342 } else { \ 343 static a_type tsd_static_data = a_initializer; \ 344 wrapper->initialized = false; \ 345 wrapper->val = tsd_static_data; \ 346 } \ 347 if (pthread_setspecific(a_name##_tsd, \ 348 (void *)wrapper)) { \ 349 malloc_write("<jemalloc>: Error setting" \ 350 " TSD for "#a_name"\n"); \ 351 abort(); \ 352 } \ 353 } \ 354 return (wrapper); \ 355} \ 356a_attr a_type * \ 357a_name##_tsd_get(void) \ 358{ \ 359 a_name##_tsd_wrapper_t *wrapper; \ 360 \ 361 assert(a_name##_booted); \ 362 wrapper = a_name##_tsd_get_wrapper(); \ 363 return (&wrapper->val); \ 364} \ 365a_attr void \ 366a_name##_tsd_set(a_type *val) \ 367{ \ 368 a_name##_tsd_wrapper_t *wrapper; \ 369 \ 370 assert(a_name##_booted); \ 371 wrapper = a_name##_tsd_get_wrapper(); \ 372 wrapper->val = *(val); \ 373 if (a_cleanup != malloc_tsd_no_cleanup) \ 374 wrapper->initialized = true; \ 375} 376#endif 377 378#endif /* JEMALLOC_H_TYPES */ 379/******************************************************************************/ 380#ifdef JEMALLOC_H_STRUCTS 381 382#endif /* JEMALLOC_H_STRUCTS */ 383/******************************************************************************/ 384#ifdef JEMALLOC_H_EXTERNS 385 386void *malloc_tsd_malloc(size_t size); 387void malloc_tsd_dalloc(void *wrapper); 388void malloc_tsd_no_cleanup(void *); 389void malloc_tsd_cleanup_register(bool (*f)(void)); 390void malloc_tsd_boot(void); 391 392#endif /* JEMALLOC_H_EXTERNS */ 393/******************************************************************************/ 394#ifdef JEMALLOC_H_INLINES 395 396#endif /* JEMALLOC_H_INLINES */ 397/******************************************************************************/ 398