1#ifndef JEMALLOC_INTERNAL_EXTENT_H
2#define JEMALLOC_INTERNAL_EXTENT_H
3
4#include "jemalloc/internal/ecache.h"
5#include "jemalloc/internal/ehooks.h"
6#include "jemalloc/internal/ph.h"
7#include "jemalloc/internal/rtree.h"
8
9/*
10 * This module contains the page-level allocator. It chooses the addresses that
11 * allocations requested by other modules will inhabit, and updates the global
12 * metadata to reflect allocation/deallocation/purging decisions.
13 */
14
15/*
16 * When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
17 * is the max ratio between the size of the active extent and the new extent.
18 */
19#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
20extern size_t opt_lg_extent_max_active_fit;
21
22edata_t *ecache_alloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
23 ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
24 bool zero, bool guarded);
25edata_t *ecache_alloc_grow(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
26 ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
27 bool zero, bool guarded);
28void ecache_dalloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
29 ecache_t *ecache, edata_t *edata);
30edata_t *ecache_evict(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
31 ecache_t *ecache, size_t npages_min);
32
33void extent_gdump_add(tsdn_t *tsdn, const edata_t *edata);
34void extent_record(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
35 edata_t *edata);
36void extent_dalloc_gap(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
37 edata_t *edata);
38edata_t *extent_alloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
39 void *new_addr, size_t size, size_t alignment, bool zero, bool *commit,
40 bool growing_retained);
41void extent_dalloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
42 edata_t *edata);
43void extent_destroy_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
44 edata_t *edata);
45bool extent_commit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
46 size_t offset, size_t length);
47bool extent_decommit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
48 size_t offset, size_t length);
49bool extent_purge_lazy_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
50 size_t offset, size_t length);
51bool extent_purge_forced_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
52 size_t offset, size_t length);
53edata_t *extent_split_wrapper(tsdn_t *tsdn, pac_t *pac,
54 ehooks_t *ehooks, edata_t *edata, size_t size_a, size_t size_b,
55 bool holding_core_locks);
56bool extent_merge_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
57 edata_t *a, edata_t *b);
58bool extent_commit_zero(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
59 bool commit, bool zero, bool growing_retained);
60size_t extent_sn_next(pac_t *pac);
61bool extent_boot(void);
62
63JEMALLOC_ALWAYS_INLINE bool
64extent_neighbor_head_state_mergeable(bool edata_is_head,
65 bool neighbor_is_head, bool forward) {
66 /*
67 * Head states checking: disallow merging if the higher addr extent is a
68 * head extent. This helps preserve first-fit, and more importantly
69 * makes sure no merge across arenas.
70 */
71 if (forward) {
72 if (neighbor_is_head) {
73 return false;
74 }
75 } else {
76 if (edata_is_head) {
77 return false;
78 }
79 }
80 return true;
81}
82
83JEMALLOC_ALWAYS_INLINE bool
84extent_can_acquire_neighbor(edata_t *edata, rtree_contents_t contents,
85 extent_pai_t pai, extent_state_t expected_state, bool forward,
86 bool expanding) {
87 edata_t *neighbor = contents.edata;
88 if (neighbor == NULL) {
89 return false;
90 }
91 /* It's not safe to access *neighbor yet; must verify states first. */
92 bool neighbor_is_head = contents.metadata.is_head;
93 if (!extent_neighbor_head_state_mergeable(edata_is_head_get(edata),
94 neighbor_is_head, forward)) {
95 return false;
96 }
97 extent_state_t neighbor_state = contents.metadata.state;
98 if (pai == EXTENT_PAI_PAC) {
99 if (neighbor_state != expected_state) {
100 return false;
101 }
102 /* From this point, it's safe to access *neighbor. */
103 if (!expanding && (edata_committed_get(edata) !=
104 edata_committed_get(neighbor))) {
105 /*
106 * Some platforms (e.g. Windows) require an explicit
107 * commit step (and writing to uncommitted memory is not
108 * allowed).
109 */
110 return false;
111 }
112 } else {
113 if (neighbor_state == extent_state_active) {
114 return false;
115 }
116 /* From this point, it's safe to access *neighbor. */
117 }
118
119 assert(edata_pai_get(edata) == pai);
120 if (edata_pai_get(neighbor) != pai) {
121 return false;
122 }
123 if (opt_retain) {
124 assert(edata_arena_ind_get(edata) ==
125 edata_arena_ind_get(neighbor));
126 } else {
127 if (edata_arena_ind_get(edata) !=
128 edata_arena_ind_get(neighbor)) {
129 return false;
130 }
131 }
132 assert(!edata_guarded_get(edata) && !edata_guarded_get(neighbor));
133
134 return true;
135}
136
137#endif /* JEMALLOC_INTERNAL_EXTENT_H */
138