microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.19.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/allocator/mimalloc-sys/mimalloc/src/bitmap.c

441lines · modeblame

91589c3aIan Davis2 years ago1/* ----------------------------------------------------------------------------
2Copyright (c) 2019-2023 Microsoft Research, Daan Leijen
3This is free software; you can redistribute it and/or modify it under the
4terms of the MIT license. A copy of the license can be found in the file
5"LICENSE" at the root of this distribution.
6-----------------------------------------------------------------------------*/
7
8/* ----------------------------------------------------------------------------
9Concurrent bitmap that can set/reset sequences of bits atomically,
ce21e681Ian Davis11 months ago10represented as an array of fields where each field is a machine word (`size_t`)
91589c3aIan Davis2 years ago11
12There are two api's; the standard one cannot have sequences that cross
13between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS).
14
15The `_across` postfixed functions do allow sequences that can cross over
16between the fields. (This is used in arena allocation)
17---------------------------------------------------------------------------- */
18
19#include "mimalloc.h"
20#include "mimalloc/internal.h"
21#include "bitmap.h"
22
23/* -----------------------------------------------------------
24Bitmap definition
25----------------------------------------------------------- */
26
27// The bit mask for a given number of blocks at a specified bit index.
28static inline size_t mi_bitmap_mask_(size_t count, size_t bitidx) {
29mi_assert_internal(count + bitidx <= MI_BITMAP_FIELD_BITS);
30mi_assert_internal(count > 0);
31if (count >= MI_BITMAP_FIELD_BITS) return MI_BITMAP_FIELD_FULL;
32if (count == 0) return 0;
33return ((((size_t)1 << count) - 1) << bitidx);
34}
35
36
37/* -----------------------------------------------------------
38Claim a bit sequence atomically
39----------------------------------------------------------- */
40
41// Try to atomically claim a sequence of `count` bits in a single
42// field at `idx` in `bitmap`. Returns `true` on success.
43inline bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx)
44{
45mi_assert_internal(bitmap_idx != NULL);
46mi_assert_internal(count <= MI_BITMAP_FIELD_BITS);
47mi_assert_internal(count > 0);
48mi_bitmap_field_t* field = &bitmap[idx];
49size_t map = mi_atomic_load_relaxed(field);
50if (map==MI_BITMAP_FIELD_FULL) return false; // short cut
51
52// search for 0-bit sequence of length count
53const size_t mask = mi_bitmap_mask_(count, 0);
54const size_t bitidx_max = MI_BITMAP_FIELD_BITS - count;
55
56#ifdef MI_HAVE_FAST_BITSCAN
57size_t bitidx = mi_ctz(~map); // quickly find the first zero bit if possible
58#else
59size_t bitidx = 0; // otherwise start at 0
60#endif
61size_t m = (mask << bitidx); // invariant: m == mask shifted by bitidx
62
63// scan linearly for a free range of zero bits
64while (bitidx <= bitidx_max) {
65const size_t mapm = (map & m);
66if (mapm == 0) { // are the mask bits free at bitidx?
67mi_assert_internal((m >> bitidx) == mask); // no overflow?
68const size_t newmap = (map | m);
69mi_assert_internal((newmap^map) >> bitidx == mask);
70if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { // TODO: use weak cas here?
71// no success, another thread claimed concurrently.. keep going (with updated `map`)
72continue;
73}
74else {
75// success, we claimed the bits!
76*bitmap_idx = mi_bitmap_index_create(idx, bitidx);
77return true;
78}
79}
80else {
81// on to the next bit range
82#ifdef MI_HAVE_FAST_BITSCAN
83mi_assert_internal(mapm != 0);
ce21e681Ian Davis11 months ago84const size_t shift = (count == 1 ? 1 : (MI_SIZE_BITS - mi_clz(mapm) - bitidx));
91589c3aIan Davis2 years ago85mi_assert_internal(shift > 0 && shift <= count);
86#else
87const size_t shift = 1;
88#endif
89bitidx += shift;
90m <<= shift;
91}
92}
93// no bits found
94return false;
95}
96
97// Find `count` bits of 0 and set them to 1 atomically; returns `true` on success.
98// Starts at idx, and wraps around to search in all `bitmap_fields` fields.
99// `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields.
100bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx) {
101size_t idx = start_field_idx;
102for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
103if (idx >= bitmap_fields) { idx = 0; } // wrap
104if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
105return true;
106}
107}
108return false;
109}
110
111// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled
112bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields,
113const size_t start_field_idx, const size_t count,
114mi_bitmap_pred_fun_t pred_fun, void* pred_arg,
115mi_bitmap_index_t* bitmap_idx) {
116size_t idx = start_field_idx;
117for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
118if (idx >= bitmap_fields) idx = 0; // wrap
119if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
120if (pred_fun == NULL || pred_fun(*bitmap_idx, pred_arg)) {
121return true;
122}
123// predicate returned false, unclaim and look further
124_mi_bitmap_unclaim(bitmap, bitmap_fields, count, *bitmap_idx);
125}
126}
127return false;
128}
129
130// Set `count` bits at `bitmap_idx` to 0 atomically
131// Returns `true` if all `count` bits were 1 previously.
132bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
133const size_t idx = mi_bitmap_index_field(bitmap_idx);
134const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
135const size_t mask = mi_bitmap_mask_(count, bitidx);
136mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
137// mi_assert_internal((bitmap[idx] & mask) == mask);
138const size_t prev = mi_atomic_and_acq_rel(&bitmap[idx], ~mask);
139return ((prev & mask) == mask);
140}
141
142
143// Set `count` bits at `bitmap_idx` to 1 atomically
144// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
145bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero) {
146const size_t idx = mi_bitmap_index_field(bitmap_idx);
147const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
148const size_t mask = mi_bitmap_mask_(count, bitidx);
149mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
150//mi_assert_internal(any_zero != NULL || (bitmap[idx] & mask) == 0);
151size_t prev = mi_atomic_or_acq_rel(&bitmap[idx], mask);
152if (any_zero != NULL) { *any_zero = ((prev & mask) != mask); }
153return ((prev & mask) == 0);
154}
155
156// Returns `true` if all `count` bits were 1. `any_ones` is `true` if there was at least one bit set to one.
157static bool mi_bitmap_is_claimedx(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_ones) {
158const size_t idx = mi_bitmap_index_field(bitmap_idx);
159const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
160const size_t mask = mi_bitmap_mask_(count, bitidx);
161mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
162const size_t field = mi_atomic_load_relaxed(&bitmap[idx]);
163if (any_ones != NULL) { *any_ones = ((field & mask) != 0); }
164return ((field & mask) == mask);
165}
166
ce21e681Ian Davis11 months ago167// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically.
91589c3aIan Davis2 years ago168// Returns `true` if successful when all previous `count` bits were 0.
169bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
170const size_t idx = mi_bitmap_index_field(bitmap_idx);
171const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
172const size_t mask = mi_bitmap_mask_(count, bitidx);
173mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
174size_t expected = mi_atomic_load_relaxed(&bitmap[idx]);
ce21e681Ian Davis11 months ago175do {
91589c3aIan Davis2 years ago176if ((expected & mask) != 0) return false;
ce21e681Ian Davis11 months ago177}
91589c3aIan Davis2 years ago178while (!mi_atomic_cas_strong_acq_rel(&bitmap[idx], &expected, expected | mask));
179mi_assert_internal((expected & mask) == 0);
180return true;
181}
182
183
184bool _mi_bitmap_is_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
185return mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, NULL);
186}
187
188bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
189bool any_ones;
190mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, &any_ones);
191return any_ones;
192}
193
194
195//--------------------------------------------------------------------------
196// the `_across` functions work on bitmaps where sequences can cross over
197// between the fields. This is used in arena allocation
198//--------------------------------------------------------------------------
199
200// Try to atomically claim a sequence of `count` bits starting from the field
201// at `idx` in `bitmap` and crossing into subsequent fields. Returns `true` on success.
202// Only needs to consider crossing into the next fields (see `mi_bitmap_try_find_from_claim_across`)
203static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t idx, const size_t count, const size_t retries, mi_bitmap_index_t* bitmap_idx)
204{
205mi_assert_internal(bitmap_idx != NULL);
206
207// check initial trailing zeros
208mi_bitmap_field_t* field = &bitmap[idx];
209size_t map = mi_atomic_load_relaxed(field);
210const size_t initial = mi_clz(map); // count of initial zeros starting at idx
211mi_assert_internal(initial <= MI_BITMAP_FIELD_BITS);
212if (initial == 0) return false;
213if (initial >= count) return _mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx); // no need to cross fields (this case won't happen for us)
214if (_mi_divide_up(count - initial, MI_BITMAP_FIELD_BITS) >= (bitmap_fields - idx)) return false; // not enough entries
ce21e681Ian Davis11 months ago215
91589c3aIan Davis2 years ago216// scan ahead
217size_t found = initial;
218size_t mask = 0; // mask bits for the final field
219while(found < count) {
220field++;
221map = mi_atomic_load_relaxed(field);
222const size_t mask_bits = (found + MI_BITMAP_FIELD_BITS <= count ? MI_BITMAP_FIELD_BITS : (count - found));
223mi_assert_internal(mask_bits > 0 && mask_bits <= MI_BITMAP_FIELD_BITS);
224mask = mi_bitmap_mask_(mask_bits, 0);
225if ((map & mask) != 0) return false; // some part is already claimed
226found += mask_bits;
227}
228mi_assert_internal(field < &bitmap[bitmap_fields]);
229
230// we found a range of contiguous zeros up to the final field; mask contains mask in the final field
231// now try to claim the range atomically
232mi_bitmap_field_t* const final_field = field;
233const size_t final_mask = mask;
234mi_bitmap_field_t* const initial_field = &bitmap[idx];
235const size_t initial_idx = MI_BITMAP_FIELD_BITS - initial;
236const size_t initial_mask = mi_bitmap_mask_(initial, initial_idx);
237
238// initial field
239size_t newmap;
240field = initial_field;
241map = mi_atomic_load_relaxed(field);
242do {
243newmap = (map | initial_mask);
244if ((map & initial_mask) != 0) { goto rollback; };
245} while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
246
247// intermediate fields
248while (++field < final_field) {
249newmap = MI_BITMAP_FIELD_FULL;
250map = 0;
251if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { goto rollback; }
252}
253
254// final field
255mi_assert_internal(field == final_field);
256map = mi_atomic_load_relaxed(field);
257do {
258newmap = (map | final_mask);
259if ((map & final_mask) != 0) { goto rollback; }
260} while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
261
262// claimed!
263*bitmap_idx = mi_bitmap_index_create(idx, initial_idx);
264return true;
265
266rollback:
267// roll back intermediate fields
268// (we just failed to claim `field` so decrement first)
269while (--field > initial_field) {
270newmap = 0;
271map = MI_BITMAP_FIELD_FULL;
272mi_assert_internal(mi_atomic_load_relaxed(field) == map);
273mi_atomic_store_release(field, newmap);
274}
275if (field == initial_field) { // (if we failed on the initial field, `field + 1 == initial_field`)
276map = mi_atomic_load_relaxed(field);
277do {
278mi_assert_internal((map & initial_mask) == initial_mask);
279newmap = (map & ~initial_mask);
280} while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
281}
ce21e681Ian Davis11 months ago282mi_stat_counter_increase(_mi_stats_main.arena_rollback_count,1);
91589c3aIan Davis2 years ago283// retry? (we make a recursive call instead of goto to be able to use const declarations)
284if (retries <= 2) {
285return mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, retries+1, bitmap_idx);
286}
287else {
288return false;
289}
290}
291
292
293// Find `count` bits of zeros and set them to 1 atomically; returns `true` on success.
294// Starts at idx, and wraps around to search in all `bitmap_fields` fields.
295bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx) {
296mi_assert_internal(count > 0);
297if (count <= 2) {
298// we don't bother with crossover fields for small counts
299return _mi_bitmap_try_find_from_claim(bitmap, bitmap_fields, start_field_idx, count, bitmap_idx);
300}
301
302// visit the fields
303size_t idx = start_field_idx;
304for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
305if (idx >= bitmap_fields) { idx = 0; } // wrap
306// first try to claim inside a field
ce21e681Ian Davis11 months ago307/*
91589c3aIan Davis2 years ago308if (count <= MI_BITMAP_FIELD_BITS) {
309if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
310return true;
311}
312}
ce21e681Ian Davis11 months ago313*/
91589c3aIan Davis2 years ago314// if that fails, then try to claim across fields
315if (mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, 0, bitmap_idx)) {
316return true;
317}
318}
319return false;
320}
321
322// Helper for masks across fields; returns the mid count, post_mask may be 0
323static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, size_t* pre_mask, size_t* mid_mask, size_t* post_mask) {
324MI_UNUSED(bitmap_fields);
325const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
326if mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS) {
327*pre_mask = mi_bitmap_mask_(count, bitidx);
328*mid_mask = 0;
329*post_mask = 0;
330mi_assert_internal(mi_bitmap_index_field(bitmap_idx) < bitmap_fields);
331return 0;
332}
333else {
334const size_t pre_bits = MI_BITMAP_FIELD_BITS - bitidx;
335mi_assert_internal(pre_bits < count);
336*pre_mask = mi_bitmap_mask_(pre_bits, bitidx);
337count -= pre_bits;
338const size_t mid_count = (count / MI_BITMAP_FIELD_BITS);
339*mid_mask = MI_BITMAP_FIELD_FULL;
340count %= MI_BITMAP_FIELD_BITS;
341*post_mask = (count==0 ? 0 : mi_bitmap_mask_(count, 0));
342mi_assert_internal(mi_bitmap_index_field(bitmap_idx) + mid_count + (count==0 ? 0 : 1) < bitmap_fields);
343return mid_count;
344}
345}
346
347// Set `count` bits at `bitmap_idx` to 0 atomically
348// Returns `true` if all `count` bits were 1 previously.
349bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
350size_t idx = mi_bitmap_index_field(bitmap_idx);
351size_t pre_mask;
352size_t mid_mask;
353size_t post_mask;
354size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
355bool all_one = true;
356mi_bitmap_field_t* field = &bitmap[idx];
357size_t prev = mi_atomic_and_acq_rel(field++, ~pre_mask); // clear first part
358if ((prev & pre_mask) != pre_mask) all_one = false;
359while(mid_count-- > 0) {
360prev = mi_atomic_and_acq_rel(field++, ~mid_mask); // clear mid part
361if ((prev & mid_mask) != mid_mask) all_one = false;
362}
363if (post_mask!=0) {
364prev = mi_atomic_and_acq_rel(field, ~post_mask); // clear end part
365if ((prev & post_mask) != post_mask) all_one = false;
366}
367return all_one;
368}
369
370// Set `count` bits at `bitmap_idx` to 1 atomically
371// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
ce21e681Ian Davis11 months ago372bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero, size_t* already_set) {
91589c3aIan Davis2 years ago373size_t idx = mi_bitmap_index_field(bitmap_idx);
374size_t pre_mask;
375size_t mid_mask;
376size_t post_mask;
377size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
378bool all_zero = true;
379bool any_zero = false;
ce21e681Ian Davis11 months ago380size_t one_count = 0;
91589c3aIan Davis2 years ago381_Atomic(size_t)*field = &bitmap[idx];
382size_t prev = mi_atomic_or_acq_rel(field++, pre_mask);
ce21e681Ian Davis11 months ago383if ((prev & pre_mask) != 0) { all_zero = false; one_count += mi_popcount(prev & pre_mask); }
91589c3aIan Davis2 years ago384if ((prev & pre_mask) != pre_mask) any_zero = true;
385while (mid_count-- > 0) {
386prev = mi_atomic_or_acq_rel(field++, mid_mask);
ce21e681Ian Davis11 months ago387if ((prev & mid_mask) != 0) { all_zero = false; one_count += mi_popcount(prev & mid_mask); }
91589c3aIan Davis2 years ago388if ((prev & mid_mask) != mid_mask) any_zero = true;
389}
390if (post_mask!=0) {
391prev = mi_atomic_or_acq_rel(field, post_mask);
ce21e681Ian Davis11 months ago392if ((prev & post_mask) != 0) { all_zero = false; one_count += mi_popcount(prev & post_mask); }
91589c3aIan Davis2 years ago393if ((prev & post_mask) != post_mask) any_zero = true;
394}
395if (pany_zero != NULL) { *pany_zero = any_zero; }
ce21e681Ian Davis11 months ago396if (already_set != NULL) { *already_set = one_count; };
397mi_assert_internal(all_zero ? one_count == 0 : one_count <= count);
91589c3aIan Davis2 years ago398return all_zero;
399}
400
401
402// Returns `true` if all `count` bits were 1.
403// `any_ones` is `true` if there was at least one bit set to one.
ce21e681Ian Davis11 months ago404static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones, size_t* already_set) {
91589c3aIan Davis2 years ago405size_t idx = mi_bitmap_index_field(bitmap_idx);
406size_t pre_mask;
407size_t mid_mask;
408size_t post_mask;
409size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
410bool all_ones = true;
411bool any_ones = false;
ce21e681Ian Davis11 months ago412size_t one_count = 0;
91589c3aIan Davis2 years ago413mi_bitmap_field_t* field = &bitmap[idx];
414size_t prev = mi_atomic_load_relaxed(field++);
415if ((prev & pre_mask) != pre_mask) all_ones = false;
ce21e681Ian Davis11 months ago416if ((prev & pre_mask) != 0) { any_ones = true; one_count += mi_popcount(prev & pre_mask); }
91589c3aIan Davis2 years ago417while (mid_count-- > 0) {
418prev = mi_atomic_load_relaxed(field++);
419if ((prev & mid_mask) != mid_mask) all_ones = false;
ce21e681Ian Davis11 months ago420if ((prev & mid_mask) != 0) { any_ones = true; one_count += mi_popcount(prev & mid_mask); }
91589c3aIan Davis2 years ago421}
422if (post_mask!=0) {
423prev = mi_atomic_load_relaxed(field);
424if ((prev & post_mask) != post_mask) all_ones = false;
ce21e681Ian Davis11 months ago425if ((prev & post_mask) != 0) { any_ones = true; one_count += mi_popcount(prev & post_mask); }
91589c3aIan Davis2 years ago426}
427if (pany_ones != NULL) { *pany_ones = any_ones; }
ce21e681Ian Davis11 months ago428if (already_set != NULL) { *already_set = one_count; }
429mi_assert_internal(all_ones ? one_count == count : one_count < count);
91589c3aIan Davis2 years ago430return all_ones;
431}
432
ce21e681Ian Davis11 months ago433bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, size_t* already_set) {
434return mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, NULL, already_set);
91589c3aIan Davis2 years ago435}
436
437bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
438bool any_ones;
ce21e681Ian Davis11 months ago439mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, &any_ones, NULL);
91589c3aIan Davis2 years ago440return any_ones;
441}