jemalloc_internal_inlines_c.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. #ifndef JEMALLOC_INTERNAL_INLINES_C_H
  2. #define JEMALLOC_INTERNAL_INLINES_C_H
  3. #include "jemalloc/internal/hook.h"
  4. #include "jemalloc/internal/jemalloc_internal_types.h"
  5. #include "jemalloc/internal/log.h"
  6. #include "jemalloc/internal/sz.h"
  7. #include "jemalloc/internal/thread_event.h"
  8. #include "jemalloc/internal/witness.h"
  9. /*
  10. * Translating the names of the 'i' functions:
  11. * Abbreviations used in the first part of the function name (before
  12. * alloc/dalloc) describe what that function accomplishes:
  13. * a: arena (query)
  14. * s: size (query, or sized deallocation)
  15. * e: extent (query)
  16. * p: aligned (allocates)
  17. * vs: size (query, without knowing that the pointer is into the heap)
  18. * r: rallocx implementation
  19. * x: xallocx implementation
  20. * Abbreviations used in the second part of the function name (after
  21. * alloc/dalloc) describe the arguments it takes
  22. * z: whether to return zeroed memory
  23. * t: accepts a tcache_t * parameter
  24. * m: accepts an arena_t * parameter
  25. */
  26. JEMALLOC_ALWAYS_INLINE arena_t *
  27. iaalloc(tsdn_t *tsdn, const void *ptr) {
  28. assert(ptr != NULL);
  29. return arena_aalloc(tsdn, ptr);
  30. }
  31. JEMALLOC_ALWAYS_INLINE size_t
  32. isalloc(tsdn_t *tsdn, const void *ptr) {
  33. assert(ptr != NULL);
  34. return arena_salloc(tsdn, ptr);
  35. }
  36. JEMALLOC_ALWAYS_INLINE void *
  37. iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,
  38. bool is_internal, arena_t *arena, bool slow_path) {
  39. void *ret;
  40. assert(!is_internal || tcache == NULL);
  41. assert(!is_internal || arena == NULL || arena_is_auto(arena));
  42. if (!tsdn_null(tsdn) && tsd_reentrancy_level_get(tsdn_tsd(tsdn)) == 0) {
  43. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  44. WITNESS_RANK_CORE, 0);
  45. }
  46. ret = arena_malloc(tsdn, arena, size, ind, zero, tcache, slow_path);
  47. if (config_stats && is_internal && likely(ret != NULL)) {
  48. arena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));
  49. }
  50. return ret;
  51. }
  52. JEMALLOC_ALWAYS_INLINE void *
  53. ialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path) {
  54. return iallocztm(tsd_tsdn(tsd), size, ind, zero, tcache_get(tsd), false,
  55. NULL, slow_path);
  56. }
  57. JEMALLOC_ALWAYS_INLINE void *
  58. ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
  59. tcache_t *tcache, bool is_internal, arena_t *arena) {
  60. void *ret;
  61. assert(usize != 0);
  62. assert(usize == sz_sa2u(usize, alignment));
  63. assert(!is_internal || tcache == NULL);
  64. assert(!is_internal || arena == NULL || arena_is_auto(arena));
  65. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  66. WITNESS_RANK_CORE, 0);
  67. ret = arena_palloc(tsdn, arena, usize, alignment, zero, tcache);
  68. assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);
  69. if (config_stats && is_internal && likely(ret != NULL)) {
  70. arena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));
  71. }
  72. return ret;
  73. }
  74. JEMALLOC_ALWAYS_INLINE void *
  75. ipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
  76. tcache_t *tcache, arena_t *arena) {
  77. return ipallocztm(tsdn, usize, alignment, zero, tcache, false, arena);
  78. }
  79. JEMALLOC_ALWAYS_INLINE void *
  80. ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero) {
  81. return ipallocztm(tsd_tsdn(tsd), usize, alignment, zero,
  82. tcache_get(tsd), false, NULL);
  83. }
  84. JEMALLOC_ALWAYS_INLINE size_t
  85. ivsalloc(tsdn_t *tsdn, const void *ptr) {
  86. return arena_vsalloc(tsdn, ptr);
  87. }
  88. JEMALLOC_ALWAYS_INLINE void
  89. idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
  90. emap_alloc_ctx_t *alloc_ctx, bool is_internal, bool slow_path) {
  91. assert(ptr != NULL);
  92. assert(!is_internal || tcache == NULL);
  93. assert(!is_internal || arena_is_auto(iaalloc(tsdn, ptr)));
  94. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  95. WITNESS_RANK_CORE, 0);
  96. if (config_stats && is_internal) {
  97. arena_internal_sub(iaalloc(tsdn, ptr), isalloc(tsdn, ptr));
  98. }
  99. if (!is_internal && !tsdn_null(tsdn) &&
  100. tsd_reentrancy_level_get(tsdn_tsd(tsdn)) != 0) {
  101. assert(tcache == NULL);
  102. }
  103. arena_dalloc(tsdn, ptr, tcache, alloc_ctx, slow_path);
  104. }
  105. JEMALLOC_ALWAYS_INLINE void
  106. idalloc(tsd_t *tsd, void *ptr) {
  107. idalloctm(tsd_tsdn(tsd), ptr, tcache_get(tsd), NULL, false, true);
  108. }
  109. JEMALLOC_ALWAYS_INLINE void
  110. isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
  111. emap_alloc_ctx_t *alloc_ctx, bool slow_path) {
  112. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  113. WITNESS_RANK_CORE, 0);
  114. arena_sdalloc(tsdn, ptr, size, tcache, alloc_ctx, slow_path);
  115. }
  116. JEMALLOC_ALWAYS_INLINE void *
  117. iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
  118. size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
  119. hook_ralloc_args_t *hook_args) {
  120. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  121. WITNESS_RANK_CORE, 0);
  122. void *p;
  123. size_t usize, copysize;
  124. usize = sz_sa2u(size, alignment);
  125. if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
  126. return NULL;
  127. }
  128. p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
  129. if (p == NULL) {
  130. return NULL;
  131. }
  132. /*
  133. * Copy at most size bytes (not size+extra), since the caller has no
  134. * expectation that the extra bytes will be reliably preserved.
  135. */
  136. copysize = (size < oldsize) ? size : oldsize;
  137. memcpy(p, ptr, copysize);
  138. hook_invoke_alloc(hook_args->is_realloc
  139. ? hook_alloc_realloc : hook_alloc_rallocx, p, (uintptr_t)p,
  140. hook_args->args);
  141. hook_invoke_dalloc(hook_args->is_realloc
  142. ? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
  143. isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
  144. return p;
  145. }
  146. /*
  147. * is_realloc threads through the knowledge of whether or not this call comes
  148. * from je_realloc (as opposed to je_rallocx); this ensures that we pass the
  149. * correct entry point into any hooks.
  150. * Note that these functions are all force-inlined, so no actual bool gets
  151. * passed-around anywhere.
  152. */
  153. JEMALLOC_ALWAYS_INLINE void *
  154. iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
  155. bool zero, tcache_t *tcache, arena_t *arena, hook_ralloc_args_t *hook_args)
  156. {
  157. assert(ptr != NULL);
  158. assert(size != 0);
  159. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  160. WITNESS_RANK_CORE, 0);
  161. if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
  162. != 0) {
  163. /*
  164. * Existing object alignment is inadequate; allocate new space
  165. * and copy.
  166. */
  167. return iralloct_realign(tsdn, ptr, oldsize, size, alignment,
  168. zero, tcache, arena, hook_args);
  169. }
  170. return arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,
  171. tcache, hook_args);
  172. }
  173. JEMALLOC_ALWAYS_INLINE void *
  174. iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
  175. bool zero, hook_ralloc_args_t *hook_args) {
  176. return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,
  177. tcache_get(tsd), NULL, hook_args);
  178. }
  179. JEMALLOC_ALWAYS_INLINE bool
  180. ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
  181. size_t alignment, bool zero, size_t *newsize) {
  182. assert(ptr != NULL);
  183. assert(size != 0);
  184. witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
  185. WITNESS_RANK_CORE, 0);
  186. if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
  187. != 0) {
  188. /* Existing object alignment is inadequate. */
  189. *newsize = oldsize;
  190. return true;
  191. }
  192. return arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero,
  193. newsize);
  194. }
  195. JEMALLOC_ALWAYS_INLINE void
  196. fastpath_success_finish(tsd_t *tsd, uint64_t allocated_after,
  197. cache_bin_t *bin, void *ret) {
  198. thread_allocated_set(tsd, allocated_after);
  199. if (config_stats) {
  200. bin->tstats.nrequests++;
  201. }
  202. LOG("core.malloc.exit", "result: %p", ret);
  203. }
  204. JEMALLOC_ALWAYS_INLINE bool
  205. malloc_initialized(void) {
  206. return (malloc_init_state == malloc_init_initialized);
  207. }
  208. /*
  209. * malloc() fastpath. Included here so that we can inline it into operator new;
  210. * function call overhead there is non-negligible as a fraction of total CPU in
  211. * allocation-heavy C++ programs. We take the fallback alloc to allow malloc
  212. * (which can return NULL) to differ in its behavior from operator new (which
  213. * can't). It matches the signature of malloc / operator new so that we can
  214. * tail-call the fallback allocator, allowing us to avoid setting up the call
  215. * frame in the common case.
  216. *
  217. * Fastpath assumes size <= SC_LOOKUP_MAXCLASS, and that we hit
  218. * tcache. If either of these is false, we tail-call to the slowpath,
  219. * malloc_default(). Tail-calling is used to avoid any caller-saved
  220. * registers.
  221. *
  222. * fastpath supports ticker and profiling, both of which will also
  223. * tail-call to the slowpath if they fire.
  224. */
  225. JEMALLOC_ALWAYS_INLINE void *
  226. imalloc_fastpath(size_t size, void *(fallback_alloc)(size_t)) {
  227. LOG("core.malloc.entry", "size: %zu", size);
  228. if (tsd_get_allocates() && unlikely(!malloc_initialized())) {
  229. return fallback_alloc(size);
  230. }
  231. tsd_t *tsd = tsd_get(false);
  232. if (unlikely((size > SC_LOOKUP_MAXCLASS) || tsd == NULL)) {
  233. return fallback_alloc(size);
  234. }
  235. /*
  236. * The code below till the branch checking the next_event threshold may
  237. * execute before malloc_init(), in which case the threshold is 0 to
  238. * trigger slow path and initialization.
  239. *
  240. * Note that when uninitialized, only the fast-path variants of the sz /
  241. * tsd facilities may be called.
  242. */
  243. szind_t ind;
  244. /*
  245. * The thread_allocated counter in tsd serves as a general purpose
  246. * accumulator for bytes of allocation to trigger different types of
  247. * events. usize is always needed to advance thread_allocated, though
  248. * it's not always needed in the core allocation logic.
  249. */
  250. size_t usize;
  251. sz_size2index_usize_fastpath(size, &ind, &usize);
  252. /* Fast path relies on size being a bin. */
  253. assert(ind < SC_NBINS);
  254. assert((SC_LOOKUP_MAXCLASS < SC_SMALL_MAXCLASS) &&
  255. (size <= SC_SMALL_MAXCLASS));
  256. uint64_t allocated, threshold;
  257. te_malloc_fastpath_ctx(tsd, &allocated, &threshold);
  258. uint64_t allocated_after = allocated + usize;
  259. /*
  260. * The ind and usize might be uninitialized (or partially) before
  261. * malloc_init(). The assertions check for: 1) full correctness (usize
  262. * & ind) when initialized; and 2) guaranteed slow-path (threshold == 0)
  263. * when !initialized.
  264. */
  265. if (!malloc_initialized()) {
  266. assert(threshold == 0);
  267. } else {
  268. assert(ind == sz_size2index(size));
  269. assert(usize > 0 && usize == sz_index2size(ind));
  270. }
  271. /*
  272. * Check for events and tsd non-nominal (fast_threshold will be set to
  273. * 0) in a single branch.
  274. */
  275. if (unlikely(allocated_after >= threshold)) {
  276. return fallback_alloc(size);
  277. }
  278. assert(tsd_fast(tsd));
  279. tcache_t *tcache = tsd_tcachep_get(tsd);
  280. assert(tcache == tcache_get(tsd));
  281. cache_bin_t *bin = &tcache->bins[ind];
  282. bool tcache_success;
  283. void *ret;
  284. /*
  285. * We split up the code this way so that redundant low-water
  286. * computation doesn't happen on the (more common) case in which we
  287. * don't touch the low water mark. The compiler won't do this
  288. * duplication on its own.
  289. */
  290. ret = cache_bin_alloc_easy(bin, &tcache_success);
  291. if (tcache_success) {
  292. fastpath_success_finish(tsd, allocated_after, bin, ret);
  293. return ret;
  294. }
  295. ret = cache_bin_alloc(bin, &tcache_success);
  296. if (tcache_success) {
  297. fastpath_success_finish(tsd, allocated_after, bin, ret);
  298. return ret;
  299. }
  300. return fallback_alloc(size);
  301. }
  302. #endif /* JEMALLOC_INTERNAL_INLINES_C_H */