1 | #define JEMALLOC_RTREE_C_ |
2 | #include "jemalloc/internal/jemalloc_preamble.h" |
3 | #include "jemalloc/internal/jemalloc_internal_includes.h" |
4 | |
5 | #include "jemalloc/internal/assert.h" |
6 | #include "jemalloc/internal/mutex.h" |
7 | |
8 | /* |
9 | * Only the most significant bits of keys passed to rtree_{read,write}() are |
10 | * used. |
11 | */ |
12 | bool |
13 | rtree_new(rtree_t *rtree, bool zeroed) { |
14 | #ifdef JEMALLOC_JET |
15 | if (!zeroed) { |
16 | memset(rtree, 0, sizeof(rtree_t)); /* Clear root. */ |
17 | } |
18 | #else |
19 | assert(zeroed); |
20 | #endif |
21 | |
22 | if (malloc_mutex_init(&rtree->init_lock, "rtree" , WITNESS_RANK_RTREE, |
23 | malloc_mutex_rank_exclusive)) { |
24 | return true; |
25 | } |
26 | |
27 | return false; |
28 | } |
29 | |
30 | static rtree_node_elm_t * |
31 | rtree_node_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) { |
32 | return (rtree_node_elm_t *)base_alloc(tsdn, b0get(), nelms * |
33 | sizeof(rtree_node_elm_t), CACHELINE); |
34 | } |
35 | rtree_node_alloc_t *JET_MUTABLE rtree_node_alloc = rtree_node_alloc_impl; |
36 | |
37 | static void |
38 | rtree_node_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *node) { |
39 | /* Nodes are never deleted during normal operation. */ |
40 | not_reached(); |
41 | } |
42 | rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc = |
43 | rtree_node_dalloc_impl; |
44 | |
45 | static rtree_leaf_elm_t * |
46 | rtree_leaf_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) { |
47 | return (rtree_leaf_elm_t *)base_alloc(tsdn, b0get(), nelms * |
48 | sizeof(rtree_leaf_elm_t), CACHELINE); |
49 | } |
50 | rtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc = rtree_leaf_alloc_impl; |
51 | |
52 | static void |
53 | rtree_leaf_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *leaf) { |
54 | /* Leaves are never deleted during normal operation. */ |
55 | not_reached(); |
56 | } |
57 | rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc = |
58 | rtree_leaf_dalloc_impl; |
59 | |
60 | #ifdef JEMALLOC_JET |
61 | # if RTREE_HEIGHT > 1 |
62 | static void |
63 | rtree_delete_subtree(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *subtree, |
64 | unsigned level) { |
65 | size_t nchildren = ZU(1) << rtree_levels[level].bits; |
66 | if (level + 2 < RTREE_HEIGHT) { |
67 | for (size_t i = 0; i < nchildren; i++) { |
68 | rtree_node_elm_t *node = |
69 | (rtree_node_elm_t *)atomic_load_p(&subtree[i].child, |
70 | ATOMIC_RELAXED); |
71 | if (node != NULL) { |
72 | rtree_delete_subtree(tsdn, rtree, node, level + |
73 | 1); |
74 | } |
75 | } |
76 | } else { |
77 | for (size_t i = 0; i < nchildren; i++) { |
78 | rtree_leaf_elm_t *leaf = |
79 | (rtree_leaf_elm_t *)atomic_load_p(&subtree[i].child, |
80 | ATOMIC_RELAXED); |
81 | if (leaf != NULL) { |
82 | rtree_leaf_dalloc(tsdn, rtree, leaf); |
83 | } |
84 | } |
85 | } |
86 | |
87 | if (subtree != rtree->root) { |
88 | rtree_node_dalloc(tsdn, rtree, subtree); |
89 | } |
90 | } |
91 | # endif |
92 | |
93 | void |
94 | rtree_delete(tsdn_t *tsdn, rtree_t *rtree) { |
95 | # if RTREE_HEIGHT > 1 |
96 | rtree_delete_subtree(tsdn, rtree, rtree->root, 0); |
97 | # endif |
98 | } |
99 | #endif |
100 | |
101 | static rtree_node_elm_t * |
102 | rtree_node_init(tsdn_t *tsdn, rtree_t *rtree, unsigned level, |
103 | atomic_p_t *elmp) { |
104 | malloc_mutex_lock(tsdn, &rtree->init_lock); |
105 | /* |
106 | * If *elmp is non-null, then it was initialized with the init lock |
107 | * held, so we can get by with 'relaxed' here. |
108 | */ |
109 | rtree_node_elm_t *node = atomic_load_p(elmp, ATOMIC_RELAXED); |
110 | if (node == NULL) { |
111 | node = rtree_node_alloc(tsdn, rtree, ZU(1) << |
112 | rtree_levels[level].bits); |
113 | if (node == NULL) { |
114 | malloc_mutex_unlock(tsdn, &rtree->init_lock); |
115 | return NULL; |
116 | } |
117 | /* |
118 | * Even though we hold the lock, a later reader might not; we |
119 | * need release semantics. |
120 | */ |
121 | atomic_store_p(elmp, node, ATOMIC_RELEASE); |
122 | } |
123 | malloc_mutex_unlock(tsdn, &rtree->init_lock); |
124 | |
125 | return node; |
126 | } |
127 | |
128 | static rtree_leaf_elm_t * |
129 | rtree_leaf_init(tsdn_t *tsdn, rtree_t *rtree, atomic_p_t *elmp) { |
130 | malloc_mutex_lock(tsdn, &rtree->init_lock); |
131 | /* |
132 | * If *elmp is non-null, then it was initialized with the init lock |
133 | * held, so we can get by with 'relaxed' here. |
134 | */ |
135 | rtree_leaf_elm_t *leaf = atomic_load_p(elmp, ATOMIC_RELAXED); |
136 | if (leaf == NULL) { |
137 | leaf = rtree_leaf_alloc(tsdn, rtree, ZU(1) << |
138 | rtree_levels[RTREE_HEIGHT-1].bits); |
139 | if (leaf == NULL) { |
140 | malloc_mutex_unlock(tsdn, &rtree->init_lock); |
141 | return NULL; |
142 | } |
143 | /* |
144 | * Even though we hold the lock, a later reader might not; we |
145 | * need release semantics. |
146 | */ |
147 | atomic_store_p(elmp, leaf, ATOMIC_RELEASE); |
148 | } |
149 | malloc_mutex_unlock(tsdn, &rtree->init_lock); |
150 | |
151 | return leaf; |
152 | } |
153 | |
154 | static bool |
155 | rtree_node_valid(rtree_node_elm_t *node) { |
156 | return ((uintptr_t)node != (uintptr_t)0); |
157 | } |
158 | |
159 | static bool |
160 | rtree_leaf_valid(rtree_leaf_elm_t *leaf) { |
161 | return ((uintptr_t)leaf != (uintptr_t)0); |
162 | } |
163 | |
164 | static rtree_node_elm_t * |
165 | rtree_child_node_tryread(rtree_node_elm_t *elm, bool dependent) { |
166 | rtree_node_elm_t *node; |
167 | |
168 | if (dependent) { |
169 | node = (rtree_node_elm_t *)atomic_load_p(&elm->child, |
170 | ATOMIC_RELAXED); |
171 | } else { |
172 | node = (rtree_node_elm_t *)atomic_load_p(&elm->child, |
173 | ATOMIC_ACQUIRE); |
174 | } |
175 | |
176 | assert(!dependent || node != NULL); |
177 | return node; |
178 | } |
179 | |
180 | static rtree_node_elm_t * |
181 | rtree_child_node_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm, |
182 | unsigned level, bool dependent) { |
183 | rtree_node_elm_t *node; |
184 | |
185 | node = rtree_child_node_tryread(elm, dependent); |
186 | if (!dependent && unlikely(!rtree_node_valid(node))) { |
187 | node = rtree_node_init(tsdn, rtree, level + 1, &elm->child); |
188 | } |
189 | assert(!dependent || node != NULL); |
190 | return node; |
191 | } |
192 | |
193 | static rtree_leaf_elm_t * |
194 | rtree_child_leaf_tryread(rtree_node_elm_t *elm, bool dependent) { |
195 | rtree_leaf_elm_t *leaf; |
196 | |
197 | if (dependent) { |
198 | leaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child, |
199 | ATOMIC_RELAXED); |
200 | } else { |
201 | leaf = (rtree_leaf_elm_t *)atomic_load_p(&elm->child, |
202 | ATOMIC_ACQUIRE); |
203 | } |
204 | |
205 | assert(!dependent || leaf != NULL); |
206 | return leaf; |
207 | } |
208 | |
209 | static rtree_leaf_elm_t * |
210 | rtree_child_leaf_read(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *elm, |
211 | unsigned level, bool dependent) { |
212 | rtree_leaf_elm_t *leaf; |
213 | |
214 | leaf = rtree_child_leaf_tryread(elm, dependent); |
215 | if (!dependent && unlikely(!rtree_leaf_valid(leaf))) { |
216 | leaf = rtree_leaf_init(tsdn, rtree, &elm->child); |
217 | } |
218 | assert(!dependent || leaf != NULL); |
219 | return leaf; |
220 | } |
221 | |
222 | rtree_leaf_elm_t * |
223 | rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, |
224 | uintptr_t key, bool dependent, bool init_missing) { |
225 | rtree_node_elm_t *node; |
226 | rtree_leaf_elm_t *leaf; |
227 | #if RTREE_HEIGHT > 1 |
228 | node = rtree->root; |
229 | #else |
230 | leaf = rtree->root; |
231 | #endif |
232 | |
233 | if (config_debug) { |
234 | uintptr_t leafkey = rtree_leafkey(key); |
235 | for (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) { |
236 | assert(rtree_ctx->cache[i].leafkey != leafkey); |
237 | } |
238 | for (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) { |
239 | assert(rtree_ctx->l2_cache[i].leafkey != leafkey); |
240 | } |
241 | } |
242 | |
243 | #define RTREE_GET_CHILD(level) { \ |
244 | assert(level < RTREE_HEIGHT-1); \ |
245 | if (level != 0 && !dependent && \ |
246 | unlikely(!rtree_node_valid(node))) { \ |
247 | return NULL; \ |
248 | } \ |
249 | uintptr_t subkey = rtree_subkey(key, level); \ |
250 | if (level + 2 < RTREE_HEIGHT) { \ |
251 | node = init_missing ? \ |
252 | rtree_child_node_read(tsdn, rtree, \ |
253 | &node[subkey], level, dependent) : \ |
254 | rtree_child_node_tryread(&node[subkey], \ |
255 | dependent); \ |
256 | } else { \ |
257 | leaf = init_missing ? \ |
258 | rtree_child_leaf_read(tsdn, rtree, \ |
259 | &node[subkey], level, dependent) : \ |
260 | rtree_child_leaf_tryread(&node[subkey], \ |
261 | dependent); \ |
262 | } \ |
263 | } |
264 | /* |
265 | * Cache replacement upon hard lookup (i.e. L1 & L2 rtree cache miss): |
266 | * (1) evict last entry in L2 cache; (2) move the collision slot from L1 |
267 | * cache down to L2; and 3) fill L1. |
268 | */ |
269 | #define RTREE_GET_LEAF(level) { \ |
270 | assert(level == RTREE_HEIGHT-1); \ |
271 | if (!dependent && unlikely(!rtree_leaf_valid(leaf))) { \ |
272 | return NULL; \ |
273 | } \ |
274 | if (RTREE_CTX_NCACHE_L2 > 1) { \ |
275 | memmove(&rtree_ctx->l2_cache[1], \ |
276 | &rtree_ctx->l2_cache[0], \ |
277 | sizeof(rtree_ctx_cache_elm_t) * \ |
278 | (RTREE_CTX_NCACHE_L2 - 1)); \ |
279 | } \ |
280 | size_t slot = rtree_cache_direct_map(key); \ |
281 | rtree_ctx->l2_cache[0].leafkey = \ |
282 | rtree_ctx->cache[slot].leafkey; \ |
283 | rtree_ctx->l2_cache[0].leaf = \ |
284 | rtree_ctx->cache[slot].leaf; \ |
285 | uintptr_t leafkey = rtree_leafkey(key); \ |
286 | rtree_ctx->cache[slot].leafkey = leafkey; \ |
287 | rtree_ctx->cache[slot].leaf = leaf; \ |
288 | uintptr_t subkey = rtree_subkey(key, level); \ |
289 | return &leaf[subkey]; \ |
290 | } |
291 | if (RTREE_HEIGHT > 1) { |
292 | RTREE_GET_CHILD(0) |
293 | } |
294 | if (RTREE_HEIGHT > 2) { |
295 | RTREE_GET_CHILD(1) |
296 | } |
297 | if (RTREE_HEIGHT > 3) { |
298 | for (unsigned i = 2; i < RTREE_HEIGHT-1; i++) { |
299 | RTREE_GET_CHILD(i) |
300 | } |
301 | } |
302 | RTREE_GET_LEAF(RTREE_HEIGHT-1) |
303 | #undef RTREE_GET_CHILD |
304 | #undef RTREE_GET_LEAF |
305 | not_reached(); |
306 | } |
307 | |
308 | void |
309 | rtree_ctx_data_init(rtree_ctx_t *ctx) { |
310 | for (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) { |
311 | rtree_ctx_cache_elm_t *cache = &ctx->cache[i]; |
312 | cache->leafkey = RTREE_LEAFKEY_INVALID; |
313 | cache->leaf = NULL; |
314 | } |
315 | for (unsigned i = 0; i < RTREE_CTX_NCACHE_L2; i++) { |
316 | rtree_ctx_cache_elm_t *cache = &ctx->l2_cache[i]; |
317 | cache->leafkey = RTREE_LEAFKEY_INVALID; |
318 | cache->leaf = NULL; |
319 | } |
320 | } |
321 | |