Home | History | Annotate | Line # | Download | only in internal
      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