1*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_preamble.h" 2*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_internal_includes.h" 3*c43cad87SWarner Losh 4*c43cad87SWarner Losh bool 5*c43cad87SWarner Losh edata_cache_init(edata_cache_t *edata_cache, base_t *base) { 6*c43cad87SWarner Losh edata_avail_new(&edata_cache->avail); 7*c43cad87SWarner Losh /* 8*c43cad87SWarner Losh * This is not strictly necessary, since the edata_cache_t is only 9*c43cad87SWarner Losh * created inside an arena, which is zeroed on creation. But this is 10*c43cad87SWarner Losh * handy as a safety measure. 11*c43cad87SWarner Losh */ 12*c43cad87SWarner Losh atomic_store_zu(&edata_cache->count, 0, ATOMIC_RELAXED); 13*c43cad87SWarner Losh if (malloc_mutex_init(&edata_cache->mtx, "edata_cache", 14*c43cad87SWarner Losh WITNESS_RANK_EDATA_CACHE, malloc_mutex_rank_exclusive)) { 15*c43cad87SWarner Losh return true; 16*c43cad87SWarner Losh } 17*c43cad87SWarner Losh edata_cache->base = base; 18*c43cad87SWarner Losh return false; 19*c43cad87SWarner Losh } 20*c43cad87SWarner Losh 21*c43cad87SWarner Losh edata_t * 22*c43cad87SWarner Losh edata_cache_get(tsdn_t *tsdn, edata_cache_t *edata_cache) { 23*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &edata_cache->mtx); 24*c43cad87SWarner Losh edata_t *edata = edata_avail_first(&edata_cache->avail); 25*c43cad87SWarner Losh if (edata == NULL) { 26*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &edata_cache->mtx); 27*c43cad87SWarner Losh return base_alloc_edata(tsdn, edata_cache->base); 28*c43cad87SWarner Losh } 29*c43cad87SWarner Losh edata_avail_remove(&edata_cache->avail, edata); 30*c43cad87SWarner Losh atomic_load_sub_store_zu(&edata_cache->count, 1); 31*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &edata_cache->mtx); 32*c43cad87SWarner Losh return edata; 33*c43cad87SWarner Losh } 34*c43cad87SWarner Losh 35*c43cad87SWarner Losh void 36*c43cad87SWarner Losh edata_cache_put(tsdn_t *tsdn, edata_cache_t *edata_cache, edata_t *edata) { 37*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &edata_cache->mtx); 38*c43cad87SWarner Losh edata_avail_insert(&edata_cache->avail, edata); 39*c43cad87SWarner Losh atomic_load_add_store_zu(&edata_cache->count, 1); 40*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &edata_cache->mtx); 41*c43cad87SWarner Losh } 42*c43cad87SWarner Losh 43*c43cad87SWarner Losh void 44*c43cad87SWarner Losh edata_cache_prefork(tsdn_t *tsdn, edata_cache_t *edata_cache) { 45*c43cad87SWarner Losh malloc_mutex_prefork(tsdn, &edata_cache->mtx); 46*c43cad87SWarner Losh } 47*c43cad87SWarner Losh 48*c43cad87SWarner Losh void 49*c43cad87SWarner Losh edata_cache_postfork_parent(tsdn_t *tsdn, edata_cache_t *edata_cache) { 50*c43cad87SWarner Losh malloc_mutex_postfork_parent(tsdn, &edata_cache->mtx); 51*c43cad87SWarner Losh } 52*c43cad87SWarner Losh 53*c43cad87SWarner Losh void 54*c43cad87SWarner Losh edata_cache_postfork_child(tsdn_t *tsdn, edata_cache_t *edata_cache) { 55*c43cad87SWarner Losh malloc_mutex_postfork_child(tsdn, &edata_cache->mtx); 56*c43cad87SWarner Losh } 57*c43cad87SWarner Losh 58*c43cad87SWarner Losh void 59*c43cad87SWarner Losh edata_cache_fast_init(edata_cache_fast_t *ecs, edata_cache_t *fallback) { 60*c43cad87SWarner Losh edata_list_inactive_init(&ecs->list); 61*c43cad87SWarner Losh ecs->fallback = fallback; 62*c43cad87SWarner Losh ecs->disabled = false; 63*c43cad87SWarner Losh } 64*c43cad87SWarner Losh 65*c43cad87SWarner Losh static void 66*c43cad87SWarner Losh edata_cache_fast_try_fill_from_fallback(tsdn_t *tsdn, 67*c43cad87SWarner Losh edata_cache_fast_t *ecs) { 68*c43cad87SWarner Losh edata_t *edata; 69*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &ecs->fallback->mtx); 70*c43cad87SWarner Losh for (int i = 0; i < EDATA_CACHE_FAST_FILL; i++) { 71*c43cad87SWarner Losh edata = edata_avail_remove_first(&ecs->fallback->avail); 72*c43cad87SWarner Losh if (edata == NULL) { 73*c43cad87SWarner Losh break; 74*c43cad87SWarner Losh } 75*c43cad87SWarner Losh edata_list_inactive_append(&ecs->list, edata); 76*c43cad87SWarner Losh atomic_load_sub_store_zu(&ecs->fallback->count, 1); 77*c43cad87SWarner Losh } 78*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &ecs->fallback->mtx); 79*c43cad87SWarner Losh } 80*c43cad87SWarner Losh 81*c43cad87SWarner Losh edata_t * 82*c43cad87SWarner Losh edata_cache_fast_get(tsdn_t *tsdn, edata_cache_fast_t *ecs) { 83*c43cad87SWarner Losh witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 84*c43cad87SWarner Losh WITNESS_RANK_EDATA_CACHE, 0); 85*c43cad87SWarner Losh 86*c43cad87SWarner Losh if (ecs->disabled) { 87*c43cad87SWarner Losh assert(edata_list_inactive_first(&ecs->list) == NULL); 88*c43cad87SWarner Losh return edata_cache_get(tsdn, ecs->fallback); 89*c43cad87SWarner Losh } 90*c43cad87SWarner Losh 91*c43cad87SWarner Losh edata_t *edata = edata_list_inactive_first(&ecs->list); 92*c43cad87SWarner Losh if (edata != NULL) { 93*c43cad87SWarner Losh edata_list_inactive_remove(&ecs->list, edata); 94*c43cad87SWarner Losh return edata; 95*c43cad87SWarner Losh } 96*c43cad87SWarner Losh /* Slow path; requires synchronization. */ 97*c43cad87SWarner Losh edata_cache_fast_try_fill_from_fallback(tsdn, ecs); 98*c43cad87SWarner Losh edata = edata_list_inactive_first(&ecs->list); 99*c43cad87SWarner Losh if (edata != NULL) { 100*c43cad87SWarner Losh edata_list_inactive_remove(&ecs->list, edata); 101*c43cad87SWarner Losh } else { 102*c43cad87SWarner Losh /* 103*c43cad87SWarner Losh * Slowest path (fallback was also empty); allocate something 104*c43cad87SWarner Losh * new. 105*c43cad87SWarner Losh */ 106*c43cad87SWarner Losh edata = base_alloc_edata(tsdn, ecs->fallback->base); 107*c43cad87SWarner Losh } 108*c43cad87SWarner Losh return edata; 109*c43cad87SWarner Losh } 110*c43cad87SWarner Losh 111*c43cad87SWarner Losh static void 112*c43cad87SWarner Losh edata_cache_fast_flush_all(tsdn_t *tsdn, edata_cache_fast_t *ecs) { 113*c43cad87SWarner Losh /* 114*c43cad87SWarner Losh * You could imagine smarter cache management policies (like 115*c43cad87SWarner Losh * only flushing down to some threshold in anticipation of 116*c43cad87SWarner Losh * future get requests). But just flushing everything provides 117*c43cad87SWarner Losh * a good opportunity to defrag too, and lets us share code between the 118*c43cad87SWarner Losh * flush and disable pathways. 119*c43cad87SWarner Losh */ 120*c43cad87SWarner Losh edata_t *edata; 121*c43cad87SWarner Losh size_t nflushed = 0; 122*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &ecs->fallback->mtx); 123*c43cad87SWarner Losh while ((edata = edata_list_inactive_first(&ecs->list)) != NULL) { 124*c43cad87SWarner Losh edata_list_inactive_remove(&ecs->list, edata); 125*c43cad87SWarner Losh edata_avail_insert(&ecs->fallback->avail, edata); 126*c43cad87SWarner Losh nflushed++; 127*c43cad87SWarner Losh } 128*c43cad87SWarner Losh atomic_load_add_store_zu(&ecs->fallback->count, nflushed); 129*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &ecs->fallback->mtx); 130*c43cad87SWarner Losh } 131*c43cad87SWarner Losh 132*c43cad87SWarner Losh void 133*c43cad87SWarner Losh edata_cache_fast_put(tsdn_t *tsdn, edata_cache_fast_t *ecs, edata_t *edata) { 134*c43cad87SWarner Losh witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn), 135*c43cad87SWarner Losh WITNESS_RANK_EDATA_CACHE, 0); 136*c43cad87SWarner Losh 137*c43cad87SWarner Losh if (ecs->disabled) { 138*c43cad87SWarner Losh assert(edata_list_inactive_first(&ecs->list) == NULL); 139*c43cad87SWarner Losh edata_cache_put(tsdn, ecs->fallback, edata); 140*c43cad87SWarner Losh return; 141*c43cad87SWarner Losh } 142*c43cad87SWarner Losh 143*c43cad87SWarner Losh /* 144*c43cad87SWarner Losh * Prepend rather than append, to do LIFO ordering in the hopes of some 145*c43cad87SWarner Losh * cache locality. 146*c43cad87SWarner Losh */ 147*c43cad87SWarner Losh edata_list_inactive_prepend(&ecs->list, edata); 148*c43cad87SWarner Losh } 149*c43cad87SWarner Losh 150*c43cad87SWarner Losh void 151*c43cad87SWarner Losh edata_cache_fast_disable(tsdn_t *tsdn, edata_cache_fast_t *ecs) { 152*c43cad87SWarner Losh edata_cache_fast_flush_all(tsdn, ecs); 153*c43cad87SWarner Losh ecs->disabled = true; 154*c43cad87SWarner Losh } 155