tsd.h revision 235238
1234370Sjasone/******************************************************************************/ 2234370Sjasone#ifdef JEMALLOC_H_TYPES 3234370Sjasone 4234370Sjasone/* Maximum number of malloc_tsd users with cleanup functions. */ 5234370Sjasone#define MALLOC_TSD_CLEANUPS_MAX 8 6234370Sjasone 7234543Sjasonetypedef bool (*malloc_tsd_cleanup_t)(void); 8234370Sjasone 9234370Sjasone/* 10234370Sjasone * TLS/TSD-agnostic macro-based implementation of thread-specific data. There 11234370Sjasone * are four macros that support (at least) three use cases: file-private, 12234370Sjasone * library-private, and library-private inlined. Following is an example 13234370Sjasone * library-private tsd variable: 14234370Sjasone * 15234370Sjasone * In example.h: 16234370Sjasone * typedef struct { 17234370Sjasone * int x; 18234370Sjasone * int y; 19234370Sjasone * } example_t; 20234370Sjasone * #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0}) 21234370Sjasone * malloc_tsd_protos(, example, example_t *) 22234370Sjasone * malloc_tsd_externs(example, example_t *) 23234370Sjasone * In example.c: 24234370Sjasone * malloc_tsd_data(, example, example_t *, EX_INITIALIZER) 25234370Sjasone * malloc_tsd_funcs(, example, example_t *, EX_INITIALIZER, 26234370Sjasone * example_tsd_cleanup) 27234370Sjasone * 28234370Sjasone * The result is a set of generated functions, e.g.: 29234370Sjasone * 30234370Sjasone * bool example_tsd_boot(void) {...} 31234370Sjasone * example_t **example_tsd_get() {...} 32234370Sjasone * void example_tsd_set(example_t **val) {...} 33234370Sjasone * 34234370Sjasone * Note that all of the functions deal in terms of (a_type *) rather than 35234370Sjasone * (a_type) so that it is possible to support non-pointer types (unlike 36234370Sjasone * pthreads TSD). example_tsd_cleanup() is passed an (a_type *) pointer that is 37234370Sjasone * cast to (void *). This means that the cleanup function needs to cast *and* 38234370Sjasone * dereference the function argument, e.g.: 39234370Sjasone * 40234370Sjasone * void 41234370Sjasone * example_tsd_cleanup(void *arg) 42234370Sjasone * { 43234370Sjasone * example_t *example = *(example_t **)arg; 44234370Sjasone * 45234370Sjasone * [...] 46234370Sjasone * if ([want the cleanup function to be called again]) { 47234370Sjasone * example_tsd_set(&example); 48234370Sjasone * } 49234370Sjasone * } 50234370Sjasone * 51234370Sjasone * If example_tsd_set() is called within example_tsd_cleanup(), it will be 52234370Sjasone * called again. This is similar to how pthreads TSD destruction works, except 53234370Sjasone * that pthreads only calls the cleanup function again if the value was set to 54234370Sjasone * non-NULL. 55234370Sjasone */ 56234370Sjasone 57234370Sjasone/* malloc_tsd_protos(). */ 58234370Sjasone#define malloc_tsd_protos(a_attr, a_name, a_type) \ 59234370Sjasonea_attr bool \ 60234370Sjasonea_name##_tsd_boot(void); \ 61234370Sjasonea_attr a_type * \ 62234370Sjasonea_name##_tsd_get(void); \ 63234370Sjasonea_attr void \ 64234370Sjasonea_name##_tsd_set(a_type *val); 65234370Sjasone 66234370Sjasone/* malloc_tsd_externs(). */ 67234370Sjasone#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 68234370Sjasone#define malloc_tsd_externs(a_name, a_type) \ 69234370Sjasoneextern __thread a_type a_name##_tls; \ 70234370Sjasoneextern __thread bool a_name##_initialized; \ 71234370Sjasoneextern bool a_name##_booted; 72234370Sjasone#elif (defined(JEMALLOC_TLS)) 73234370Sjasone#define malloc_tsd_externs(a_name, a_type) \ 74234370Sjasoneextern __thread a_type a_name##_tls; \ 75234370Sjasoneextern pthread_key_t a_name##_tsd; \ 76234370Sjasoneextern bool a_name##_booted; 77235238Sjasone#elif (defined(_WIN32)) 78235238Sjasone#define malloc_tsd_externs(a_name, a_type) \ 79235238Sjasoneextern DWORD a_name##_tsd; \ 80235238Sjasoneextern bool a_name##_booted; 81234370Sjasone#else 82234370Sjasone#define malloc_tsd_externs(a_name, a_type) \ 83234370Sjasoneextern pthread_key_t a_name##_tsd; \ 84234370Sjasoneextern bool a_name##_booted; 85234370Sjasone#endif 86234370Sjasone 87234370Sjasone/* malloc_tsd_data(). */ 88234370Sjasone#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 89234370Sjasone#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 90234370Sjasonea_attr __thread a_type JEMALLOC_TLS_MODEL \ 91234370Sjasone a_name##_tls = a_initializer; \ 92234370Sjasonea_attr __thread bool JEMALLOC_TLS_MODEL \ 93234370Sjasone a_name##_initialized = false; \ 94234370Sjasonea_attr bool a_name##_booted = false; 95234370Sjasone#elif (defined(JEMALLOC_TLS)) 96234370Sjasone#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 97234370Sjasonea_attr __thread a_type JEMALLOC_TLS_MODEL \ 98234370Sjasone a_name##_tls = a_initializer; \ 99234370Sjasonea_attr pthread_key_t a_name##_tsd; \ 100234370Sjasonea_attr bool a_name##_booted = false; 101235238Sjasone#elif (defined(_WIN32)) 102235238Sjasone#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 103235238Sjasonea_attr DWORD a_name##_tsd; \ 104235238Sjasonea_attr bool a_name##_booted = false; 105234370Sjasone#else 106234370Sjasone#define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ 107234370Sjasonea_attr pthread_key_t a_name##_tsd; \ 108234370Sjasonea_attr bool a_name##_booted = false; 109234370Sjasone#endif 110234370Sjasone 111234370Sjasone/* malloc_tsd_funcs(). */ 112234370Sjasone#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 113234370Sjasone#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 114234370Sjasone a_cleanup) \ 115234370Sjasone/* Initialization/cleanup. */ \ 116234370Sjasonea_attr bool \ 117234543Sjasonea_name##_tsd_cleanup_wrapper(void) \ 118234370Sjasone{ \ 119234370Sjasone \ 120234370Sjasone if (a_name##_initialized) { \ 121234370Sjasone a_name##_initialized = false; \ 122234569Sjasone a_cleanup(&a_name##_tls); \ 123234370Sjasone } \ 124234370Sjasone return (a_name##_initialized); \ 125234370Sjasone} \ 126234370Sjasonea_attr bool \ 127234370Sjasonea_name##_tsd_boot(void) \ 128234370Sjasone{ \ 129234370Sjasone \ 130234370Sjasone if (a_cleanup != malloc_tsd_no_cleanup) { \ 131234370Sjasone malloc_tsd_cleanup_register( \ 132234543Sjasone &a_name##_tsd_cleanup_wrapper); \ 133234370Sjasone } \ 134234370Sjasone a_name##_booted = true; \ 135234370Sjasone return (false); \ 136234370Sjasone} \ 137234370Sjasone/* Get/set. */ \ 138234370Sjasonea_attr a_type * \ 139234370Sjasonea_name##_tsd_get(void) \ 140234370Sjasone{ \ 141234370Sjasone \ 142234370Sjasone assert(a_name##_booted); \ 143234370Sjasone return (&a_name##_tls); \ 144234370Sjasone} \ 145234370Sjasonea_attr void \ 146234370Sjasonea_name##_tsd_set(a_type *val) \ 147234370Sjasone{ \ 148234370Sjasone \ 149234370Sjasone assert(a_name##_booted); \ 150234370Sjasone a_name##_tls = (*val); \ 151234370Sjasone if (a_cleanup != malloc_tsd_no_cleanup) \ 152234370Sjasone a_name##_initialized = true; \ 153234370Sjasone} 154234370Sjasone#elif (defined(JEMALLOC_TLS)) 155234370Sjasone#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 156234370Sjasone a_cleanup) \ 157234370Sjasone/* Initialization/cleanup. */ \ 158234370Sjasonea_attr bool \ 159234370Sjasonea_name##_tsd_boot(void) \ 160234370Sjasone{ \ 161234370Sjasone \ 162234370Sjasone if (a_cleanup != malloc_tsd_no_cleanup) { \ 163234370Sjasone if (pthread_key_create(&a_name##_tsd, a_cleanup) != 0) \ 164234370Sjasone return (true); \ 165234370Sjasone } \ 166234370Sjasone a_name##_booted = true; \ 167234370Sjasone return (false); \ 168234370Sjasone} \ 169234370Sjasone/* Get/set. */ \ 170234370Sjasonea_attr a_type * \ 171234370Sjasonea_name##_tsd_get(void) \ 172234370Sjasone{ \ 173234370Sjasone \ 174234370Sjasone assert(a_name##_booted); \ 175234370Sjasone return (&a_name##_tls); \ 176234370Sjasone} \ 177234370Sjasonea_attr void \ 178234370Sjasonea_name##_tsd_set(a_type *val) \ 179234370Sjasone{ \ 180234370Sjasone \ 181234370Sjasone assert(a_name##_booted); \ 182234370Sjasone a_name##_tls = (*val); \ 183234370Sjasone if (a_cleanup != malloc_tsd_no_cleanup) { \ 184234370Sjasone if (pthread_setspecific(a_name##_tsd, \ 185234370Sjasone (void *)(&a_name##_tls))) { \ 186234370Sjasone malloc_write("<jemalloc>: Error" \ 187234370Sjasone " setting TSD for "#a_name"\n"); \ 188234370Sjasone if (opt_abort) \ 189234370Sjasone abort(); \ 190234370Sjasone } \ 191234370Sjasone } \ 192234370Sjasone} 193235238Sjasone#elif (defined(_WIN32)) 194235238Sjasone#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 195235238Sjasone a_cleanup) \ 196235238Sjasone/* Data structure. */ \ 197235238Sjasonetypedef struct { \ 198235238Sjasone bool initialized; \ 199235238Sjasone a_type val; \ 200235238Sjasone} a_name##_tsd_wrapper_t; \ 201235238Sjasone/* Initialization/cleanup. */ \ 202235238Sjasonea_attr bool \ 203235238Sjasonea_name##_tsd_cleanup_wrapper(void) \ 204235238Sjasone{ \ 205235238Sjasone a_name##_tsd_wrapper_t *wrapper; \ 206235238Sjasone \ 207235238Sjasone wrapper = (a_name##_tsd_wrapper_t *) TlsGetValue(a_name##_tsd); \ 208235238Sjasone if (wrapper == NULL) \ 209235238Sjasone return (false); \ 210235238Sjasone if (a_cleanup != malloc_tsd_no_cleanup && \ 211235238Sjasone wrapper->initialized) { \ 212235238Sjasone a_type val = wrapper->val; \ 213235238Sjasone a_type tsd_static_data = a_initializer; \ 214235238Sjasone wrapper->initialized = false; \ 215235238Sjasone wrapper->val = tsd_static_data; \ 216235238Sjasone a_cleanup(&val); \ 217235238Sjasone if (wrapper->initialized) { \ 218235238Sjasone /* Trigger another cleanup round. */ \ 219235238Sjasone return (true); \ 220235238Sjasone } \ 221235238Sjasone } \ 222235238Sjasone malloc_tsd_dalloc(wrapper); \ 223235238Sjasone return (false); \ 224235238Sjasone} \ 225235238Sjasonea_attr bool \ 226235238Sjasonea_name##_tsd_boot(void) \ 227235238Sjasone{ \ 228235238Sjasone \ 229235238Sjasone a_name##_tsd = TlsAlloc(); \ 230235238Sjasone if (a_name##_tsd == TLS_OUT_OF_INDEXES) \ 231235238Sjasone return (true); \ 232235238Sjasone if (a_cleanup != malloc_tsd_no_cleanup) { \ 233235238Sjasone malloc_tsd_cleanup_register( \ 234235238Sjasone &a_name##_tsd_cleanup_wrapper); \ 235235238Sjasone } \ 236235238Sjasone a_name##_booted = true; \ 237235238Sjasone return (false); \ 238235238Sjasone} \ 239235238Sjasone/* Get/set. */ \ 240235238Sjasonea_attr a_name##_tsd_wrapper_t * \ 241235238Sjasonea_name##_tsd_get_wrapper(void) \ 242235238Sjasone{ \ 243235238Sjasone a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *) \ 244235238Sjasone TlsGetValue(a_name##_tsd); \ 245235238Sjasone \ 246235238Sjasone if (wrapper == NULL) { \ 247235238Sjasone wrapper = (a_name##_tsd_wrapper_t *) \ 248235238Sjasone malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ 249235238Sjasone if (wrapper == NULL) { \ 250235238Sjasone malloc_write("<jemalloc>: Error allocating" \ 251235238Sjasone " TSD for "#a_name"\n"); \ 252235238Sjasone abort(); \ 253235238Sjasone } else { \ 254235238Sjasone static a_type tsd_static_data = a_initializer; \ 255235238Sjasone wrapper->initialized = false; \ 256235238Sjasone wrapper->val = tsd_static_data; \ 257235238Sjasone } \ 258235238Sjasone if (!TlsSetValue(a_name##_tsd, (void *)wrapper)) { \ 259235238Sjasone malloc_write("<jemalloc>: Error setting" \ 260235238Sjasone " TSD for "#a_name"\n"); \ 261235238Sjasone abort(); \ 262235238Sjasone } \ 263235238Sjasone } \ 264235238Sjasone return (wrapper); \ 265235238Sjasone} \ 266235238Sjasonea_attr a_type * \ 267235238Sjasonea_name##_tsd_get(void) \ 268235238Sjasone{ \ 269235238Sjasone a_name##_tsd_wrapper_t *wrapper; \ 270235238Sjasone \ 271235238Sjasone assert(a_name##_booted); \ 272235238Sjasone wrapper = a_name##_tsd_get_wrapper(); \ 273235238Sjasone return (&wrapper->val); \ 274235238Sjasone} \ 275235238Sjasonea_attr void \ 276235238Sjasonea_name##_tsd_set(a_type *val) \ 277235238Sjasone{ \ 278235238Sjasone a_name##_tsd_wrapper_t *wrapper; \ 279235238Sjasone \ 280235238Sjasone assert(a_name##_booted); \ 281235238Sjasone wrapper = a_name##_tsd_get_wrapper(); \ 282235238Sjasone wrapper->val = *(val); \ 283235238Sjasone if (a_cleanup != malloc_tsd_no_cleanup) \ 284235238Sjasone wrapper->initialized = true; \ 285235238Sjasone} 286234370Sjasone#else 287234370Sjasone#define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ 288234370Sjasone a_cleanup) \ 289234370Sjasone/* Data structure. */ \ 290234370Sjasonetypedef struct { \ 291234370Sjasone bool initialized; \ 292234370Sjasone a_type val; \ 293234370Sjasone} a_name##_tsd_wrapper_t; \ 294234370Sjasone/* Initialization/cleanup. */ \ 295234370Sjasonea_attr void \ 296234370Sjasonea_name##_tsd_cleanup_wrapper(void *arg) \ 297234370Sjasone{ \ 298234370Sjasone a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\ 299234370Sjasone \ 300234370Sjasone if (a_cleanup != malloc_tsd_no_cleanup && \ 301234370Sjasone wrapper->initialized) { \ 302234370Sjasone wrapper->initialized = false; \ 303234370Sjasone a_cleanup(&wrapper->val); \ 304234370Sjasone if (wrapper->initialized) { \ 305234370Sjasone /* Trigger another cleanup round. */ \ 306234370Sjasone if (pthread_setspecific(a_name##_tsd, \ 307234370Sjasone (void *)wrapper)) { \ 308234370Sjasone malloc_write("<jemalloc>: Error" \ 309234370Sjasone " setting TSD for "#a_name"\n"); \ 310234370Sjasone if (opt_abort) \ 311234370Sjasone abort(); \ 312234370Sjasone } \ 313234370Sjasone return; \ 314234370Sjasone } \ 315234370Sjasone } \ 316234543Sjasone malloc_tsd_dalloc(wrapper); \ 317234370Sjasone} \ 318234370Sjasonea_attr bool \ 319234370Sjasonea_name##_tsd_boot(void) \ 320234370Sjasone{ \ 321234370Sjasone \ 322234370Sjasone if (pthread_key_create(&a_name##_tsd, \ 323234370Sjasone a_name##_tsd_cleanup_wrapper) != 0) \ 324234370Sjasone return (true); \ 325234370Sjasone a_name##_booted = true; \ 326234370Sjasone return (false); \ 327234370Sjasone} \ 328234370Sjasone/* Get/set. */ \ 329234370Sjasonea_attr a_name##_tsd_wrapper_t * \ 330234370Sjasonea_name##_tsd_get_wrapper(void) \ 331234370Sjasone{ \ 332234370Sjasone a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *) \ 333234370Sjasone pthread_getspecific(a_name##_tsd); \ 334234370Sjasone \ 335234370Sjasone if (wrapper == NULL) { \ 336234370Sjasone wrapper = (a_name##_tsd_wrapper_t *) \ 337234370Sjasone malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ 338234370Sjasone if (wrapper == NULL) { \ 339234370Sjasone malloc_write("<jemalloc>: Error allocating" \ 340234370Sjasone " TSD for "#a_name"\n"); \ 341234543Sjasone abort(); \ 342234370Sjasone } else { \ 343234370Sjasone static a_type tsd_static_data = a_initializer; \ 344234543Sjasone wrapper->initialized = false; \ 345234370Sjasone wrapper->val = tsd_static_data; \ 346234370Sjasone } \ 347234370Sjasone if (pthread_setspecific(a_name##_tsd, \ 348234370Sjasone (void *)wrapper)) { \ 349234370Sjasone malloc_write("<jemalloc>: Error setting" \ 350234370Sjasone " TSD for "#a_name"\n"); \ 351234543Sjasone abort(); \ 352234370Sjasone } \ 353234370Sjasone } \ 354234370Sjasone return (wrapper); \ 355234370Sjasone} \ 356234370Sjasonea_attr a_type * \ 357234370Sjasonea_name##_tsd_get(void) \ 358234370Sjasone{ \ 359234370Sjasone a_name##_tsd_wrapper_t *wrapper; \ 360234370Sjasone \ 361234370Sjasone assert(a_name##_booted); \ 362234370Sjasone wrapper = a_name##_tsd_get_wrapper(); \ 363234370Sjasone return (&wrapper->val); \ 364234370Sjasone} \ 365234370Sjasonea_attr void \ 366234370Sjasonea_name##_tsd_set(a_type *val) \ 367234370Sjasone{ \ 368234370Sjasone a_name##_tsd_wrapper_t *wrapper; \ 369234370Sjasone \ 370234370Sjasone assert(a_name##_booted); \ 371234370Sjasone wrapper = a_name##_tsd_get_wrapper(); \ 372234370Sjasone wrapper->val = *(val); \ 373234370Sjasone if (a_cleanup != malloc_tsd_no_cleanup) \ 374234370Sjasone wrapper->initialized = true; \ 375234370Sjasone} 376234370Sjasone#endif 377234370Sjasone 378234370Sjasone#endif /* JEMALLOC_H_TYPES */ 379234370Sjasone/******************************************************************************/ 380234370Sjasone#ifdef JEMALLOC_H_STRUCTS 381234370Sjasone 382234370Sjasone#endif /* JEMALLOC_H_STRUCTS */ 383234370Sjasone/******************************************************************************/ 384234370Sjasone#ifdef JEMALLOC_H_EXTERNS 385234370Sjasone 386234370Sjasonevoid *malloc_tsd_malloc(size_t size); 387234370Sjasonevoid malloc_tsd_dalloc(void *wrapper); 388234370Sjasonevoid malloc_tsd_no_cleanup(void *); 389234543Sjasonevoid malloc_tsd_cleanup_register(bool (*f)(void)); 390234370Sjasonevoid malloc_tsd_boot(void); 391234370Sjasone 392234370Sjasone#endif /* JEMALLOC_H_EXTERNS */ 393234370Sjasone/******************************************************************************/ 394234370Sjasone#ifdef JEMALLOC_H_INLINES 395234370Sjasone 396234370Sjasone#endif /* JEMALLOC_H_INLINES */ 397234370Sjasone/******************************************************************************/ 398