#include "jemalloc/internal/jemalloc_preamble.h" #include "jemalloc/internal/jemalloc_internal_includes.h" #include "jemalloc/internal/assert.h" #include "jemalloc/internal/bin.h" #include "jemalloc/internal/sc.h" #include "jemalloc/internal/witness.h" bool bin_update_shard_size(unsigned bin_shard_sizes[SC_NBINS], size_t start_size, size_t end_size, size_t nshards) { if (nshards > BIN_SHARDS_MAX || nshards == 0) { return true; } if (start_size > SC_SMALL_MAXCLASS) { return false; } if (end_size > SC_SMALL_MAXCLASS) { end_size = SC_SMALL_MAXCLASS; } /* Compute the index since this may happen before sz init. */ szind_t ind1 = sz_size2index_compute(start_size); szind_t ind2 = sz_size2index_compute(end_size); for (unsigned i = ind1; i <= ind2; i++) { bin_shard_sizes[i] = (unsigned)nshards; } return false; } void bin_shard_sizes_boot(unsigned bin_shard_sizes[SC_NBINS]) { /* Load the default number of shards. */ for (unsigned i = 0; i < SC_NBINS; i++) { bin_shard_sizes[i] = N_BIN_SHARDS_DEFAULT; } } bool bin_init(bin_t *bin) { if (malloc_mutex_init(&bin->lock, "bin", WITNESS_RANK_BIN, malloc_mutex_rank_exclusive)) { return true; } bin->slabcur = NULL; edata_heap_new(&bin->slabs_nonfull); edata_list_active_init(&bin->slabs_full); if (config_stats) { memset(&bin->stats, 0, sizeof(bin_stats_t)); } return false; } void bin_prefork(tsdn_t *tsdn, bin_t *bin) { malloc_mutex_prefork(tsdn, &bin->lock); } void bin_postfork_parent(tsdn_t *tsdn, bin_t *bin) { malloc_mutex_postfork_parent(tsdn, &bin->lock); } void bin_postfork_child(tsdn_t *tsdn, bin_t *bin) { malloc_mutex_postfork_child(tsdn, &bin->lock); } void * bin_slab_reg_alloc(edata_t *slab, const bin_info_t *bin_info) { void *ret; slab_data_t *slab_data = edata_slab_data_get(slab); size_t regind; assert(edata_nfree_get(slab) > 0); assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info)); regind = bitmap_sfu(slab_data->bitmap, &bin_info->bitmap_info); ret = (void *)((byte_t *)edata_addr_get(slab) + (uintptr_t)(bin_info->reg_size * regind)); edata_nfree_dec(slab); return ret; } void bin_slab_reg_alloc_batch( edata_t *slab, const bin_info_t *bin_info, unsigned cnt, void **ptrs) { slab_data_t *slab_data = edata_slab_data_get(slab); assert(edata_nfree_get(slab) >= cnt); assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info)); #if (!defined JEMALLOC_INTERNAL_POPCOUNTL) || (defined BITMAP_USE_TREE) for (unsigned i = 0; i < cnt; i++) { size_t regind = bitmap_sfu( slab_data->bitmap, &bin_info->bitmap_info); *(ptrs + i) = (void *)((uintptr_t)edata_addr_get(slab) + (uintptr_t)(bin_info->reg_size * regind)); } #else unsigned group = 0; bitmap_t g = slab_data->bitmap[group]; unsigned i = 0; while (i < cnt) { while (g == 0) { g = slab_data->bitmap[++group]; } size_t shift = group << LG_BITMAP_GROUP_NBITS; size_t pop = popcount_lu(g); if (pop > (cnt - i)) { pop = cnt - i; } /* * Load from memory locations only once, outside the * hot loop below. */ uintptr_t base = (uintptr_t)edata_addr_get(slab); uintptr_t regsize = (uintptr_t)bin_info->reg_size; while (pop--) { size_t bit = cfs_lu(&g); size_t regind = shift + bit; /* NOLINTNEXTLINE(performance-no-int-to-ptr) */ *(ptrs + i) = (void *)(base + regsize * regind); i++; } slab_data->bitmap[group] = g; } #endif edata_nfree_sub(slab, cnt); } void bin_slabs_nonfull_insert(bin_t *bin, edata_t *slab) { assert(edata_nfree_get(slab) > 0); edata_heap_insert(&bin->slabs_nonfull, slab); if (config_stats) { bin->stats.nonfull_slabs++; } } void bin_slabs_nonfull_remove(bin_t *bin, edata_t *slab) { edata_heap_remove(&bin->slabs_nonfull, slab); if (config_stats) { bin->stats.nonfull_slabs--; } } edata_t * bin_slabs_nonfull_tryget(bin_t *bin) { edata_t *slab = edata_heap_remove_first(&bin->slabs_nonfull); if (slab == NULL) { return NULL; } if (config_stats) { bin->stats.reslabs++; bin->stats.nonfull_slabs--; } return slab; } void bin_slabs_full_insert(bool is_auto, bin_t *bin, edata_t *slab) { assert(edata_nfree_get(slab) == 0); /* * Tracking extents is required by arena_reset, which is not allowed * for auto arenas. Bypass this step to avoid touching the edata * linkage (often results in cache misses) for auto arenas. */ if (is_auto) { return; } edata_list_active_append(&bin->slabs_full, slab); } void bin_slabs_full_remove(bool is_auto, bin_t *bin, edata_t *slab) { if (is_auto) { return; } edata_list_active_remove(&bin->slabs_full, slab); } void bin_dissociate_slab(bool is_auto, edata_t *slab, bin_t *bin) { /* Dissociate slab from bin. */ if (slab == bin->slabcur) { bin->slabcur = NULL; } else { szind_t binind = edata_szind_get(slab); const bin_info_t *bin_info = &bin_infos[binind]; /* * The following block's conditional is necessary because if the * slab only contains one region, then it never gets inserted * into the non-full slabs heap. */ if (bin_info->nregs == 1) { bin_slabs_full_remove(is_auto, bin, slab); } else { bin_slabs_nonfull_remove(bin, slab); } } } void bin_lower_slab(tsdn_t *tsdn, bool is_auto, edata_t *slab, bin_t *bin) { assert(edata_nfree_get(slab) > 0); /* * Make sure that if bin->slabcur is non-NULL, it refers to the * oldest/lowest non-full slab. It is okay to NULL slabcur out rather * than proactively keeping it pointing at the oldest/lowest non-full * slab. */ if (bin->slabcur != NULL && edata_snad_comp(bin->slabcur, slab) > 0) { /* Switch slabcur. */ if (edata_nfree_get(bin->slabcur) > 0) { bin_slabs_nonfull_insert(bin, bin->slabcur); } else { bin_slabs_full_insert(is_auto, bin, bin->slabcur); } bin->slabcur = slab; if (config_stats) { bin->stats.reslabs++; } } else { bin_slabs_nonfull_insert(bin, slab); } } void bin_dalloc_slab_prepare(tsdn_t *tsdn, edata_t *slab, bin_t *bin) { malloc_mutex_assert_owner(tsdn, &bin->lock); assert(slab != bin->slabcur); if (config_stats) { bin->stats.curslabs--; } } void bin_dalloc_locked_handle_newly_empty( tsdn_t *tsdn, bool is_auto, edata_t *slab, bin_t *bin) { bin_dissociate_slab(is_auto, slab, bin); bin_dalloc_slab_prepare(tsdn, slab, bin); } void bin_dalloc_locked_handle_newly_nonempty( tsdn_t *tsdn, bool is_auto, edata_t *slab, bin_t *bin) { bin_slabs_full_remove(is_auto, bin, slab); bin_lower_slab(tsdn, is_auto, slab, bin); } void bin_refill_slabcur_with_fresh_slab(tsdn_t *tsdn, bin_t *bin, szind_t binind, edata_t *fresh_slab) { malloc_mutex_assert_owner(tsdn, &bin->lock); /* Only called after slabcur and nonfull both failed. */ assert(bin->slabcur == NULL); assert(edata_heap_first(&bin->slabs_nonfull) == NULL); assert(fresh_slab != NULL); /* A new slab from arena_slab_alloc() */ assert(edata_nfree_get(fresh_slab) == bin_infos[binind].nregs); if (config_stats) { bin->stats.nslabs++; bin->stats.curslabs++; } bin->slabcur = fresh_slab; } void * bin_malloc_with_fresh_slab(tsdn_t *tsdn, bin_t *bin, szind_t binind, edata_t *fresh_slab) { malloc_mutex_assert_owner(tsdn, &bin->lock); bin_refill_slabcur_with_fresh_slab(tsdn, bin, binind, fresh_slab); return bin_slab_reg_alloc(bin->slabcur, &bin_infos[binind]); } bool bin_refill_slabcur_no_fresh_slab(tsdn_t *tsdn, bool is_auto, bin_t *bin) { malloc_mutex_assert_owner(tsdn, &bin->lock); /* Only called after bin_slab_reg_alloc[_batch] failed. */ assert(bin->slabcur == NULL || edata_nfree_get(bin->slabcur) == 0); if (bin->slabcur != NULL) { bin_slabs_full_insert(is_auto, bin, bin->slabcur); } /* Look for a usable slab. */ bin->slabcur = bin_slabs_nonfull_tryget(bin); assert(bin->slabcur == NULL || edata_nfree_get(bin->slabcur) > 0); return (bin->slabcur == NULL); } void * bin_malloc_no_fresh_slab(tsdn_t *tsdn, bool is_auto, bin_t *bin, szind_t binind) { malloc_mutex_assert_owner(tsdn, &bin->lock); if (bin->slabcur == NULL || edata_nfree_get(bin->slabcur) == 0) { if (bin_refill_slabcur_no_fresh_slab(tsdn, is_auto, bin)) { return NULL; } } assert(bin->slabcur != NULL && edata_nfree_get(bin->slabcur) > 0); return bin_slab_reg_alloc(bin->slabcur, &bin_infos[binind]); } bin_t * bin_choose(tsdn_t *tsdn, arena_t *arena, szind_t binind, unsigned *binshard_p) { unsigned binshard; if (tsdn_null(tsdn) || tsd_arena_get(tsdn_tsd(tsdn)) == NULL) { binshard = 0; } else { binshard = tsd_binshardsp_get(tsdn_tsd(tsdn))->binshard[binind]; } assert(binshard < bin_infos[binind].n_shards); if (binshard_p != NULL) { *binshard_p = binshard; } return arena_get_bin(arena, binind, binshard); }