1*8e33eff8Schristos #define JEMALLOC_TSD_C_ 2*8e33eff8Schristos #include "jemalloc/internal/jemalloc_preamble.h" 3*8e33eff8Schristos #include "jemalloc/internal/jemalloc_internal_includes.h" 4*8e33eff8Schristos 5*8e33eff8Schristos #include "jemalloc/internal/assert.h" 6*8e33eff8Schristos #include "jemalloc/internal/mutex.h" 7*8e33eff8Schristos #include "jemalloc/internal/rtree.h" 8*8e33eff8Schristos 9*8e33eff8Schristos /******************************************************************************/ 10*8e33eff8Schristos /* Data. */ 11*8e33eff8Schristos 12*8e33eff8Schristos static unsigned ncleanups; 13*8e33eff8Schristos static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX]; 14*8e33eff8Schristos 15*8e33eff8Schristos #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP 16*8e33eff8Schristos __thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER; 17*8e33eff8Schristos __thread bool JEMALLOC_TLS_MODEL tsd_initialized = false; 18*8e33eff8Schristos bool tsd_booted = false; 19*8e33eff8Schristos #elif (defined(JEMALLOC_TLS)) 20*8e33eff8Schristos __thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER; 21*8e33eff8Schristos pthread_key_t tsd_tsd; 22*8e33eff8Schristos bool tsd_booted = false; 23*8e33eff8Schristos #elif (defined(_WIN32)) 24*8e33eff8Schristos DWORD tsd_tsd; 25*8e33eff8Schristos tsd_wrapper_t tsd_boot_wrapper = {false, TSD_INITIALIZER}; 26*8e33eff8Schristos bool tsd_booted = false; 27*8e33eff8Schristos #else 28*8e33eff8Schristos 29*8e33eff8Schristos /* 30*8e33eff8Schristos * This contains a mutex, but it's pretty convenient to allow the mutex code to 31*8e33eff8Schristos * have a dependency on tsd. So we define the struct here, and only refer to it 32*8e33eff8Schristos * by pointer in the header. 33*8e33eff8Schristos */ 34*8e33eff8Schristos struct tsd_init_head_s { 35*8e33eff8Schristos ql_head(tsd_init_block_t) blocks; 36*8e33eff8Schristos malloc_mutex_t lock; 37*8e33eff8Schristos }; 38*8e33eff8Schristos 39*8e33eff8Schristos pthread_key_t tsd_tsd; 40*8e33eff8Schristos tsd_init_head_t tsd_init_head = { 41*8e33eff8Schristos ql_head_initializer(blocks), 42*8e33eff8Schristos #ifndef __lint__ 43*8e33eff8Schristos // XXX: broken lint 44*8e33eff8Schristos MALLOC_MUTEX_INITIALIZER 45*8e33eff8Schristos #endif 46*8e33eff8Schristos }; 47*8e33eff8Schristos tsd_wrapper_t tsd_boot_wrapper = { 48*8e33eff8Schristos false, 49*8e33eff8Schristos TSD_INITIALIZER 50*8e33eff8Schristos }; 51*8e33eff8Schristos bool tsd_booted = false; 52*8e33eff8Schristos #endif 53*8e33eff8Schristos 54*8e33eff8Schristos 55*8e33eff8Schristos /******************************************************************************/ 56*8e33eff8Schristos 57*8e33eff8Schristos void 58*8e33eff8Schristos tsd_slow_update(tsd_t *tsd) { 59*8e33eff8Schristos if (tsd_nominal(tsd)) { 60*8e33eff8Schristos if (malloc_slow || !tsd_tcache_enabled_get(tsd) || 61*8e33eff8Schristos tsd_reentrancy_level_get(tsd) > 0) { 62*8e33eff8Schristos tsd->state = tsd_state_nominal_slow; 63*8e33eff8Schristos } else { 64*8e33eff8Schristos tsd->state = tsd_state_nominal; 65*8e33eff8Schristos } 66*8e33eff8Schristos } 67*8e33eff8Schristos } 68*8e33eff8Schristos 69*8e33eff8Schristos static bool 70*8e33eff8Schristos tsd_data_init(tsd_t *tsd) { 71*8e33eff8Schristos /* 72*8e33eff8Schristos * We initialize the rtree context first (before the tcache), since the 73*8e33eff8Schristos * tcache initialization depends on it. 74*8e33eff8Schristos */ 75*8e33eff8Schristos rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd)); 76*8e33eff8Schristos 77*8e33eff8Schristos /* 78*8e33eff8Schristos * A nondeterministic seed based on the address of tsd reduces 79*8e33eff8Schristos * the likelihood of lockstep non-uniform cache index 80*8e33eff8Schristos * utilization among identical concurrent processes, but at the 81*8e33eff8Schristos * cost of test repeatability. For debug builds, instead use a 82*8e33eff8Schristos * deterministic seed. 83*8e33eff8Schristos */ 84*8e33eff8Schristos *tsd_offset_statep_get(tsd) = config_debug ? 0 : 85*8e33eff8Schristos (uint64_t)(uintptr_t)tsd; 86*8e33eff8Schristos 87*8e33eff8Schristos return tsd_tcache_enabled_data_init(tsd); 88*8e33eff8Schristos } 89*8e33eff8Schristos 90*8e33eff8Schristos static void 91*8e33eff8Schristos assert_tsd_data_cleanup_done(tsd_t *tsd) { 92*8e33eff8Schristos assert(!tsd_nominal(tsd)); 93*8e33eff8Schristos assert(*tsd_arenap_get_unsafe(tsd) == NULL); 94*8e33eff8Schristos assert(*tsd_iarenap_get_unsafe(tsd) == NULL); 95*8e33eff8Schristos assert(*tsd_arenas_tdata_bypassp_get_unsafe(tsd) == true); 96*8e33eff8Schristos assert(*tsd_arenas_tdatap_get_unsafe(tsd) == NULL); 97*8e33eff8Schristos assert(*tsd_tcache_enabledp_get_unsafe(tsd) == false); 98*8e33eff8Schristos assert(*tsd_prof_tdatap_get_unsafe(tsd) == NULL); 99*8e33eff8Schristos } 100*8e33eff8Schristos 101*8e33eff8Schristos static bool 102*8e33eff8Schristos tsd_data_init_nocleanup(tsd_t *tsd) { 103*8e33eff8Schristos assert(tsd->state == tsd_state_reincarnated || 104*8e33eff8Schristos tsd->state == tsd_state_minimal_initialized); 105*8e33eff8Schristos /* 106*8e33eff8Schristos * During reincarnation, there is no guarantee that the cleanup function 107*8e33eff8Schristos * will be called (deallocation may happen after all tsd destructors). 108*8e33eff8Schristos * We set up tsd in a way that no cleanup is needed. 109*8e33eff8Schristos */ 110*8e33eff8Schristos rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd)); 111*8e33eff8Schristos *tsd_arenas_tdata_bypassp_get(tsd) = true; 112*8e33eff8Schristos *tsd_tcache_enabledp_get_unsafe(tsd) = false; 113*8e33eff8Schristos *tsd_reentrancy_levelp_get(tsd) = 1; 114*8e33eff8Schristos assert_tsd_data_cleanup_done(tsd); 115*8e33eff8Schristos 116*8e33eff8Schristos return false; 117*8e33eff8Schristos } 118*8e33eff8Schristos 119*8e33eff8Schristos tsd_t * 120*8e33eff8Schristos tsd_fetch_slow(tsd_t *tsd, bool minimal) { 121*8e33eff8Schristos assert(!tsd_fast(tsd)); 122*8e33eff8Schristos 123*8e33eff8Schristos if (tsd->state == tsd_state_nominal_slow) { 124*8e33eff8Schristos /* On slow path but no work needed. */ 125*8e33eff8Schristos assert(malloc_slow || !tsd_tcache_enabled_get(tsd) || 126*8e33eff8Schristos tsd_reentrancy_level_get(tsd) > 0 || 127*8e33eff8Schristos *tsd_arenas_tdata_bypassp_get(tsd)); 128*8e33eff8Schristos } else if (tsd->state == tsd_state_uninitialized) { 129*8e33eff8Schristos if (!minimal) { 130*8e33eff8Schristos tsd->state = tsd_state_nominal; 131*8e33eff8Schristos tsd_slow_update(tsd); 132*8e33eff8Schristos /* Trigger cleanup handler registration. */ 133*8e33eff8Schristos tsd_set(tsd); 134*8e33eff8Schristos tsd_data_init(tsd); 135*8e33eff8Schristos } else { 136*8e33eff8Schristos tsd->state = tsd_state_minimal_initialized; 137*8e33eff8Schristos tsd_set(tsd); 138*8e33eff8Schristos tsd_data_init_nocleanup(tsd); 139*8e33eff8Schristos } 140*8e33eff8Schristos } else if (tsd->state == tsd_state_minimal_initialized) { 141*8e33eff8Schristos if (!minimal) { 142*8e33eff8Schristos /* Switch to fully initialized. */ 143*8e33eff8Schristos tsd->state = tsd_state_nominal; 144*8e33eff8Schristos assert(*tsd_reentrancy_levelp_get(tsd) >= 1); 145*8e33eff8Schristos (*tsd_reentrancy_levelp_get(tsd))--; 146*8e33eff8Schristos tsd_slow_update(tsd); 147*8e33eff8Schristos tsd_data_init(tsd); 148*8e33eff8Schristos } else { 149*8e33eff8Schristos assert_tsd_data_cleanup_done(tsd); 150*8e33eff8Schristos } 151*8e33eff8Schristos } else if (tsd->state == tsd_state_purgatory) { 152*8e33eff8Schristos tsd->state = tsd_state_reincarnated; 153*8e33eff8Schristos tsd_set(tsd); 154*8e33eff8Schristos tsd_data_init_nocleanup(tsd); 155*8e33eff8Schristos } else { 156*8e33eff8Schristos assert(tsd->state == tsd_state_reincarnated); 157*8e33eff8Schristos } 158*8e33eff8Schristos 159*8e33eff8Schristos return tsd; 160*8e33eff8Schristos } 161*8e33eff8Schristos 162*8e33eff8Schristos void * 163*8e33eff8Schristos malloc_tsd_malloc(size_t size) { 164*8e33eff8Schristos return a0malloc(CACHELINE_CEILING(size)); 165*8e33eff8Schristos } 166*8e33eff8Schristos 167*8e33eff8Schristos void 168*8e33eff8Schristos malloc_tsd_dalloc(void *wrapper) { 169*8e33eff8Schristos a0dalloc(wrapper); 170*8e33eff8Schristos } 171*8e33eff8Schristos 172*8e33eff8Schristos __BEGIN_DECLS 173*8e33eff8Schristos void _malloc_thread_cleanup(void); 174*8e33eff8Schristos __END_DECLS 175*8e33eff8Schristos 176*8e33eff8Schristos #if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32) 177*8e33eff8Schristos #ifndef _WIN32 178*8e33eff8Schristos JEMALLOC_EXPORT 179*8e33eff8Schristos #endif 180*8e33eff8Schristos void 181*8e33eff8Schristos _malloc_thread_cleanup(void) { 182*8e33eff8Schristos bool pending[MALLOC_TSD_CLEANUPS_MAX], again; 183*8e33eff8Schristos unsigned i; 184*8e33eff8Schristos 185*8e33eff8Schristos for (i = 0; i < ncleanups; i++) { 186*8e33eff8Schristos pending[i] = true; 187*8e33eff8Schristos } 188*8e33eff8Schristos 189*8e33eff8Schristos do { 190*8e33eff8Schristos again = false; 191*8e33eff8Schristos for (i = 0; i < ncleanups; i++) { 192*8e33eff8Schristos if (pending[i]) { 193*8e33eff8Schristos pending[i] = cleanups[i](); 194*8e33eff8Schristos if (pending[i]) { 195*8e33eff8Schristos again = true; 196*8e33eff8Schristos } 197*8e33eff8Schristos } 198*8e33eff8Schristos } 199*8e33eff8Schristos } while (again); 200*8e33eff8Schristos } 201*8e33eff8Schristos #endif 202*8e33eff8Schristos 203*8e33eff8Schristos void 204*8e33eff8Schristos malloc_tsd_cleanup_register(bool (*f)(void)) { 205*8e33eff8Schristos assert(ncleanups < MALLOC_TSD_CLEANUPS_MAX); 206*8e33eff8Schristos cleanups[ncleanups] = f; 207*8e33eff8Schristos ncleanups++; 208*8e33eff8Schristos } 209*8e33eff8Schristos 210*8e33eff8Schristos static void 211*8e33eff8Schristos tsd_do_data_cleanup(tsd_t *tsd) { 212*8e33eff8Schristos prof_tdata_cleanup(tsd); 213*8e33eff8Schristos iarena_cleanup(tsd); 214*8e33eff8Schristos arena_cleanup(tsd); 215*8e33eff8Schristos arenas_tdata_cleanup(tsd); 216*8e33eff8Schristos tcache_cleanup(tsd); 217*8e33eff8Schristos witnesses_cleanup(tsd_witness_tsdp_get_unsafe(tsd)); 218*8e33eff8Schristos } 219*8e33eff8Schristos 220*8e33eff8Schristos void 221*8e33eff8Schristos tsd_cleanup(void *arg) { 222*8e33eff8Schristos tsd_t *tsd = (tsd_t *)arg; 223*8e33eff8Schristos 224*8e33eff8Schristos switch (tsd->state) { 225*8e33eff8Schristos case tsd_state_uninitialized: 226*8e33eff8Schristos /* Do nothing. */ 227*8e33eff8Schristos break; 228*8e33eff8Schristos case tsd_state_minimal_initialized: 229*8e33eff8Schristos /* This implies the thread only did free() in its life time. */ 230*8e33eff8Schristos /* Fall through. */ 231*8e33eff8Schristos case tsd_state_reincarnated: 232*8e33eff8Schristos /* 233*8e33eff8Schristos * Reincarnated means another destructor deallocated memory 234*8e33eff8Schristos * after the destructor was called. Cleanup isn't required but 235*8e33eff8Schristos * is still called for testing and completeness. 236*8e33eff8Schristos */ 237*8e33eff8Schristos assert_tsd_data_cleanup_done(tsd); 238*8e33eff8Schristos /* Fall through. */ 239*8e33eff8Schristos case tsd_state_nominal: 240*8e33eff8Schristos case tsd_state_nominal_slow: 241*8e33eff8Schristos tsd_do_data_cleanup(tsd); 242*8e33eff8Schristos tsd->state = tsd_state_purgatory; 243*8e33eff8Schristos tsd_set(tsd); 244*8e33eff8Schristos break; 245*8e33eff8Schristos case tsd_state_purgatory: 246*8e33eff8Schristos /* 247*8e33eff8Schristos * The previous time this destructor was called, we set the 248*8e33eff8Schristos * state to tsd_state_purgatory so that other destructors 249*8e33eff8Schristos * wouldn't cause re-creation of the tsd. This time, do 250*8e33eff8Schristos * nothing, and do not request another callback. 251*8e33eff8Schristos */ 252*8e33eff8Schristos break; 253*8e33eff8Schristos default: 254*8e33eff8Schristos not_reached(); 255*8e33eff8Schristos } 256*8e33eff8Schristos #ifdef JEMALLOC_JET 257*8e33eff8Schristos test_callback_t test_callback = *tsd_test_callbackp_get_unsafe(tsd); 258*8e33eff8Schristos int *data = tsd_test_datap_get_unsafe(tsd); 259*8e33eff8Schristos if (test_callback != NULL) { 260*8e33eff8Schristos test_callback(data); 261*8e33eff8Schristos } 262*8e33eff8Schristos #endif 263*8e33eff8Schristos } 264*8e33eff8Schristos 265*8e33eff8Schristos tsd_t * 266*8e33eff8Schristos malloc_tsd_boot0(void) { 267*8e33eff8Schristos tsd_t *tsd; 268*8e33eff8Schristos 269*8e33eff8Schristos ncleanups = 0; 270*8e33eff8Schristos if (tsd_boot0()) { 271*8e33eff8Schristos return NULL; 272*8e33eff8Schristos } 273*8e33eff8Schristos tsd = tsd_fetch(); 274*8e33eff8Schristos *tsd_arenas_tdata_bypassp_get(tsd) = true; 275*8e33eff8Schristos return tsd; 276*8e33eff8Schristos } 277*8e33eff8Schristos 278*8e33eff8Schristos void 279*8e33eff8Schristos malloc_tsd_boot1(void) { 280*8e33eff8Schristos tsd_boot1(); 281*8e33eff8Schristos tsd_t *tsd = tsd_fetch(); 282*8e33eff8Schristos /* malloc_slow has been set properly. Update tsd_slow. */ 283*8e33eff8Schristos tsd_slow_update(tsd); 284*8e33eff8Schristos *tsd_arenas_tdata_bypassp_get(tsd) = false; 285*8e33eff8Schristos } 286*8e33eff8Schristos 287*8e33eff8Schristos #ifdef _WIN32 288*8e33eff8Schristos static BOOL WINAPI 289*8e33eff8Schristos _tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { 290*8e33eff8Schristos switch (fdwReason) { 291*8e33eff8Schristos #ifdef JEMALLOC_LAZY_LOCK 292*8e33eff8Schristos case DLL_THREAD_ATTACH: 293*8e33eff8Schristos isthreaded = true; 294*8e33eff8Schristos break; 295*8e33eff8Schristos #endif 296*8e33eff8Schristos case DLL_THREAD_DETACH: 297*8e33eff8Schristos _malloc_thread_cleanup(); 298*8e33eff8Schristos break; 299*8e33eff8Schristos default: 300*8e33eff8Schristos break; 301*8e33eff8Schristos } 302*8e33eff8Schristos return true; 303*8e33eff8Schristos } 304*8e33eff8Schristos 305*8e33eff8Schristos /* 306*8e33eff8Schristos * We need to be able to say "read" here (in the "pragma section"), but have 307*8e33eff8Schristos * hooked "read". We won't read for the rest of the file, so we can get away 308*8e33eff8Schristos * with unhooking. 309*8e33eff8Schristos */ 310*8e33eff8Schristos #ifdef read 311*8e33eff8Schristos # undef read 312*8e33eff8Schristos #endif 313*8e33eff8Schristos 314*8e33eff8Schristos #ifdef _MSC_VER 315*8e33eff8Schristos # ifdef _M_IX86 316*8e33eff8Schristos # pragma comment(linker, "/INCLUDE:__tls_used") 317*8e33eff8Schristos # pragma comment(linker, "/INCLUDE:_tls_callback") 318*8e33eff8Schristos # else 319*8e33eff8Schristos # pragma comment(linker, "/INCLUDE:_tls_used") 320*8e33eff8Schristos # pragma comment(linker, "/INCLUDE:tls_callback") 321*8e33eff8Schristos # endif 322*8e33eff8Schristos # pragma section(".CRT$XLY",long,read) 323*8e33eff8Schristos #endif 324*8e33eff8Schristos JEMALLOC_SECTION(".CRT$XLY") JEMALLOC_ATTR(used) 325*8e33eff8Schristos BOOL (WINAPI *const tls_callback)(HINSTANCE hinstDLL, 326*8e33eff8Schristos DWORD fdwReason, LPVOID lpvReserved) = _tls_callback; 327*8e33eff8Schristos #endif 328*8e33eff8Schristos 329*8e33eff8Schristos #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ 330*8e33eff8Schristos !defined(_WIN32)) 331*8e33eff8Schristos void * 332*8e33eff8Schristos tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block) { 333*8e33eff8Schristos pthread_t self = pthread_self(); 334*8e33eff8Schristos tsd_init_block_t *iter; 335*8e33eff8Schristos 336*8e33eff8Schristos /* Check whether this thread has already inserted into the list. */ 337*8e33eff8Schristos malloc_mutex_lock(TSDN_NULL, &head->lock); 338*8e33eff8Schristos ql_foreach(iter, &head->blocks, link) { 339*8e33eff8Schristos if (iter->thread == self) { 340*8e33eff8Schristos malloc_mutex_unlock(TSDN_NULL, &head->lock); 341*8e33eff8Schristos return iter->data; 342*8e33eff8Schristos } 343*8e33eff8Schristos } 344*8e33eff8Schristos /* Insert block into list. */ 345*8e33eff8Schristos ql_elm_new(block, link); 346*8e33eff8Schristos block->thread = self; 347*8e33eff8Schristos ql_tail_insert(&head->blocks, block, link); 348*8e33eff8Schristos malloc_mutex_unlock(TSDN_NULL, &head->lock); 349*8e33eff8Schristos return NULL; 350*8e33eff8Schristos } 351*8e33eff8Schristos 352*8e33eff8Schristos void 353*8e33eff8Schristos tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) { 354*8e33eff8Schristos malloc_mutex_lock(TSDN_NULL, &head->lock); 355*8e33eff8Schristos ql_remove(&head->blocks, block, link); 356*8e33eff8Schristos malloc_mutex_unlock(TSDN_NULL, &head->lock); 357*8e33eff8Schristos } 358*8e33eff8Schristos #endif 359