#ifndef JEMALLOC_INTERNAL_MALLOC_DISPATCH_INLINES_H #define JEMALLOC_INTERNAL_MALLOC_DISPATCH_INLINES_H #include "jemalloc/internal/jemalloc_preamble.h" #include "jemalloc/internal/arena.h" #include "jemalloc/internal/arena_inlines.h" #include "jemalloc/internal/bin_inlines.h" #include "jemalloc/internal/div.h" #include "jemalloc/internal/emap.h" #include "jemalloc/internal/jemalloc_internal_types.h" #include "jemalloc/internal/large.h" #include "jemalloc/internal/malloc_dispatch.h" #include "jemalloc/internal/safety_check.h" #include "jemalloc/internal/sc.h" #include "jemalloc/internal/sz.h" #include "jemalloc/internal/tcache_inlines.h" JEMALLOC_ALWAYS_INLINE void * malloc_dispatch_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero, bool slab, tcache_t *tcache, bool slow_path) { assert(!tsdn_null(tsdn) || tcache == NULL); if (likely(tcache != NULL)) { if (likely(slab)) { assert(sz_can_use_slab(size)); return tcache_alloc_small(tsdn_tsd(tsdn), arena, tcache, size, ind, zero, slow_path); } else if (likely(tcache_can_cache_large(tcache, ind))) { return tcache_alloc_large(tsdn_tsd(tsdn), arena, tcache, size, ind, zero, slow_path); } /* (size > tcache_max) case falls through. */ } return arena_malloc_hard(tsdn, arena, size, ind, zero, slab); } static inline void malloc_dispatch_dalloc_large_no_tcache( tsdn_t *tsdn, void *ptr, szind_t szind, size_t usize) { /* * szind both classifies small vs large and validates the extent -- * inactive extents have szind == SC_NSIZES. */ if (config_prof && unlikely(szind < SC_NBINS)) { malloc_dispatch_dalloc_promoted(tsdn, ptr, NULL, true); } else { edata_t *edata = emap_edata_lookup( tsdn, &arena_emap_global, ptr); if (large_dalloc_safety_checks(edata, ptr, usize)) { /* See the comment in isfree. */ return; } large_dalloc(tsdn, edata); } } static inline void malloc_dispatch_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) { assert(ptr != NULL); emap_alloc_ctx_t alloc_ctx; emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr, &alloc_ctx); if (config_debug) { edata_t *edata = emap_edata_lookup( tsdn, &arena_emap_global, ptr); assert(alloc_ctx.szind == edata_szind_get(edata)); assert(alloc_ctx.szind < SC_NSIZES); assert(alloc_ctx.slab == edata_slab_get(edata)); assert(emap_alloc_ctx_usize_get(&alloc_ctx) == edata_usize_get(edata)); } if (likely(alloc_ctx.slab)) { /* Small allocation. */ arena_dalloc_small(tsdn, ptr); } else { malloc_dispatch_dalloc_large_no_tcache( tsdn, ptr, alloc_ctx.szind, emap_alloc_ctx_usize_get(&alloc_ctx)); } } JEMALLOC_ALWAYS_INLINE void malloc_dispatch_dalloc_large(tsdn_t *tsdn, void *ptr, tcache_t *tcache, szind_t szind, size_t usize, bool slow_path) { assert(!tsdn_null(tsdn) && tcache != NULL); bool is_sample_promoted = config_prof && szind < SC_NBINS; if (unlikely(is_sample_promoted)) { malloc_dispatch_dalloc_promoted(tsdn, ptr, tcache, slow_path); } else { if (tcache_can_cache_large(tcache, szind)) { tcache_dalloc_large( tsdn_tsd(tsdn), tcache, ptr, szind, slow_path); } else { edata_t *edata = emap_edata_lookup( tsdn, &arena_emap_global, ptr); if (large_dalloc_safety_checks(edata, ptr, usize)) { /* See the comment in isfree. */ return; } large_dalloc(tsdn, edata); } } } JEMALLOC_ALWAYS_INLINE bool malloc_dispatch_dalloc_small_safety_check(tsdn_t *tsdn, void *ptr) { if (!config_debug) { return false; } edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); szind_t binind = edata_szind_get(edata); div_info_t div_info = arena_binind_div_info[binind]; /* * Calls the internal function bin_slab_regind_impl because the * safety check does not require a lock. */ size_t regind = bin_slab_regind_impl(&div_info, binind, edata, ptr); slab_data_t *slab_data = edata_slab_data_get(edata); const bin_info_t *bin_info = &bin_infos[binind]; assert(edata_nfree_get(edata) < bin_info->nregs); if (unlikely(!bitmap_get( slab_data->bitmap, &bin_info->bitmap_info, regind))) { safety_check_fail( "Invalid deallocation detected: the pointer being freed (%p) not " "currently active, possibly caused by double free bugs.\n", ptr); return true; } return false; } JEMALLOC_ALWAYS_INLINE void malloc_dispatch_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache, emap_alloc_ctx_t *caller_alloc_ctx, bool slow_path) { assert(!tsdn_null(tsdn) || tcache == NULL); assert(ptr != NULL); if (unlikely(tcache == NULL)) { malloc_dispatch_dalloc_no_tcache(tsdn, ptr); return; } emap_alloc_ctx_t alloc_ctx; if (caller_alloc_ctx != NULL) { alloc_ctx = *caller_alloc_ctx; } else { util_assume(tsdn != NULL); emap_alloc_ctx_lookup( tsdn, &arena_emap_global, ptr, &alloc_ctx); } if (config_debug) { edata_t *edata = emap_edata_lookup( tsdn, &arena_emap_global, ptr); assert(alloc_ctx.szind == edata_szind_get(edata)); assert(alloc_ctx.szind < SC_NSIZES); assert(alloc_ctx.slab == edata_slab_get(edata)); assert(emap_alloc_ctx_usize_get(&alloc_ctx) == edata_usize_get(edata)); } if (likely(alloc_ctx.slab)) { /* Small allocation. */ if (malloc_dispatch_dalloc_small_safety_check(tsdn, ptr)) { return; } tcache_dalloc_small( tsdn_tsd(tsdn), tcache, ptr, alloc_ctx.szind, slow_path); } else { malloc_dispatch_dalloc_large(tsdn, ptr, tcache, alloc_ctx.szind, emap_alloc_ctx_usize_get(&alloc_ctx), slow_path); } } static inline void malloc_dispatch_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) { assert(ptr != NULL); assert(size <= SC_LARGE_MAXCLASS); emap_alloc_ctx_t alloc_ctx; if (!config_prof || !opt_prof) { /* * There is no risk of being confused by a promoted sampled * object, so base szind and slab on the given size. */ szind_t szind = sz_size2index(size); emap_alloc_ctx_init( &alloc_ctx, szind, (szind < SC_NBINS), size); } if ((config_prof && opt_prof) || config_debug) { emap_alloc_ctx_lookup( tsdn, &arena_emap_global, ptr, &alloc_ctx); assert(alloc_ctx.szind == sz_size2index(size)); assert((config_prof && opt_prof) || alloc_ctx.slab == (alloc_ctx.szind < SC_NBINS)); if (config_debug) { edata_t *edata = emap_edata_lookup( tsdn, &arena_emap_global, ptr); assert(alloc_ctx.szind == edata_szind_get(edata)); assert(alloc_ctx.slab == edata_slab_get(edata)); } } if (likely(alloc_ctx.slab)) { /* Small allocation. */ arena_dalloc_small(tsdn, ptr); } else { malloc_dispatch_dalloc_large_no_tcache( tsdn, ptr, alloc_ctx.szind, emap_alloc_ctx_usize_get(&alloc_ctx)); } } JEMALLOC_ALWAYS_INLINE void malloc_dispatch_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache, emap_alloc_ctx_t *caller_alloc_ctx, bool slow_path) { assert(!tsdn_null(tsdn) || tcache == NULL); assert(ptr != NULL); assert(size <= SC_LARGE_MAXCLASS); if (unlikely(tcache == NULL)) { malloc_dispatch_sdalloc_no_tcache(tsdn, ptr, size); return; } emap_alloc_ctx_t alloc_ctx; if (config_prof && opt_prof) { if (caller_alloc_ctx == NULL) { /* Uncommon case and should be a static check. */ emap_alloc_ctx_lookup( tsdn, &arena_emap_global, ptr, &alloc_ctx); assert(alloc_ctx.szind == sz_size2index(size)); assert(emap_alloc_ctx_usize_get(&alloc_ctx) == size); } else { alloc_ctx = *caller_alloc_ctx; } } else { /* * There is no risk of being confused by a promoted sampled * object, so base szind and slab on the given size. */ alloc_ctx.szind = sz_size2index(size); alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS); } if (config_debug) { edata_t *edata = emap_edata_lookup( tsdn, &arena_emap_global, ptr); assert(alloc_ctx.szind == edata_szind_get(edata)); assert(alloc_ctx.slab == edata_slab_get(edata)); emap_alloc_ctx_init( &alloc_ctx, alloc_ctx.szind, alloc_ctx.slab, sz_s2u(size)); assert(emap_alloc_ctx_usize_get(&alloc_ctx) == edata_usize_get(edata)); } if (likely(alloc_ctx.slab)) { /* Small allocation. */ if (malloc_dispatch_dalloc_small_safety_check(tsdn, ptr)) { return; } tcache_dalloc_small( tsdn_tsd(tsdn), tcache, ptr, alloc_ctx.szind, slow_path); } else { malloc_dispatch_dalloc_large(tsdn, ptr, tcache, alloc_ctx.szind, sz_s2u(size), slow_path); } } #endif /* JEMALLOC_INTERNAL_MALLOC_DISPATCH_INLINES_H */