tsd.h revision 288090
1/******************************************************************************/ 2#ifdef JEMALLOC_H_TYPES 3 4/* Maximum number of malloc_tsd users with cleanup functions. */ 5#define MALLOC_TSD_CLEANUPS_MAX 2 6 7typedef bool (*malloc_tsd_cleanup_t)(void); 8 9#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ 10 !defined(_WIN32)) 11typedef struct tsd_init_block_s tsd_init_block_t; 12typedef struct tsd_init_head_s tsd_init_head_t; 13#endif 14 15typedef struct tsd_s tsd_t; 16 17typedef enum { 18 tsd_state_uninitialized, 19 tsd_state_nominal, 20 tsd_state_purgatory, 21 tsd_state_reincarnated 22} tsd_state_t; 23 24/* 25 * TLS/TSD-agnostic macro-based implementation of thread-specific data. There 26 * are five macros that support (at least) three use cases: file-private, 27 * library-private, and library-private inlined. Following is an example 28 * library-private tsd variable: 29 * 30 * In example.h: 31 * typedef struct { 32 * int x; 33 * int y; 34 * } example_t; 35 * #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0}) 36 * malloc_tsd_types(example_, example_t) 37 * malloc_tsd_protos(, example_, example_t) 38 * malloc_tsd_externs(example_, example_t) 39 * In example.c: 40 * malloc_tsd_data(, example_, example_t, EX_INITIALIZER) 41 * malloc_tsd_funcs(, example_, example_t, EX_INITIALIZER, 42 * example_tsd_cleanup) 43 * 44 * The result is a set of generated functions, e.g.: 45 * 46 * bool example_tsd_boot(void) {...} 47 * example_t *example_tsd_get() {...} 48 * void example_tsd_set(example_t *val) {...} 49 * 50 * Note that all of the functions deal in terms of (a_type *) rather than 51 * (a_type) so that it is possible to support non-pointer types (unlike 52 * pthreads TSD). example_tsd_cleanup() is passed an (a_type *) pointer that is 53 * cast to (void *). This means that the cleanup function needs to cast the 54 * function argument to (a_type *), then dereference the resulting pointer to 55 * access fields, e.g. 56 * 57 * void 58 * example_tsd_cleanup(void *arg) 59 * { 60 * example_t *example = (example_t *)arg; 61 * 62 * example->x = 42; 63 * [...] 64 * if ([want the cleanup function to be called again]) 65 * example_tsd_set(example); 66 * } 67 * 68 * If example_tsd_set() is called within example_tsd_cleanup(), it will be 69 * called again. This is similar to how pthreads TSD destruction works, except 70 * that pthreads only calls the cleanup function again if the value was set to 71 * non-NULL. 72 */ 73 74/* malloc_tsd_types(). */ 75#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 76#define malloc_tsd_types(a_name, a_type) 77#elif (defined(JEMALLOC_TLS)) 78#define malloc_tsd_types(a_name, a_type) 79#elif (defined(_WIN32)) 80#define malloc_tsd_types(a_name, a_type) \ 81typedef struct { \ 82 bool initialized; \ 83 a_type val; \ 84} a_name##tsd_wrapper_t; 85#else 86#define malloc_tsd_types(a_name, a_type) \ 87typedef struct { \ 88 bool initialized; \ 89 a_type val; \ 90} a_name##tsd_wrapper_t; 91#endif 92 93/* malloc_tsd_protos(). */ 94#define malloc_tsd_protos(a_attr, a_name, a_type) \ 95a_attr bool \ 96a_name##tsd_boot0(void); \ 97a_attr void \ 98a_name##tsd_boot1(void); \ 99a_attr bool \ 100a_name##tsd_boot(void); \ 101a_attr a_type * \ 102a_name##tsd_get(void); \ 103a_attr void \ 104a_name##tsd_set(a_type *val); 105 106/* malloc_tsd_externs(). */ 107#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 108#define malloc_tsd_externs(a_name, a_type) \ 109extern __thread a_type a_name##tsd_tls; \ 110extern __thread bool a_name##tsd_initialized; \ 111extern bool a_name##tsd_booted; 112#elif (defined(JEMALLOC_TLS)) 113#define malloc_tsd_externs(a_name, a_type) \ 114extern __thread a_type a_name##tsd_tls; \ 115extern pthread_key_t a_name##tsd_tsd; \ 116extern bool a_name##tsd_booted; 117#elif (defined(_WIN32)) 118#define malloc_tsd_externs(a_name, a_type) \ 119extern DWORD a_name##tsd_tsd; \ 120extern a_name##tsd_wrapper_t a_name##tsd_boot_wrapper; \ 121extern bool a_name##tsd_booted; 122#else 123#define malloc_tsd_externs(a_name, a_type) \ 124extern pthread_key_t a_name##tsd_tsd; \ 125extern tsd_init_head_t a_name##tsd_init_head; \ 126extern a_name##tsd_wrapper_t a_name##tsd_boot_wrapper; \ 127extern bool a_name##tsd_booted; 128#endif 129 130/* malloc_tsd_data(). */ 131#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 132#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 133a_attr __thread a_type JEMALLOC_TLS_MODEL \ 134 a_name##tsd_tls = a_initializer; \ 135a_attr __thread bool JEMALLOC_TLS_MODEL \ 136 a_name##tsd_initialized = false; \ 137a_attr bool a_name##tsd_booted = false; 138#elif (defined(JEMALLOC_TLS)) 139#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 140a_attr __thread a_type JEMALLOC_TLS_MODEL \ 141 a_name##tsd_tls = a_initializer; \ 142a_attr pthread_key_t a_name##tsd_tsd; \ 143a_attr bool a_name##tsd_booted = false; 144#elif (defined(_WIN32)) 145#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 146a_attr DWORD a_name##tsd_tsd; \ 147a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = { \ 148 false, \ 149 a_initializer \ 150}; \ 151a_attr bool a_name##tsd_booted = false; 152#else 153#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 154a_attr pthread_key_t a_name##tsd_tsd; \ 155a_attr tsd_init_head_t a_name##tsd_init_head = { \ 156 ql_head_initializer(blocks), \ 157 MALLOC_MUTEX_INITIALIZER \ 158}; \ 159a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = { \ 160 false, \ 161 a_initializer \ 162}; \ 163a_attr bool a_name##tsd_booted = false; 164#endif 165 166/* malloc_tsd_funcs(). */ 167#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 168#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 169 a_cleanup) \ 170/* Initialization/cleanup. */ \ 171a_attr bool \ 172a_name##tsd_cleanup_wrapper(void) \ 173{ \ 174 \ 175 if (a_name##tsd_initialized) { \ 176 a_name##tsd_initialized = false; \ 177 a_cleanup(&a_name##tsd_tls); \ 178 } \ 179 return (a_name##tsd_initialized); \ 180} \ 181a_attr bool \ 182a_name##tsd_boot0(void) \ 183{ \ 184 \ 185 if (a_cleanup != malloc_tsd_no_cleanup) { \ 186 malloc_tsd_cleanup_register( \ 187 &a_name##tsd_cleanup_wrapper); \ 188 } \ 189 a_name##tsd_booted = true; \ 190 return (false); \ 191} \ 192a_attr void \ 193a_name##tsd_boot1(void) \ 194{ \ 195 \ 196 /* Do nothing. */ \ 197} \ 198a_attr bool \ 199a_name##tsd_boot(void) \ 200{ \ 201 \ 202 return (a_name##tsd_boot0()); \ 203} \ 204/* Get/set. */ \ 205a_attr a_type * \ 206a_name##tsd_get(void) \ 207{ \ 208 \ 209 assert(a_name##tsd_booted); \ 210 return (&a_name##tsd_tls); \ 211} \ 212a_attr void \ 213a_name##tsd_set(a_type *val) \ 214{ \ 215 \ 216 assert(a_name##tsd_booted); \ 217 a_name##tsd_tls = (*val); \ 218 if (a_cleanup != malloc_tsd_no_cleanup) \ 219 a_name##tsd_initialized = true; \ 220} 221#elif (defined(JEMALLOC_TLS)) 222#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 223 a_cleanup) \ 224/* Initialization/cleanup. */ \ 225a_attr bool \ 226a_name##tsd_boot0(void) \ 227{ \ 228 \ 229 if (a_cleanup != malloc_tsd_no_cleanup) { \ 230 if (pthread_key_create(&a_name##tsd_tsd, a_cleanup) != \ 231 0) \ 232 return (true); \ 233 } \ 234 a_name##tsd_booted = true; \ 235 return (false); \ 236} \ 237a_attr void \ 238a_name##tsd_boot1(void) \ 239{ \ 240 \ 241 /* Do nothing. */ \ 242} \ 243a_attr bool \ 244a_name##tsd_boot(void) \ 245{ \ 246 \ 247 return (a_name##tsd_boot0()); \ 248} \ 249/* Get/set. */ \ 250a_attr a_type * \ 251a_name##tsd_get(void) \ 252{ \ 253 \ 254 assert(a_name##tsd_booted); \ 255 return (&a_name##tsd_tls); \ 256} \ 257a_attr void \ 258a_name##tsd_set(a_type *val) \ 259{ \ 260 \ 261 assert(a_name##tsd_booted); \ 262 a_name##tsd_tls = (*val); \ 263 if (a_cleanup != malloc_tsd_no_cleanup) { \ 264 if (pthread_setspecific(a_name##tsd_tsd, \ 265 (void *)(&a_name##tsd_tls))) { \ 266 malloc_write("<jemalloc>: Error" \ 267 " setting TSD for "#a_name"\n"); \ 268 if (opt_abort) \ 269 abort(); \ 270 } \ 271 } \ 272} 273#elif (defined(_WIN32)) 274#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 275 a_cleanup) \ 276/* Initialization/cleanup. */ \ 277a_attr bool \ 278a_name##tsd_cleanup_wrapper(void) \ 279{ \ 280 DWORD error = GetLastError(); \ 281 a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \ 282 TlsGetValue(a_name##tsd_tsd); \ 283 SetLastError(error); \ 284 \ 285 if (wrapper == NULL) \ 286 return (false); \ 287 if (a_cleanup != malloc_tsd_no_cleanup && \ 288 wrapper->initialized) { \ 289 wrapper->initialized = false; \ 290 a_cleanup(&wrapper->val); \ 291 if (wrapper->initialized) { \ 292 /* Trigger another cleanup round. */ \ 293 return (true); \ 294 } \ 295 } \ 296 malloc_tsd_dalloc(wrapper); \ 297 return (false); \ 298} \ 299a_attr void \ 300a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper) \ 301{ \ 302 \ 303 if (!TlsSetValue(a_name##tsd_tsd, (void *)wrapper)) { \ 304 malloc_write("<jemalloc>: Error setting" \ 305 " TSD for "#a_name"\n"); \ 306 abort(); \ 307 } \ 308} \ 309a_attr a_name##tsd_wrapper_t * \ 310a_name##tsd_wrapper_get(void) \ 311{ \ 312 DWORD error = GetLastError(); \ 313 a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \ 314 TlsGetValue(a_name##tsd_tsd); \ 315 SetLastError(error); \ 316 \ 317 if (unlikely(wrapper == NULL)) { \ 318 wrapper = (a_name##tsd_wrapper_t *) \ 319 malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ 320 if (wrapper == NULL) { \ 321 malloc_write("<jemalloc>: Error allocating" \ 322 " TSD for "#a_name"\n"); \ 323 abort(); \ 324 } else { \ 325 wrapper->initialized = false; \ 326 wrapper->val = a_initializer; \ 327 } \ 328 a_name##tsd_wrapper_set(wrapper); \ 329 } \ 330 return (wrapper); \ 331} \ 332a_attr bool \ 333a_name##tsd_boot0(void) \ 334{ \ 335 \ 336 a_name##tsd_tsd = TlsAlloc(); \ 337 if (a_name##tsd_tsd == TLS_OUT_OF_INDEXES) \ 338 return (true); \ 339 if (a_cleanup != malloc_tsd_no_cleanup) { \ 340 malloc_tsd_cleanup_register( \ 341 &a_name##tsd_cleanup_wrapper); \ 342 } \ 343 a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper); \ 344 a_name##tsd_booted = true; \ 345 return (false); \ 346} \ 347a_attr void \ 348a_name##tsd_boot1(void) \ 349{ \ 350 a_name##tsd_wrapper_t *wrapper; \ 351 wrapper = (a_name##tsd_wrapper_t *) \ 352 malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ 353 if (wrapper == NULL) { \ 354 malloc_write("<jemalloc>: Error allocating" \ 355 " TSD for "#a_name"\n"); \ 356 abort(); \ 357 } \ 358 memcpy(wrapper, &a_name##tsd_boot_wrapper, \ 359 sizeof(a_name##tsd_wrapper_t)); \ 360 a_name##tsd_wrapper_set(wrapper); \ 361} \ 362a_attr bool \ 363a_name##tsd_boot(void) \ 364{ \ 365 \ 366 if (a_name##tsd_boot0()) \ 367 return (true); \ 368 a_name##tsd_boot1(); \ 369 return (false); \ 370} \ 371/* Get/set. */ \ 372a_attr a_type * \ 373a_name##tsd_get(void) \ 374{ \ 375 a_name##tsd_wrapper_t *wrapper; \ 376 \ 377 assert(a_name##tsd_booted); \ 378 wrapper = a_name##tsd_wrapper_get(); \ 379 return (&wrapper->val); \ 380} \ 381a_attr void \ 382a_name##tsd_set(a_type *val) \ 383{ \ 384 a_name##tsd_wrapper_t *wrapper; \ 385 \ 386 assert(a_name##tsd_booted); \ 387 wrapper = a_name##tsd_wrapper_get(); \ 388 wrapper->val = *(val); \ 389 if (a_cleanup != malloc_tsd_no_cleanup) \ 390 wrapper->initialized = true; \ 391} 392#else 393#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 394 a_cleanup) \ 395/* Initialization/cleanup. */ \ 396a_attr void \ 397a_name##tsd_cleanup_wrapper(void *arg) \ 398{ \ 399 a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)arg; \ 400 \ 401 if (a_cleanup != malloc_tsd_no_cleanup && \ 402 wrapper->initialized) { \ 403 wrapper->initialized = false; \ 404 a_cleanup(&wrapper->val); \ 405 if (wrapper->initialized) { \ 406 /* Trigger another cleanup round. */ \ 407 if (pthread_setspecific(a_name##tsd_tsd, \ 408 (void *)wrapper)) { \ 409 malloc_write("<jemalloc>: Error" \ 410 " setting TSD for "#a_name"\n"); \ 411 if (opt_abort) \ 412 abort(); \ 413 } \ 414 return; \ 415 } \ 416 } \ 417 malloc_tsd_dalloc(wrapper); \ 418} \ 419a_attr void \ 420a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper) \ 421{ \ 422 \ 423 if (pthread_setspecific(a_name##tsd_tsd, \ 424 (void *)wrapper)) { \ 425 malloc_write("<jemalloc>: Error setting" \ 426 " TSD for "#a_name"\n"); \ 427 abort(); \ 428 } \ 429} \ 430a_attr a_name##tsd_wrapper_t * \ 431a_name##tsd_wrapper_get(void) \ 432{ \ 433 a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \ 434 pthread_getspecific(a_name##tsd_tsd); \ 435 \ 436 if (unlikely(wrapper == NULL)) { \ 437 tsd_init_block_t block; \ 438 wrapper = tsd_init_check_recursion( \ 439 &a_name##tsd_init_head, &block); \ 440 if (wrapper) \ 441 return (wrapper); \ 442 wrapper = (a_name##tsd_wrapper_t *) \ 443 malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ 444 block.data = wrapper; \ 445 if (wrapper == NULL) { \ 446 malloc_write("<jemalloc>: Error allocating" \ 447 " TSD for "#a_name"\n"); \ 448 abort(); \ 449 } else { \ 450 wrapper->initialized = false; \ 451 wrapper->val = a_initializer; \ 452 } \ 453 a_name##tsd_wrapper_set(wrapper); \ 454 tsd_init_finish(&a_name##tsd_init_head, &block); \ 455 } \ 456 return (wrapper); \ 457} \ 458a_attr bool \ 459a_name##tsd_boot0(void) \ 460{ \ 461 \ 462 if (pthread_key_create(&a_name##tsd_tsd, \ 463 a_name##tsd_cleanup_wrapper) != 0) \ 464 return (true); \ 465 a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper); \ 466 a_name##tsd_booted = true; \ 467 return (false); \ 468} \ 469a_attr void \ 470a_name##tsd_boot1(void) \ 471{ \ 472 a_name##tsd_wrapper_t *wrapper; \ 473 wrapper = (a_name##tsd_wrapper_t *) \ 474 malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ 475 if (wrapper == NULL) { \ 476 malloc_write("<jemalloc>: Error allocating" \ 477 " TSD for "#a_name"\n"); \ 478 abort(); \ 479 } \ 480 memcpy(wrapper, &a_name##tsd_boot_wrapper, \ 481 sizeof(a_name##tsd_wrapper_t)); \ 482 a_name##tsd_wrapper_set(wrapper); \ 483} \ 484a_attr bool \ 485a_name##tsd_boot(void) \ 486{ \ 487 \ 488 if (a_name##tsd_boot0()) \ 489 return (true); \ 490 a_name##tsd_boot1(); \ 491 return (false); \ 492} \ 493/* Get/set. */ \ 494a_attr a_type * \ 495a_name##tsd_get(void) \ 496{ \ 497 a_name##tsd_wrapper_t *wrapper; \ 498 \ 499 assert(a_name##tsd_booted); \ 500 wrapper = a_name##tsd_wrapper_get(); \ 501 return (&wrapper->val); \ 502} \ 503a_attr void \ 504a_name##tsd_set(a_type *val) \ 505{ \ 506 a_name##tsd_wrapper_t *wrapper; \ 507 \ 508 assert(a_name##tsd_booted); \ 509 wrapper = a_name##tsd_wrapper_get(); \ 510 wrapper->val = *(val); \ 511 if (a_cleanup != malloc_tsd_no_cleanup) \ 512 wrapper->initialized = true; \ 513} 514#endif 515 516#endif /* JEMALLOC_H_TYPES */ 517/******************************************************************************/ 518#ifdef JEMALLOC_H_STRUCTS 519 520#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ 521 !defined(_WIN32)) 522struct tsd_init_block_s { 523 ql_elm(tsd_init_block_t) link; 524 pthread_t thread; 525 void *data; 526}; 527struct tsd_init_head_s { 528 ql_head(tsd_init_block_t) blocks; 529 malloc_mutex_t lock; 530}; 531#endif 532 533#define MALLOC_TSD \ 534/* O(name, type) */ \ 535 O(tcache, tcache_t *) \ 536 O(thread_allocated, uint64_t) \ 537 O(thread_deallocated, uint64_t) \ 538 O(prof_tdata, prof_tdata_t *) \ 539 O(arena, arena_t *) \ 540 O(arenas_cache, arena_t **) \ 541 O(narenas_cache, unsigned) \ 542 O(arenas_cache_bypass, bool) \ 543 O(tcache_enabled, tcache_enabled_t) \ 544 O(quarantine, quarantine_t *) \ 545 546#define TSD_INITIALIZER { \ 547 tsd_state_uninitialized, \ 548 NULL, \ 549 0, \ 550 0, \ 551 NULL, \ 552 NULL, \ 553 NULL, \ 554 0, \ 555 false, \ 556 tcache_enabled_default, \ 557 NULL \ 558} 559 560struct tsd_s { 561 tsd_state_t state; 562#define O(n, t) \ 563 t n; 564MALLOC_TSD 565#undef O 566}; 567 568static const tsd_t tsd_initializer = TSD_INITIALIZER; 569 570malloc_tsd_types(, tsd_t) 571 572#endif /* JEMALLOC_H_STRUCTS */ 573/******************************************************************************/ 574#ifdef JEMALLOC_H_EXTERNS 575 576void *malloc_tsd_malloc(size_t size); 577void malloc_tsd_dalloc(void *wrapper); 578void malloc_tsd_no_cleanup(void *arg); 579void malloc_tsd_cleanup_register(bool (*f)(void)); 580bool malloc_tsd_boot0(void); 581void malloc_tsd_boot1(void); 582#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ 583 !defined(_WIN32)) 584void *tsd_init_check_recursion(tsd_init_head_t *head, 585 tsd_init_block_t *block); 586void tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block); 587#endif 588void tsd_cleanup(void *arg); 589 590#endif /* JEMALLOC_H_EXTERNS */ 591/******************************************************************************/ 592#ifdef JEMALLOC_H_INLINES 593 594#ifndef JEMALLOC_ENABLE_INLINE 595malloc_tsd_protos(JEMALLOC_ATTR(unused), , tsd_t) 596 597tsd_t *tsd_fetch(void); 598bool tsd_nominal(tsd_t *tsd); 599#define O(n, t) \ 600t *tsd_##n##p_get(tsd_t *tsd); \ 601t tsd_##n##_get(tsd_t *tsd); \ 602void tsd_##n##_set(tsd_t *tsd, t n); 603MALLOC_TSD 604#undef O 605#endif 606 607#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TSD_C_)) 608malloc_tsd_externs(, tsd_t) 609malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, , tsd_t, tsd_initializer, tsd_cleanup) 610 611JEMALLOC_ALWAYS_INLINE tsd_t * 612tsd_fetch(void) 613{ 614 tsd_t *tsd = tsd_get(); 615 616 if (unlikely(tsd->state != tsd_state_nominal)) { 617 if (tsd->state == tsd_state_uninitialized) { 618 tsd->state = tsd_state_nominal; 619 /* Trigger cleanup handler registration. */ 620 tsd_set(tsd); 621 } else if (tsd->state == tsd_state_purgatory) { 622 tsd->state = tsd_state_reincarnated; 623 tsd_set(tsd); 624 } else 625 assert(tsd->state == tsd_state_reincarnated); 626 } 627 628 return (tsd); 629} 630 631JEMALLOC_INLINE bool 632tsd_nominal(tsd_t *tsd) 633{ 634 635 return (tsd->state == tsd_state_nominal); 636} 637 638#define O(n, t) \ 639JEMALLOC_ALWAYS_INLINE t * \ 640tsd_##n##p_get(tsd_t *tsd) \ 641{ \ 642 \ 643 return (&tsd->n); \ 644} \ 645 \ 646JEMALLOC_ALWAYS_INLINE t \ 647tsd_##n##_get(tsd_t *tsd) \ 648{ \ 649 \ 650 return (*tsd_##n##p_get(tsd)); \ 651} \ 652 \ 653JEMALLOC_ALWAYS_INLINE void \ 654tsd_##n##_set(tsd_t *tsd, t n) \ 655{ \ 656 \ 657 assert(tsd->state == tsd_state_nominal); \ 658 tsd->n = n; \ 659} 660MALLOC_TSD 661#undef O 662#endif 663 664#endif /* JEMALLOC_H_INLINES */ 665/******************************************************************************/ 666