1#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_B_H 2#define JEMALLOC_INTERNAL_ARENA_INLINES_B_H 3 4#include "jemalloc/internal/jemalloc_internal_types.h" 5#include "jemalloc/internal/mutex.h" 6#include "jemalloc/internal/rtree.h" 7#include "jemalloc/internal/sc.h" 8#include "jemalloc/internal/sz.h" 9#include "jemalloc/internal/ticker.h" 10 11JEMALLOC_ALWAYS_INLINE bool 12arena_has_default_hooks(arena_t *arena) { 13 return (extent_hooks_get(arena) == &extent_hooks_default); 14} 15 16JEMALLOC_ALWAYS_INLINE arena_t * 17arena_choose_maybe_huge(tsd_t *tsd, arena_t *arena, size_t size) { 18 if (arena != NULL) { 19 return arena; 20 } 21 22 /* 23 * For huge allocations, use the dedicated huge arena if both are true: 24 * 1) is using auto arena selection (i.e. arena == NULL), and 2) the 25 * thread is not assigned to a manual arena. 26 */ 27 if (unlikely(size >= oversize_threshold)) { 28 arena_t *tsd_arena = tsd_arena_get(tsd); 29 if (tsd_arena == NULL || arena_is_auto(tsd_arena)) { 30 return arena_choose_huge(tsd); 31 } 32 } 33 34 return arena_choose(tsd, NULL); 35} 36 37JEMALLOC_ALWAYS_INLINE prof_tctx_t * 38arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) { 39 cassert(config_prof); 40 assert(ptr != NULL); 41 42 /* Static check. */ 43 if (alloc_ctx == NULL) { 44 const extent_t *extent = iealloc(tsdn, ptr); 45 if (unlikely(!extent_slab_get(extent))) { 46 return large_prof_tctx_get(tsdn, extent); 47 } 48 } else { 49 if (unlikely(!alloc_ctx->slab)) { 50 return large_prof_tctx_get(tsdn, iealloc(tsdn, ptr)); 51 } 52 } 53 return (prof_tctx_t *)(uintptr_t)1U; 54} 55 56JEMALLOC_ALWAYS_INLINE void 57arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize, 58 alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) { 59 cassert(config_prof); 60 assert(ptr != NULL); 61 62 /* Static check. */ 63 if (alloc_ctx == NULL) { 64 extent_t *extent = iealloc(tsdn, ptr); 65 if (unlikely(!extent_slab_get(extent))) { 66 large_prof_tctx_set(tsdn, extent, tctx); 67 } 68 } else { 69 if (unlikely(!alloc_ctx->slab)) { 70 large_prof_tctx_set(tsdn, iealloc(tsdn, ptr), tctx); 71 } 72 } 73} 74 75static inline void 76arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) { 77 cassert(config_prof); 78 assert(ptr != NULL); 79 80 extent_t *extent = iealloc(tsdn, ptr); 81 assert(!extent_slab_get(extent)); 82 83 large_prof_tctx_reset(tsdn, extent); 84} 85 86JEMALLOC_ALWAYS_INLINE nstime_t 87arena_prof_alloc_time_get(tsdn_t *tsdn, const void *ptr, 88 alloc_ctx_t *alloc_ctx) { 89 cassert(config_prof); 90 assert(ptr != NULL); 91 92 extent_t *extent = iealloc(tsdn, ptr); 93 /* 94 * Unlike arena_prof_prof_tctx_{get, set}, we only call this once we're 95 * sure we have a sampled allocation. 96 */ 97 assert(!extent_slab_get(extent)); 98 return large_prof_alloc_time_get(extent); 99} 100 101JEMALLOC_ALWAYS_INLINE void 102arena_prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx, 103 nstime_t t) { 104 cassert(config_prof); 105 assert(ptr != NULL); 106 107 extent_t *extent = iealloc(tsdn, ptr); 108 assert(!extent_slab_get(extent)); 109 large_prof_alloc_time_set(extent, t); 110} 111 112JEMALLOC_ALWAYS_INLINE void 113arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) { 114 tsd_t *tsd; 115 ticker_t *decay_ticker; 116 117 if (unlikely(tsdn_null(tsdn))) { 118 return; 119 } 120 tsd = tsdn_tsd(tsdn); 121 decay_ticker = decay_ticker_get(tsd, arena_ind_get(arena)); 122 if (unlikely(decay_ticker == NULL)) { 123 return; 124 } 125 if (unlikely(ticker_ticks(decay_ticker, nticks))) { 126 arena_decay(tsdn, arena, false, false); 127 } 128} 129 130JEMALLOC_ALWAYS_INLINE void 131arena_decay_tick(tsdn_t *tsdn, arena_t *arena) { 132 malloc_mutex_assert_not_owner(tsdn, &arena->decay_dirty.mtx); 133 malloc_mutex_assert_not_owner(tsdn, &arena->decay_muzzy.mtx); 134 135 arena_decay_ticks(tsdn, arena, 1); 136} 137 138/* Purge a single extent to retained / unmapped directly. */ 139JEMALLOC_ALWAYS_INLINE void 140arena_decay_extent(tsdn_t *tsdn,arena_t *arena, extent_hooks_t **r_extent_hooks, 141 extent_t *extent) { 142 size_t extent_size = extent_size_get(extent); 143 extent_dalloc_wrapper(tsdn, arena, 144 r_extent_hooks, extent); 145 if (config_stats) { 146 /* Update stats accordingly. */ 147 arena_stats_lock(tsdn, &arena->stats); 148 arena_stats_add_u64(tsdn, &arena->stats, 149 &arena->decay_dirty.stats->nmadvise, 1); 150 arena_stats_add_u64(tsdn, &arena->stats, 151 &arena->decay_dirty.stats->purged, extent_size >> LG_PAGE); 152 arena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped, 153 extent_size); 154 arena_stats_unlock(tsdn, &arena->stats); 155 } 156} 157 158JEMALLOC_ALWAYS_INLINE void * 159arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero, 160 tcache_t *tcache, bool slow_path) { 161 assert(!tsdn_null(tsdn) || tcache == NULL); 162 163 if (likely(tcache != NULL)) { 164 if (likely(size <= SC_SMALL_MAXCLASS)) { 165 return tcache_alloc_small(tsdn_tsd(tsdn), arena, 166 tcache, size, ind, zero, slow_path); 167 } 168 if (likely(size <= tcache_maxclass)) { 169 return tcache_alloc_large(tsdn_tsd(tsdn), arena, 170 tcache, size, ind, zero, slow_path); 171 } 172 /* (size > tcache_maxclass) case falls through. */ 173 assert(size > tcache_maxclass); 174 } 175 176 return arena_malloc_hard(tsdn, arena, size, ind, zero); 177} 178 179JEMALLOC_ALWAYS_INLINE arena_t * 180arena_aalloc(tsdn_t *tsdn, const void *ptr) { 181 return extent_arena_get(iealloc(tsdn, ptr)); 182} 183 184JEMALLOC_ALWAYS_INLINE size_t 185arena_salloc(tsdn_t *tsdn, const void *ptr) { 186 assert(ptr != NULL); 187 188 rtree_ctx_t rtree_ctx_fallback; 189 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 190 191 szind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx, 192 (uintptr_t)ptr, true); 193 assert(szind != SC_NSIZES); 194 195 return sz_index2size(szind); 196} 197 198JEMALLOC_ALWAYS_INLINE size_t 199arena_vsalloc(tsdn_t *tsdn, const void *ptr) { 200 /* 201 * Return 0 if ptr is not within an extent managed by jemalloc. This 202 * function has two extra costs relative to isalloc(): 203 * - The rtree calls cannot claim to be dependent lookups, which induces 204 * rtree lookup load dependencies. 205 * - The lookup may fail, so there is an extra branch to check for 206 * failure. 207 */ 208 209 rtree_ctx_t rtree_ctx_fallback; 210 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 211 212 extent_t *extent; 213 szind_t szind; 214 if (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx, 215 (uintptr_t)ptr, false, &extent, &szind)) { 216 return 0; 217 } 218 219 if (extent == NULL) { 220 return 0; 221 } 222 assert(extent_state_get(extent) == extent_state_active); 223 /* Only slab members should be looked up via interior pointers. */ 224 assert(extent_addr_get(extent) == ptr || extent_slab_get(extent)); 225 226 assert(szind != SC_NSIZES); 227 228 return sz_index2size(szind); 229} 230 231static inline void 232arena_dalloc_large_no_tcache(tsdn_t *tsdn, void *ptr, szind_t szind) { 233 if (config_prof && unlikely(szind < SC_NBINS)) { 234 arena_dalloc_promoted(tsdn, ptr, NULL, true); 235 } else { 236 extent_t *extent = iealloc(tsdn, ptr); 237 large_dalloc(tsdn, extent); 238 } 239} 240 241static inline void 242arena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) { 243 assert(ptr != NULL); 244 245 rtree_ctx_t rtree_ctx_fallback; 246 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); 247 248 szind_t szind; 249 bool slab; 250 rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr, 251 true, &szind, &slab); 252 253 if (config_debug) { 254 extent_t *extent = rtree_extent_read(tsdn, &extents_rtree, 255 rtree_ctx, (uintptr_t)ptr, true); 256 assert(szind == extent_szind_get(extent)); 257 assert(szind < SC_NSIZES); 258 assert(slab == extent_slab_get(extent)); 259 } 260 261 if (likely(slab)) { 262 /* Small allocation. */ 263 arena_dalloc_small(tsdn, ptr); 264 } else { 265 arena_dalloc_large_no_tcache(tsdn, ptr, szind); 266 } 267} 268 269JEMALLOC_ALWAYS_INLINE void 270arena_dalloc_large(tsdn_t *tsdn, void *ptr, tcache_t *tcache, szind_t szind, 271 bool slow_path) { 272 if (szind < nhbins) { 273 if (config_prof && unlikely(szind < SC_NBINS)) { 274 arena_dalloc_promoted(tsdn, ptr, tcache, slow_path); 275 } else { 276 tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr, szind, 277 slow_path); 278 } 279 } else { 280 extent_t *extent = iealloc(tsdn, ptr); 281 large_dalloc(tsdn, extent); 282 } 283} 284 285JEMALLOC_ALWAYS_INLINE void 286arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache, 287 alloc_ctx_t *alloc_ctx, bool slow_path) { 288 assert(!tsdn_null(tsdn) || tcache == NULL); 289 assert(ptr != NULL); 290 291 if (unlikely(tcache == NULL)) { 292 arena_dalloc_no_tcache(tsdn, ptr); 293 return; 294 } 295 296 szind_t szind; 297 bool slab; 298 rtree_ctx_t *rtree_ctx; 299 if (alloc_ctx != NULL) { 300 szind = alloc_ctx->szind; 301 slab = alloc_ctx->slab; 302 assert(szind != SC_NSIZES); 303 } else { 304 rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn)); 305 rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, 306 (uintptr_t)ptr, true, &szind, &slab); 307 } 308 309 if (config_debug) { 310 rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn)); 311 extent_t *extent = rtree_extent_read(tsdn, &extents_rtree, 312 rtree_ctx, (uintptr_t)ptr, true); 313 assert(szind == extent_szind_get(extent)); 314 assert(szind < SC_NSIZES); 315 assert(slab == extent_slab_get(extent)); 316 } 317 318 if (likely(slab)) { 319 /* Small allocation. */ 320 tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind, 321 slow_path); 322 } else { 323 arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path); 324 } 325} 326 327static inline void 328arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) { 329 assert(ptr != NULL); 330 assert(size <= SC_LARGE_MAXCLASS); 331 332 szind_t szind; 333 bool slab; 334 if (!config_prof || !opt_prof) { 335 /* 336 * There is no risk of being confused by a promoted sampled 337 * object, so base szind and slab on the given size. 338 */ 339 szind = sz_size2index(size); 340 slab = (szind < SC_NBINS); 341 } 342 343 if ((config_prof && opt_prof) || config_debug) { 344 rtree_ctx_t rtree_ctx_fallback; 345 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, 346 &rtree_ctx_fallback); 347 348 rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, 349 (uintptr_t)ptr, true, &szind, &slab); 350 351 assert(szind == sz_size2index(size)); 352 assert((config_prof && opt_prof) || slab == (szind < SC_NBINS)); 353 354 if (config_debug) { 355 extent_t *extent = rtree_extent_read(tsdn, 356 &extents_rtree, rtree_ctx, (uintptr_t)ptr, true); 357 assert(szind == extent_szind_get(extent)); 358 assert(slab == extent_slab_get(extent)); 359 } 360 } 361 362 if (likely(slab)) { 363 /* Small allocation. */ 364 arena_dalloc_small(tsdn, ptr); 365 } else { 366 arena_dalloc_large_no_tcache(tsdn, ptr, szind); 367 } 368} 369 370JEMALLOC_ALWAYS_INLINE void 371arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache, 372 alloc_ctx_t *alloc_ctx, bool slow_path) { 373 assert(!tsdn_null(tsdn) || tcache == NULL); 374 assert(ptr != NULL); 375 assert(size <= SC_LARGE_MAXCLASS); 376 377 if (unlikely(tcache == NULL)) { 378 arena_sdalloc_no_tcache(tsdn, ptr, size); 379 return; 380 } 381 382 szind_t szind; 383 bool slab; 384 alloc_ctx_t local_ctx; 385 if (config_prof && opt_prof) { 386 if (alloc_ctx == NULL) { 387 /* Uncommon case and should be a static check. */ 388 rtree_ctx_t rtree_ctx_fallback; 389 rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, 390 &rtree_ctx_fallback); 391 rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, 392 (uintptr_t)ptr, true, &local_ctx.szind, 393 &local_ctx.slab); 394 assert(local_ctx.szind == sz_size2index(size)); 395 alloc_ctx = &local_ctx; 396 } 397 slab = alloc_ctx->slab; 398 szind = alloc_ctx->szind; 399 } else { 400 /* 401 * There is no risk of being confused by a promoted sampled 402 * object, so base szind and slab on the given size. 403 */ 404 szind = sz_size2index(size); 405 slab = (szind < SC_NBINS); 406 } 407 408 if (config_debug) { 409 rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn)); 410 rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, 411 (uintptr_t)ptr, true, &szind, &slab); 412 extent_t *extent = rtree_extent_read(tsdn, 413 &extents_rtree, rtree_ctx, (uintptr_t)ptr, true); 414 assert(szind == extent_szind_get(extent)); 415 assert(slab == extent_slab_get(extent)); 416 } 417 418 if (likely(slab)) { 419 /* Small allocation. */ 420 tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind, 421 slow_path); 422 } else { 423 arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path); 424 } 425} 426 427#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_B_H */ 428