1 1.1 christos #ifndef JEMALLOC_INTERNAL_TCACHE_INLINES_H 2 1.1 christos #define JEMALLOC_INTERNAL_TCACHE_INLINES_H 3 1.1 christos 4 1.1.1.3 christos #include "jemalloc/internal/jemalloc_preamble.h" 5 1.1.1.3 christos #include "jemalloc/internal/arena_externs.h" 6 1.1 christos #include "jemalloc/internal/bin.h" 7 1.1.1.3 christos #include "jemalloc/internal/jemalloc_internal_inlines_b.h" 8 1.1 christos #include "jemalloc/internal/jemalloc_internal_types.h" 9 1.1.1.3 christos #include "jemalloc/internal/large_externs.h" 10 1.1.1.2 christos #include "jemalloc/internal/san.h" 11 1.1.1.2 christos #include "jemalloc/internal/sc.h" 12 1.1 christos #include "jemalloc/internal/sz.h" 13 1.1.1.3 christos #include "jemalloc/internal/tcache_externs.h" 14 1.1 christos #include "jemalloc/internal/util.h" 15 1.1 christos 16 1.1 christos static inline bool 17 1.1 christos tcache_enabled_get(tsd_t *tsd) { 18 1.1 christos return tsd_tcache_enabled_get(tsd); 19 1.1 christos } 20 1.1 christos 21 1.1.1.3 christos static inline unsigned 22 1.1.1.3 christos tcache_nbins_get(tcache_slow_t *tcache_slow) { 23 1.1.1.3 christos assert(tcache_slow != NULL); 24 1.1.1.3 christos unsigned nbins = tcache_slow->tcache_nbins; 25 1.1.1.3 christos assert(nbins <= TCACHE_NBINS_MAX); 26 1.1.1.3 christos return nbins; 27 1.1.1.3 christos } 28 1.1.1.3 christos 29 1.1.1.3 christos static inline size_t 30 1.1.1.3 christos tcache_max_get(tcache_slow_t *tcache_slow) { 31 1.1.1.3 christos assert(tcache_slow != NULL); 32 1.1.1.3 christos size_t tcache_max = sz_index2size(tcache_nbins_get(tcache_slow) - 1); 33 1.1.1.3 christos assert(tcache_max <= TCACHE_MAXCLASS_LIMIT); 34 1.1.1.3 christos return tcache_max; 35 1.1.1.3 christos } 36 1.1.1.3 christos 37 1.1 christos static inline void 38 1.1.1.3 christos tcache_max_set(tcache_slow_t *tcache_slow, size_t tcache_max) { 39 1.1.1.3 christos assert(tcache_slow != NULL); 40 1.1.1.3 christos assert(tcache_max <= TCACHE_MAXCLASS_LIMIT); 41 1.1.1.3 christos tcache_slow->tcache_nbins = sz_size2index(tcache_max) + 1; 42 1.1.1.3 christos } 43 1.1 christos 44 1.1.1.3 christos static inline void 45 1.1.1.3 christos tcache_bin_settings_backup( 46 1.1.1.3 christos tcache_t *tcache, cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX]) { 47 1.1.1.3 christos for (unsigned i = 0; i < TCACHE_NBINS_MAX; i++) { 48 1.1.1.3 christos cache_bin_info_init(&tcache_bin_info[i], 49 1.1.1.3 christos cache_bin_ncached_max_get_unsafe(&tcache->bins[i])); 50 1.1.1.3 christos } 51 1.1 christos } 52 1.1 christos 53 1.1.1.2 christos JEMALLOC_ALWAYS_INLINE bool 54 1.1.1.3 christos tcache_bin_disabled(szind_t ind, cache_bin_t *bin, tcache_slow_t *tcache_slow) { 55 1.1.1.3 christos assert(bin != NULL); 56 1.1.1.3 christos assert(ind < TCACHE_NBINS_MAX); 57 1.1.1.3 christos bool disabled = cache_bin_disabled(bin); 58 1.1.1.3 christos 59 1.1.1.3 christos /* 60 1.1.1.3 christos * If a bin's ind >= nbins or ncached_max == 0, it must be disabled. 61 1.1.1.3 christos * However, when ind < nbins, it could be either enabled 62 1.1.1.3 christos * (ncached_max > 0) or disabled (ncached_max == 0). Similarly, when 63 1.1.1.3 christos * ncached_max > 0, it could be either enabled (ind < nbins) or 64 1.1.1.3 christos * disabled (ind >= nbins). Thus, if a bin is disabled, it has either 65 1.1.1.3 christos * ind >= nbins or ncached_max == 0. If a bin is enabled, it has 66 1.1.1.3 christos * ind < nbins and ncached_max > 0. 67 1.1.1.3 christos */ 68 1.1.1.3 christos unsigned nbins = tcache_nbins_get(tcache_slow); 69 1.1.1.3 christos cache_bin_sz_t ncached_max = cache_bin_ncached_max_get_unsafe(bin); 70 1.1.1.3 christos if (ind >= nbins) { 71 1.1.1.3 christos assert(disabled); 72 1.1.1.3 christos } else { 73 1.1.1.3 christos assert(!disabled || ncached_max == 0); 74 1.1.1.3 christos } 75 1.1.1.3 christos if (ncached_max == 0) { 76 1.1.1.3 christos assert(disabled); 77 1.1.1.3 christos } else { 78 1.1.1.3 christos assert(!disabled || ind >= nbins); 79 1.1.1.3 christos } 80 1.1.1.3 christos if (disabled) { 81 1.1.1.3 christos assert(ind >= nbins || ncached_max == 0); 82 1.1.1.3 christos } else { 83 1.1.1.3 christos assert(ind < nbins && ncached_max > 0); 84 1.1 christos } 85 1.1 christos 86 1.1.1.3 christos return disabled; 87 1.1 christos } 88 1.1 christos 89 1.1 christos JEMALLOC_ALWAYS_INLINE void * 90 1.1.1.3 christos tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size, 91 1.1.1.3 christos szind_t binind, bool zero, bool slow_path) { 92 1.1 christos void *ret; 93 1.1.1.3 christos bool tcache_success; 94 1.1 christos 95 1.1.1.2 christos assert(binind < SC_NBINS); 96 1.1.1.2 christos cache_bin_t *bin = &tcache->bins[binind]; 97 1.1.1.2 christos ret = cache_bin_alloc(bin, &tcache_success); 98 1.1 christos assert(tcache_success == (ret != NULL)); 99 1.1 christos if (unlikely(!tcache_success)) { 100 1.1 christos bool tcache_hard_success; 101 1.1 christos arena = arena_choose(tsd, arena); 102 1.1 christos if (unlikely(arena == NULL)) { 103 1.1 christos return NULL; 104 1.1 christos } 105 1.1.1.3 christos if (unlikely(tcache_bin_disabled( 106 1.1.1.3 christos binind, bin, tcache->tcache_slow))) { 107 1.1.1.2 christos /* stats and zero are handled directly by the arena. */ 108 1.1.1.2 christos return arena_malloc_hard(tsd_tsdn(tsd), arena, size, 109 1.1.1.3 christos binind, zero, /* slab */ true); 110 1.1.1.2 christos } 111 1.1.1.2 christos tcache_bin_flush_stashed(tsd, tcache, bin, binind, 112 1.1.1.2 christos /* is_small */ true); 113 1.1 christos 114 1.1.1.3 christos ret = tcache_alloc_small_hard(tsd_tsdn(tsd), arena, tcache, bin, 115 1.1.1.3 christos binind, &tcache_hard_success); 116 1.1 christos if (tcache_hard_success == false) { 117 1.1 christos return NULL; 118 1.1 christos } 119 1.1 christos } 120 1.1 christos 121 1.1 christos assert(ret); 122 1.1.1.2 christos if (unlikely(zero)) { 123 1.1.1.2 christos size_t usize = sz_index2size(binind); 124 1.1 christos assert(tcache_salloc(tsd_tsdn(tsd), ret) == usize); 125 1.1 christos memset(ret, 0, usize); 126 1.1 christos } 127 1.1 christos if (config_stats) { 128 1.1 christos bin->tstats.nrequests++; 129 1.1 christos } 130 1.1 christos return ret; 131 1.1 christos } 132 1.1 christos 133 1.1 christos JEMALLOC_ALWAYS_INLINE void * 134 1.1 christos tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size, 135 1.1 christos szind_t binind, bool zero, bool slow_path) { 136 1.1 christos void *ret; 137 1.1.1.3 christos bool tcache_success; 138 1.1 christos 139 1.1.1.2 christos cache_bin_t *bin = &tcache->bins[binind]; 140 1.1.1.3 christos assert(binind >= SC_NBINS 141 1.1.1.3 christos && !tcache_bin_disabled(binind, bin, tcache->tcache_slow)); 142 1.1.1.2 christos ret = cache_bin_alloc(bin, &tcache_success); 143 1.1 christos assert(tcache_success == (ret != NULL)); 144 1.1 christos if (unlikely(!tcache_success)) { 145 1.1 christos /* 146 1.1 christos * Only allocate one large object at a time, because it's quite 147 1.1 christos * expensive to create one and not use it. 148 1.1 christos */ 149 1.1 christos arena = arena_choose(tsd, arena); 150 1.1 christos if (unlikely(arena == NULL)) { 151 1.1 christos return NULL; 152 1.1 christos } 153 1.1.1.2 christos tcache_bin_flush_stashed(tsd, tcache, bin, binind, 154 1.1.1.2 christos /* is_small */ false); 155 1.1 christos 156 1.1 christos ret = large_malloc(tsd_tsdn(tsd), arena, sz_s2u(size), zero); 157 1.1 christos if (ret == NULL) { 158 1.1 christos return NULL; 159 1.1 christos } 160 1.1 christos } else { 161 1.1.1.2 christos if (unlikely(zero)) { 162 1.1.1.2 christos size_t usize = sz_index2size(binind); 163 1.1.1.3 christos assert(usize <= tcache_max_get(tcache->tcache_slow)); 164 1.1 christos memset(ret, 0, usize); 165 1.1 christos } 166 1.1.1.3 christos } 167 1.1 christos 168 1.1.1.3 christos if (config_stats) { 169 1.1.1.3 christos bin->tstats.nrequests++; 170 1.1 christos } 171 1.1 christos 172 1.1 christos return ret; 173 1.1 christos } 174 1.1 christos 175 1.1 christos JEMALLOC_ALWAYS_INLINE void 176 1.1.1.3 christos tcache_dalloc_small( 177 1.1.1.3 christos tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind, bool slow_path) { 178 1.1.1.2 christos assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= SC_SMALL_MAXCLASS); 179 1.1 christos 180 1.1.1.2 christos cache_bin_t *bin = &tcache->bins[binind]; 181 1.1.1.2 christos /* 182 1.1.1.2 christos * Not marking the branch unlikely because this is past free_fastpath() 183 1.1.1.2 christos * (which handles the most common cases), i.e. at this point it's often 184 1.1.1.2 christos * uncommon cases. 185 1.1.1.2 christos */ 186 1.1.1.2 christos if (cache_bin_nonfast_aligned(ptr)) { 187 1.1.1.2 christos /* Junk unconditionally, even if bin is full. */ 188 1.1.1.2 christos san_junk_ptr(ptr, sz_index2size(binind)); 189 1.1.1.2 christos if (cache_bin_stash(bin, ptr)) { 190 1.1.1.2 christos return; 191 1.1.1.2 christos } 192 1.1.1.2 christos assert(cache_bin_full(bin)); 193 1.1.1.2 christos /* Bin full; fall through into the flush branch. */ 194 1.1.1.2 christos } 195 1.1.1.2 christos 196 1.1.1.2 christos if (unlikely(!cache_bin_dalloc_easy(bin, ptr))) { 197 1.1.1.3 christos if (unlikely(tcache_bin_disabled( 198 1.1.1.3 christos binind, bin, tcache->tcache_slow))) { 199 1.1.1.2 christos arena_dalloc_small(tsd_tsdn(tsd), ptr); 200 1.1.1.2 christos return; 201 1.1.1.2 christos } 202 1.1.1.3 christos cache_bin_sz_t max = cache_bin_ncached_max_get(bin); 203 1.1.1.3 christos unsigned remain = max >> opt_lg_tcache_flush_small_div; 204 1.1.1.2 christos tcache_bin_flush_small(tsd, tcache, bin, binind, remain); 205 1.1.1.2 christos bool ret = cache_bin_dalloc_easy(bin, ptr); 206 1.1.1.2 christos assert(ret); 207 1.1 christos } 208 1.1 christos } 209 1.1 christos 210 1.1 christos JEMALLOC_ALWAYS_INLINE void 211 1.1.1.3 christos tcache_dalloc_large( 212 1.1.1.3 christos tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind, bool slow_path) { 213 1.1.1.3 christos assert(tcache_salloc(tsd_tsdn(tsd), ptr) > SC_SMALL_MAXCLASS); 214 1.1.1.2 christos assert(tcache_salloc(tsd_tsdn(tsd), ptr) 215 1.1.1.3 christos <= tcache_max_get(tcache->tcache_slow)); 216 1.1.1.3 christos assert(!tcache_bin_disabled( 217 1.1.1.3 christos binind, &tcache->bins[binind], tcache->tcache_slow)); 218 1.1 christos 219 1.1.1.2 christos cache_bin_t *bin = &tcache->bins[binind]; 220 1.1.1.2 christos if (unlikely(!cache_bin_dalloc_easy(bin, ptr))) { 221 1.1.1.3 christos unsigned remain = cache_bin_ncached_max_get(bin) 222 1.1.1.3 christos >> opt_lg_tcache_flush_large_div; 223 1.1.1.2 christos tcache_bin_flush_large(tsd, tcache, bin, binind, remain); 224 1.1.1.2 christos bool ret = cache_bin_dalloc_easy(bin, ptr); 225 1.1.1.2 christos assert(ret); 226 1.1 christos } 227 1.1 christos } 228 1.1 christos 229 1.1 christos JEMALLOC_ALWAYS_INLINE tcache_t * 230 1.1 christos tcaches_get(tsd_t *tsd, unsigned ind) { 231 1.1 christos tcaches_t *elm = &tcaches[ind]; 232 1.1 christos if (unlikely(elm->tcache == NULL)) { 233 1.1.1.2 christos malloc_printf("<jemalloc>: invalid tcache id (%u).\n", ind); 234 1.1.1.2 christos abort(); 235 1.1.1.2 christos } else if (unlikely(elm->tcache == TCACHES_ELM_NEED_REINIT)) { 236 1.1 christos elm->tcache = tcache_create_explicit(tsd); 237 1.1 christos } 238 1.1 christos return elm->tcache; 239 1.1 christos } 240 1.1 christos 241 1.1 christos #endif /* JEMALLOC_INTERNAL_TCACHE_INLINES_H */ 242