1a4bd5210SJason Evans #define JEMALLOC_TCACHE_C_ 2a4bd5210SJason Evans #include "jemalloc/internal/jemalloc_internal.h" 3a4bd5210SJason Evans 4a4bd5210SJason Evans /******************************************************************************/ 5a4bd5210SJason Evans /* Data. */ 6a4bd5210SJason Evans 7a4bd5210SJason Evans malloc_tsd_data(, tcache, tcache_t *, NULL) 8a4bd5210SJason Evans malloc_tsd_data(, tcache_enabled, tcache_enabled_t, tcache_enabled_default) 9a4bd5210SJason Evans 10a4bd5210SJason Evans bool opt_tcache = true; 11a4bd5210SJason Evans ssize_t opt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT; 12a4bd5210SJason Evans 13a4bd5210SJason Evans tcache_bin_info_t *tcache_bin_info; 14a4bd5210SJason Evans static unsigned stack_nelms; /* Total stack elms per tcache. */ 15a4bd5210SJason Evans 16a4bd5210SJason Evans size_t nhbins; 17a4bd5210SJason Evans size_t tcache_maxclass; 18a4bd5210SJason Evans 19a4bd5210SJason Evans /******************************************************************************/ 20a4bd5210SJason Evans 218ed34ab0SJason Evans size_t tcache_salloc(const void *ptr) 228ed34ab0SJason Evans { 238ed34ab0SJason Evans 248ed34ab0SJason Evans return (arena_salloc(ptr, false)); 258ed34ab0SJason Evans } 268ed34ab0SJason Evans 27e722f8f8SJason Evans void 28e722f8f8SJason Evans tcache_event_hard(tcache_t *tcache) 29e722f8f8SJason Evans { 30e722f8f8SJason Evans size_t binind = tcache->next_gc_bin; 31e722f8f8SJason Evans tcache_bin_t *tbin = &tcache->tbins[binind]; 32e722f8f8SJason Evans tcache_bin_info_t *tbin_info = &tcache_bin_info[binind]; 33e722f8f8SJason Evans 34e722f8f8SJason Evans if (tbin->low_water > 0) { 35e722f8f8SJason Evans /* 36e722f8f8SJason Evans * Flush (ceiling) 3/4 of the objects below the low water mark. 37e722f8f8SJason Evans */ 38e722f8f8SJason Evans if (binind < NBINS) { 39e722f8f8SJason Evans tcache_bin_flush_small(tbin, binind, tbin->ncached - 40e722f8f8SJason Evans tbin->low_water + (tbin->low_water >> 2), tcache); 41e722f8f8SJason Evans } else { 42e722f8f8SJason Evans tcache_bin_flush_large(tbin, binind, tbin->ncached - 43e722f8f8SJason Evans tbin->low_water + (tbin->low_water >> 2), tcache); 44e722f8f8SJason Evans } 45e722f8f8SJason Evans /* 46e722f8f8SJason Evans * Reduce fill count by 2X. Limit lg_fill_div such that the 47e722f8f8SJason Evans * fill count is always at least 1. 48e722f8f8SJason Evans */ 49e722f8f8SJason Evans if ((tbin_info->ncached_max >> (tbin->lg_fill_div+1)) >= 1) 50e722f8f8SJason Evans tbin->lg_fill_div++; 51e722f8f8SJason Evans } else if (tbin->low_water < 0) { 52e722f8f8SJason Evans /* 53e722f8f8SJason Evans * Increase fill count by 2X. Make sure lg_fill_div stays 54e722f8f8SJason Evans * greater than 0. 55e722f8f8SJason Evans */ 56e722f8f8SJason Evans if (tbin->lg_fill_div > 1) 57e722f8f8SJason Evans tbin->lg_fill_div--; 58e722f8f8SJason Evans } 59e722f8f8SJason Evans tbin->low_water = tbin->ncached; 60e722f8f8SJason Evans 61e722f8f8SJason Evans tcache->next_gc_bin++; 62e722f8f8SJason Evans if (tcache->next_gc_bin == nhbins) 63e722f8f8SJason Evans tcache->next_gc_bin = 0; 64e722f8f8SJason Evans tcache->ev_cnt = 0; 65e722f8f8SJason Evans } 66e722f8f8SJason Evans 67a4bd5210SJason Evans void * 68a4bd5210SJason Evans tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) 69a4bd5210SJason Evans { 70a4bd5210SJason Evans void *ret; 71a4bd5210SJason Evans 72a4bd5210SJason Evans arena_tcache_fill_small(tcache->arena, tbin, binind, 73a4bd5210SJason Evans config_prof ? tcache->prof_accumbytes : 0); 74a4bd5210SJason Evans if (config_prof) 75a4bd5210SJason Evans tcache->prof_accumbytes = 0; 76a4bd5210SJason Evans ret = tcache_alloc_easy(tbin); 77a4bd5210SJason Evans 78a4bd5210SJason Evans return (ret); 79a4bd5210SJason Evans } 80a4bd5210SJason Evans 81a4bd5210SJason Evans void 82a4bd5210SJason Evans tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, 83a4bd5210SJason Evans tcache_t *tcache) 84a4bd5210SJason Evans { 85a4bd5210SJason Evans void *ptr; 86a4bd5210SJason Evans unsigned i, nflush, ndeferred; 87a4bd5210SJason Evans bool merged_stats = false; 88a4bd5210SJason Evans 89a4bd5210SJason Evans assert(binind < NBINS); 90a4bd5210SJason Evans assert(rem <= tbin->ncached); 91a4bd5210SJason Evans 92a4bd5210SJason Evans for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) { 93a4bd5210SJason Evans /* Lock the arena bin associated with the first object. */ 94a4bd5210SJason Evans arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( 95a4bd5210SJason Evans tbin->avail[0]); 96a4bd5210SJason Evans arena_t *arena = chunk->arena; 97a4bd5210SJason Evans arena_bin_t *bin = &arena->bins[binind]; 98a4bd5210SJason Evans 99a4bd5210SJason Evans if (config_prof && arena == tcache->arena) { 100f8ca2db1SJason Evans if (arena_prof_accum(arena, tcache->prof_accumbytes)) 101f8ca2db1SJason Evans prof_idump(); 102a4bd5210SJason Evans tcache->prof_accumbytes = 0; 103a4bd5210SJason Evans } 104a4bd5210SJason Evans 105a4bd5210SJason Evans malloc_mutex_lock(&bin->lock); 106a4bd5210SJason Evans if (config_stats && arena == tcache->arena) { 107a4bd5210SJason Evans assert(merged_stats == false); 108a4bd5210SJason Evans merged_stats = true; 109a4bd5210SJason Evans bin->stats.nflushes++; 110a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 111a4bd5210SJason Evans tbin->tstats.nrequests = 0; 112a4bd5210SJason Evans } 113a4bd5210SJason Evans ndeferred = 0; 114a4bd5210SJason Evans for (i = 0; i < nflush; i++) { 115a4bd5210SJason Evans ptr = tbin->avail[i]; 116a4bd5210SJason Evans assert(ptr != NULL); 117a4bd5210SJason Evans chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 118a4bd5210SJason Evans if (chunk->arena == arena) { 119a4bd5210SJason Evans size_t pageind = ((uintptr_t)ptr - 120a4bd5210SJason Evans (uintptr_t)chunk) >> LG_PAGE; 121a4bd5210SJason Evans arena_chunk_map_t *mapelm = 122e722f8f8SJason Evans arena_mapp_get(chunk, pageind); 123a4bd5210SJason Evans if (config_fill && opt_junk) { 124a4bd5210SJason Evans arena_alloc_junk_small(ptr, 125a4bd5210SJason Evans &arena_bin_info[binind], true); 126a4bd5210SJason Evans } 127e722f8f8SJason Evans arena_dalloc_bin_locked(arena, chunk, ptr, 128e722f8f8SJason Evans mapelm); 129a4bd5210SJason Evans } else { 130a4bd5210SJason Evans /* 131a4bd5210SJason Evans * This object was allocated via a different 132a4bd5210SJason Evans * arena bin than the one that is currently 133a4bd5210SJason Evans * locked. Stash the object, so that it can be 134a4bd5210SJason Evans * handled in a future pass. 135a4bd5210SJason Evans */ 136a4bd5210SJason Evans tbin->avail[ndeferred] = ptr; 137a4bd5210SJason Evans ndeferred++; 138a4bd5210SJason Evans } 139a4bd5210SJason Evans } 140a4bd5210SJason Evans malloc_mutex_unlock(&bin->lock); 141a4bd5210SJason Evans } 142a4bd5210SJason Evans if (config_stats && merged_stats == false) { 143a4bd5210SJason Evans /* 144a4bd5210SJason Evans * The flush loop didn't happen to flush to this thread's 145a4bd5210SJason Evans * arena, so the stats didn't get merged. Manually do so now. 146a4bd5210SJason Evans */ 147a4bd5210SJason Evans arena_bin_t *bin = &tcache->arena->bins[binind]; 148a4bd5210SJason Evans malloc_mutex_lock(&bin->lock); 149a4bd5210SJason Evans bin->stats.nflushes++; 150a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 151a4bd5210SJason Evans tbin->tstats.nrequests = 0; 152a4bd5210SJason Evans malloc_mutex_unlock(&bin->lock); 153a4bd5210SJason Evans } 154a4bd5210SJason Evans 155a4bd5210SJason Evans memmove(tbin->avail, &tbin->avail[tbin->ncached - rem], 156a4bd5210SJason Evans rem * sizeof(void *)); 157a4bd5210SJason Evans tbin->ncached = rem; 158a4bd5210SJason Evans if ((int)tbin->ncached < tbin->low_water) 159a4bd5210SJason Evans tbin->low_water = tbin->ncached; 160a4bd5210SJason Evans } 161a4bd5210SJason Evans 162a4bd5210SJason Evans void 163a4bd5210SJason Evans tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem, 164a4bd5210SJason Evans tcache_t *tcache) 165a4bd5210SJason Evans { 166a4bd5210SJason Evans void *ptr; 167a4bd5210SJason Evans unsigned i, nflush, ndeferred; 168a4bd5210SJason Evans bool merged_stats = false; 169a4bd5210SJason Evans 170a4bd5210SJason Evans assert(binind < nhbins); 171a4bd5210SJason Evans assert(rem <= tbin->ncached); 172a4bd5210SJason Evans 173a4bd5210SJason Evans for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) { 174a4bd5210SJason Evans /* Lock the arena associated with the first object. */ 175a4bd5210SJason Evans arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( 176a4bd5210SJason Evans tbin->avail[0]); 177a4bd5210SJason Evans arena_t *arena = chunk->arena; 178f8ca2db1SJason Evans UNUSED bool idump; 179a4bd5210SJason Evans 180f8ca2db1SJason Evans if (config_prof) 181f8ca2db1SJason Evans idump = false; 182a4bd5210SJason Evans malloc_mutex_lock(&arena->lock); 183a4bd5210SJason Evans if ((config_prof || config_stats) && arena == tcache->arena) { 184a4bd5210SJason Evans if (config_prof) { 185f8ca2db1SJason Evans idump = arena_prof_accum_locked(arena, 186a4bd5210SJason Evans tcache->prof_accumbytes); 187a4bd5210SJason Evans tcache->prof_accumbytes = 0; 188a4bd5210SJason Evans } 189a4bd5210SJason Evans if (config_stats) { 190a4bd5210SJason Evans merged_stats = true; 191a4bd5210SJason Evans arena->stats.nrequests_large += 192a4bd5210SJason Evans tbin->tstats.nrequests; 193a4bd5210SJason Evans arena->stats.lstats[binind - NBINS].nrequests += 194a4bd5210SJason Evans tbin->tstats.nrequests; 195a4bd5210SJason Evans tbin->tstats.nrequests = 0; 196a4bd5210SJason Evans } 197a4bd5210SJason Evans } 198a4bd5210SJason Evans ndeferred = 0; 199a4bd5210SJason Evans for (i = 0; i < nflush; i++) { 200a4bd5210SJason Evans ptr = tbin->avail[i]; 201a4bd5210SJason Evans assert(ptr != NULL); 202a4bd5210SJason Evans chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 203a4bd5210SJason Evans if (chunk->arena == arena) 204e722f8f8SJason Evans arena_dalloc_large_locked(arena, chunk, ptr); 205a4bd5210SJason Evans else { 206a4bd5210SJason Evans /* 207a4bd5210SJason Evans * This object was allocated via a different 208a4bd5210SJason Evans * arena than the one that is currently locked. 209a4bd5210SJason Evans * Stash the object, so that it can be handled 210a4bd5210SJason Evans * in a future pass. 211a4bd5210SJason Evans */ 212a4bd5210SJason Evans tbin->avail[ndeferred] = ptr; 213a4bd5210SJason Evans ndeferred++; 214a4bd5210SJason Evans } 215a4bd5210SJason Evans } 216a4bd5210SJason Evans malloc_mutex_unlock(&arena->lock); 217f8ca2db1SJason Evans if (config_prof && idump) 218f8ca2db1SJason Evans prof_idump(); 219a4bd5210SJason Evans } 220a4bd5210SJason Evans if (config_stats && merged_stats == false) { 221a4bd5210SJason Evans /* 222a4bd5210SJason Evans * The flush loop didn't happen to flush to this thread's 223a4bd5210SJason Evans * arena, so the stats didn't get merged. Manually do so now. 224a4bd5210SJason Evans */ 225a4bd5210SJason Evans arena_t *arena = tcache->arena; 226a4bd5210SJason Evans malloc_mutex_lock(&arena->lock); 227a4bd5210SJason Evans arena->stats.nrequests_large += tbin->tstats.nrequests; 228a4bd5210SJason Evans arena->stats.lstats[binind - NBINS].nrequests += 229a4bd5210SJason Evans tbin->tstats.nrequests; 230a4bd5210SJason Evans tbin->tstats.nrequests = 0; 231a4bd5210SJason Evans malloc_mutex_unlock(&arena->lock); 232a4bd5210SJason Evans } 233a4bd5210SJason Evans 234a4bd5210SJason Evans memmove(tbin->avail, &tbin->avail[tbin->ncached - rem], 235a4bd5210SJason Evans rem * sizeof(void *)); 236a4bd5210SJason Evans tbin->ncached = rem; 237a4bd5210SJason Evans if ((int)tbin->ncached < tbin->low_water) 238a4bd5210SJason Evans tbin->low_water = tbin->ncached; 239a4bd5210SJason Evans } 240a4bd5210SJason Evans 241a4bd5210SJason Evans void 242a4bd5210SJason Evans tcache_arena_associate(tcache_t *tcache, arena_t *arena) 243a4bd5210SJason Evans { 244a4bd5210SJason Evans 245a4bd5210SJason Evans if (config_stats) { 246a4bd5210SJason Evans /* Link into list of extant tcaches. */ 247a4bd5210SJason Evans malloc_mutex_lock(&arena->lock); 248a4bd5210SJason Evans ql_elm_new(tcache, link); 249a4bd5210SJason Evans ql_tail_insert(&arena->tcache_ql, tcache, link); 250a4bd5210SJason Evans malloc_mutex_unlock(&arena->lock); 251a4bd5210SJason Evans } 252a4bd5210SJason Evans tcache->arena = arena; 253a4bd5210SJason Evans } 254a4bd5210SJason Evans 255a4bd5210SJason Evans void 256a4bd5210SJason Evans tcache_arena_dissociate(tcache_t *tcache) 257a4bd5210SJason Evans { 258a4bd5210SJason Evans 259a4bd5210SJason Evans if (config_stats) { 260a4bd5210SJason Evans /* Unlink from list of extant tcaches. */ 261a4bd5210SJason Evans malloc_mutex_lock(&tcache->arena->lock); 262a4bd5210SJason Evans ql_remove(&tcache->arena->tcache_ql, tcache, link); 263a4bd5210SJason Evans tcache_stats_merge(tcache, tcache->arena); 264*f921d10fSJason Evans malloc_mutex_unlock(&tcache->arena->lock); 265a4bd5210SJason Evans } 266a4bd5210SJason Evans } 267a4bd5210SJason Evans 268a4bd5210SJason Evans tcache_t * 269a4bd5210SJason Evans tcache_create(arena_t *arena) 270a4bd5210SJason Evans { 271a4bd5210SJason Evans tcache_t *tcache; 272a4bd5210SJason Evans size_t size, stack_offset; 273a4bd5210SJason Evans unsigned i; 274a4bd5210SJason Evans 275a4bd5210SJason Evans size = offsetof(tcache_t, tbins) + (sizeof(tcache_bin_t) * nhbins); 276a4bd5210SJason Evans /* Naturally align the pointer stacks. */ 277a4bd5210SJason Evans size = PTR_CEILING(size); 278a4bd5210SJason Evans stack_offset = size; 279a4bd5210SJason Evans size += stack_nelms * sizeof(void *); 280a4bd5210SJason Evans /* 281a4bd5210SJason Evans * Round up to the nearest multiple of the cacheline size, in order to 282a4bd5210SJason Evans * avoid the possibility of false cacheline sharing. 283a4bd5210SJason Evans * 284a4bd5210SJason Evans * That this works relies on the same logic as in ipalloc(), but we 285a4bd5210SJason Evans * cannot directly call ipalloc() here due to tcache bootstrapping 286a4bd5210SJason Evans * issues. 287a4bd5210SJason Evans */ 288a4bd5210SJason Evans size = (size + CACHELINE_MASK) & (-CACHELINE); 289a4bd5210SJason Evans 290a4bd5210SJason Evans if (size <= SMALL_MAXCLASS) 291a4bd5210SJason Evans tcache = (tcache_t *)arena_malloc_small(arena, size, true); 292a4bd5210SJason Evans else if (size <= tcache_maxclass) 293a4bd5210SJason Evans tcache = (tcache_t *)arena_malloc_large(arena, size, true); 294a4bd5210SJason Evans else 295*f921d10fSJason Evans tcache = (tcache_t *)icalloct(size, false, arena); 296a4bd5210SJason Evans 297a4bd5210SJason Evans if (tcache == NULL) 298a4bd5210SJason Evans return (NULL); 299a4bd5210SJason Evans 300a4bd5210SJason Evans tcache_arena_associate(tcache, arena); 301a4bd5210SJason Evans 302a4bd5210SJason Evans assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0); 303a4bd5210SJason Evans for (i = 0; i < nhbins; i++) { 304a4bd5210SJason Evans tcache->tbins[i].lg_fill_div = 1; 305a4bd5210SJason Evans tcache->tbins[i].avail = (void **)((uintptr_t)tcache + 306a4bd5210SJason Evans (uintptr_t)stack_offset); 307a4bd5210SJason Evans stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); 308a4bd5210SJason Evans } 309a4bd5210SJason Evans 310a4bd5210SJason Evans tcache_tsd_set(&tcache); 311a4bd5210SJason Evans 312a4bd5210SJason Evans return (tcache); 313a4bd5210SJason Evans } 314a4bd5210SJason Evans 315a4bd5210SJason Evans void 316a4bd5210SJason Evans tcache_destroy(tcache_t *tcache) 317a4bd5210SJason Evans { 318a4bd5210SJason Evans unsigned i; 319a4bd5210SJason Evans size_t tcache_size; 320a4bd5210SJason Evans 321a4bd5210SJason Evans tcache_arena_dissociate(tcache); 322a4bd5210SJason Evans 323a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 324a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 325a4bd5210SJason Evans tcache_bin_flush_small(tbin, i, 0, tcache); 326a4bd5210SJason Evans 327a4bd5210SJason Evans if (config_stats && tbin->tstats.nrequests != 0) { 328a4bd5210SJason Evans arena_t *arena = tcache->arena; 329a4bd5210SJason Evans arena_bin_t *bin = &arena->bins[i]; 330a4bd5210SJason Evans malloc_mutex_lock(&bin->lock); 331a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 332a4bd5210SJason Evans malloc_mutex_unlock(&bin->lock); 333a4bd5210SJason Evans } 334a4bd5210SJason Evans } 335a4bd5210SJason Evans 336a4bd5210SJason Evans for (; i < nhbins; i++) { 337a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 338a4bd5210SJason Evans tcache_bin_flush_large(tbin, i, 0, tcache); 339a4bd5210SJason Evans 340a4bd5210SJason Evans if (config_stats && tbin->tstats.nrequests != 0) { 341a4bd5210SJason Evans arena_t *arena = tcache->arena; 342a4bd5210SJason Evans malloc_mutex_lock(&arena->lock); 343a4bd5210SJason Evans arena->stats.nrequests_large += tbin->tstats.nrequests; 344a4bd5210SJason Evans arena->stats.lstats[i - NBINS].nrequests += 345a4bd5210SJason Evans tbin->tstats.nrequests; 346a4bd5210SJason Evans malloc_mutex_unlock(&arena->lock); 347a4bd5210SJason Evans } 348a4bd5210SJason Evans } 349a4bd5210SJason Evans 350f8ca2db1SJason Evans if (config_prof && tcache->prof_accumbytes > 0 && 351f8ca2db1SJason Evans arena_prof_accum(tcache->arena, tcache->prof_accumbytes)) 352f8ca2db1SJason Evans prof_idump(); 353a4bd5210SJason Evans 354a4bd5210SJason Evans tcache_size = arena_salloc(tcache, false); 355a4bd5210SJason Evans if (tcache_size <= SMALL_MAXCLASS) { 356a4bd5210SJason Evans arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); 357a4bd5210SJason Evans arena_t *arena = chunk->arena; 358a4bd5210SJason Evans size_t pageind = ((uintptr_t)tcache - (uintptr_t)chunk) >> 359a4bd5210SJason Evans LG_PAGE; 360e722f8f8SJason Evans arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); 361a4bd5210SJason Evans 362e722f8f8SJason Evans arena_dalloc_bin(arena, chunk, tcache, pageind, mapelm); 363a4bd5210SJason Evans } else if (tcache_size <= tcache_maxclass) { 364a4bd5210SJason Evans arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); 365a4bd5210SJason Evans arena_t *arena = chunk->arena; 366a4bd5210SJason Evans 367a4bd5210SJason Evans arena_dalloc_large(arena, chunk, tcache); 368a4bd5210SJason Evans } else 369*f921d10fSJason Evans idalloct(tcache, false); 370a4bd5210SJason Evans } 371a4bd5210SJason Evans 372a4bd5210SJason Evans void 373a4bd5210SJason Evans tcache_thread_cleanup(void *arg) 374a4bd5210SJason Evans { 375a4bd5210SJason Evans tcache_t *tcache = *(tcache_t **)arg; 376a4bd5210SJason Evans 377a4bd5210SJason Evans if (tcache == TCACHE_STATE_DISABLED) { 378a4bd5210SJason Evans /* Do nothing. */ 379a4bd5210SJason Evans } else if (tcache == TCACHE_STATE_REINCARNATED) { 380a4bd5210SJason Evans /* 381a4bd5210SJason Evans * Another destructor called an allocator function after this 382a4bd5210SJason Evans * destructor was called. Reset tcache to 383a4bd5210SJason Evans * TCACHE_STATE_PURGATORY in order to receive another callback. 384a4bd5210SJason Evans */ 385a4bd5210SJason Evans tcache = TCACHE_STATE_PURGATORY; 386a4bd5210SJason Evans tcache_tsd_set(&tcache); 387a4bd5210SJason Evans } else if (tcache == TCACHE_STATE_PURGATORY) { 388a4bd5210SJason Evans /* 389a4bd5210SJason Evans * The previous time this destructor was called, we set the key 390a4bd5210SJason Evans * to TCACHE_STATE_PURGATORY so that other destructors wouldn't 391a4bd5210SJason Evans * cause re-creation of the tcache. This time, do nothing, so 392a4bd5210SJason Evans * that the destructor will not be called again. 393a4bd5210SJason Evans */ 394a4bd5210SJason Evans } else if (tcache != NULL) { 395a4bd5210SJason Evans assert(tcache != TCACHE_STATE_PURGATORY); 396a4bd5210SJason Evans tcache_destroy(tcache); 397a4bd5210SJason Evans tcache = TCACHE_STATE_PURGATORY; 398a4bd5210SJason Evans tcache_tsd_set(&tcache); 399a4bd5210SJason Evans } 400a4bd5210SJason Evans } 401a4bd5210SJason Evans 402*f921d10fSJason Evans /* Caller must own arena->lock. */ 403a4bd5210SJason Evans void 404a4bd5210SJason Evans tcache_stats_merge(tcache_t *tcache, arena_t *arena) 405a4bd5210SJason Evans { 406a4bd5210SJason Evans unsigned i; 407a4bd5210SJason Evans 408*f921d10fSJason Evans cassert(config_stats); 409*f921d10fSJason Evans 410a4bd5210SJason Evans /* Merge and reset tcache stats. */ 411a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 412a4bd5210SJason Evans arena_bin_t *bin = &arena->bins[i]; 413a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 414a4bd5210SJason Evans malloc_mutex_lock(&bin->lock); 415a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 416a4bd5210SJason Evans malloc_mutex_unlock(&bin->lock); 417a4bd5210SJason Evans tbin->tstats.nrequests = 0; 418a4bd5210SJason Evans } 419a4bd5210SJason Evans 420a4bd5210SJason Evans for (; i < nhbins; i++) { 421a4bd5210SJason Evans malloc_large_stats_t *lstats = &arena->stats.lstats[i - NBINS]; 422a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 423a4bd5210SJason Evans arena->stats.nrequests_large += tbin->tstats.nrequests; 424a4bd5210SJason Evans lstats->nrequests += tbin->tstats.nrequests; 425a4bd5210SJason Evans tbin->tstats.nrequests = 0; 426a4bd5210SJason Evans } 427a4bd5210SJason Evans } 428a4bd5210SJason Evans 429a4bd5210SJason Evans bool 430a4bd5210SJason Evans tcache_boot0(void) 431a4bd5210SJason Evans { 432a4bd5210SJason Evans unsigned i; 433a4bd5210SJason Evans 434a4bd5210SJason Evans /* 435a4bd5210SJason Evans * If necessary, clamp opt_lg_tcache_max, now that arena_maxclass is 436a4bd5210SJason Evans * known. 437a4bd5210SJason Evans */ 438a4bd5210SJason Evans if (opt_lg_tcache_max < 0 || (1U << opt_lg_tcache_max) < SMALL_MAXCLASS) 439a4bd5210SJason Evans tcache_maxclass = SMALL_MAXCLASS; 440a4bd5210SJason Evans else if ((1U << opt_lg_tcache_max) > arena_maxclass) 441a4bd5210SJason Evans tcache_maxclass = arena_maxclass; 442a4bd5210SJason Evans else 443a4bd5210SJason Evans tcache_maxclass = (1U << opt_lg_tcache_max); 444a4bd5210SJason Evans 445a4bd5210SJason Evans nhbins = NBINS + (tcache_maxclass >> LG_PAGE); 446a4bd5210SJason Evans 447a4bd5210SJason Evans /* Initialize tcache_bin_info. */ 448a4bd5210SJason Evans tcache_bin_info = (tcache_bin_info_t *)base_alloc(nhbins * 449a4bd5210SJason Evans sizeof(tcache_bin_info_t)); 450a4bd5210SJason Evans if (tcache_bin_info == NULL) 451a4bd5210SJason Evans return (true); 452a4bd5210SJason Evans stack_nelms = 0; 453a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 454a4bd5210SJason Evans if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) { 455a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 456a4bd5210SJason Evans (arena_bin_info[i].nregs << 1); 457a4bd5210SJason Evans } else { 458a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 459a4bd5210SJason Evans TCACHE_NSLOTS_SMALL_MAX; 460a4bd5210SJason Evans } 461a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 462a4bd5210SJason Evans } 463a4bd5210SJason Evans for (; i < nhbins; i++) { 464a4bd5210SJason Evans tcache_bin_info[i].ncached_max = TCACHE_NSLOTS_LARGE; 465a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 466a4bd5210SJason Evans } 467a4bd5210SJason Evans 468a4bd5210SJason Evans return (false); 469a4bd5210SJason Evans } 470a4bd5210SJason Evans 471a4bd5210SJason Evans bool 472a4bd5210SJason Evans tcache_boot1(void) 473a4bd5210SJason Evans { 474a4bd5210SJason Evans 475a4bd5210SJason Evans if (tcache_tsd_boot() || tcache_enabled_tsd_boot()) 476a4bd5210SJason Evans return (true); 477a4bd5210SJason Evans 478a4bd5210SJason Evans return (false); 479a4bd5210SJason Evans } 480