mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-06 23:39:31 +03:00
initial no more pairmap
This commit is contained in:
parent
7443ee317e
commit
ec9c61c066
5 changed files with 465 additions and 711 deletions
|
@ -700,7 +700,9 @@ static inline bool mi_page_try_claim_ownership(mi_page_t* page) {
|
|||
return ((old&1)==0);
|
||||
}
|
||||
|
||||
static inline void _mi_page_unown(mi_page_t* page) {
|
||||
// release ownership of a page. This may free the page if all blocks were concurrently
|
||||
// freed in the meantime. Returns true if the page was freed.
|
||||
static inline bool _mi_page_unown(mi_page_t* page) {
|
||||
mi_assert_internal(mi_page_is_owned(page));
|
||||
mi_assert_internal(mi_page_is_abandoned(page));
|
||||
mi_thread_free_t tf_new;
|
||||
|
@ -712,13 +714,14 @@ static inline void _mi_page_unown(mi_page_t* page) {
|
|||
if (mi_page_all_free(page)) { // it may become free just before unowning it
|
||||
_mi_arena_page_unabandon(page);
|
||||
_mi_arena_page_free(page);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
tf_old = mi_atomic_load_relaxed(&page->xthread_free);
|
||||
}
|
||||
mi_assert_internal(mi_tf_block(tf_old)==NULL);
|
||||
tf_new = mi_tf_create(NULL, false);
|
||||
} while (!mi_atomic_cas_weak_release(&page->xthread_free, &tf_old, tf_new));
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
|
|
|
@ -117,16 +117,16 @@ terms of the MIT license. A copy of the license can be found in the file
|
|||
#define MI_ARENA_SLICE_SHIFT (13 + MI_SIZE_SHIFT) // 64 KiB (32 KiB on 32-bit)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MI_BITMAP_CHUNK_BITS_SHIFT
|
||||
#define MI_BITMAP_CHUNK_BITS_SHIFT (6 + MI_SIZE_SHIFT) // optimized for 512 bits per chunk (avx512)
|
||||
#ifndef MI_BCHUNK_BITS_SHIFT
|
||||
#define MI_BCHUNK_BITS_SHIFT (6 + MI_SIZE_SHIFT) // optimized for 512 bits per chunk (avx512)
|
||||
#endif
|
||||
|
||||
#define MI_BITMAP_CHUNK_BITS (1 << MI_BITMAP_CHUNK_BITS_SHIFT)
|
||||
#define MI_BCHUNK_BITS (1 << MI_BCHUNK_BITS_SHIFT)
|
||||
#define MI_ARENA_SLICE_SIZE (MI_ZU(1) << MI_ARENA_SLICE_SHIFT)
|
||||
#define MI_ARENA_SLICE_ALIGN (MI_ARENA_SLICE_SIZE)
|
||||
|
||||
#define MI_ARENA_MIN_OBJ_SLICES (1)
|
||||
#define MI_ARENA_MAX_OBJ_SLICES (MI_BITMAP_CHUNK_BITS) // 32 MiB (for now, cannot cross chunk boundaries)
|
||||
#define MI_ARENA_MAX_OBJ_SLICES (MI_BCHUNK_BITS) // 32 MiB (for now, cannot cross chunk boundaries)
|
||||
|
||||
#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_MIN_OBJ_SLICES * MI_ARENA_SLICE_SIZE)
|
||||
#define MI_ARENA_MAX_OBJ_SIZE (MI_ARENA_MAX_OBJ_SLICES * MI_ARENA_SLICE_SIZE)
|
||||
|
|
62
src/arena.c
62
src/arena.c
|
@ -48,7 +48,7 @@ typedef struct mi_arena_s {
|
|||
mi_bitmap_t* slices_committed; // is the slice committed? (i.e. accessible)
|
||||
mi_bitmap_t* slices_purge; // can the slice be purged? (slice in purge => slice in free)
|
||||
mi_bitmap_t* slices_dirty; // is the slice potentially non-zero?
|
||||
mi_pairmap_t pages_abandoned[MI_BIN_COUNT]; // abandoned pages per size bin (a set bit means the start of the page)
|
||||
mi_bitmap_t* pages_abandoned[MI_BIN_COUNT]; // abandoned pages per size bin (a set bit means the start of the page)
|
||||
// the full queue contains abandoned full pages
|
||||
// followed by the bitmaps (whose size depends on the arena size)
|
||||
} mi_arena_t;
|
||||
|
@ -476,16 +476,24 @@ void* _mi_arena_alloc(size_t size, bool commit, bool allow_large, mi_arena_id_t
|
|||
Arena page allocation
|
||||
----------------------------------------------------------- */
|
||||
|
||||
static bool mi_arena_claim_abandoned(size_t slice_index, void* arg1, void* arg2) {
|
||||
mi_arena_t* arena = (mi_arena_t*)arg1;
|
||||
mi_subproc_t* subproc = (mi_subproc_t*)arg2;
|
||||
|
||||
static bool mi_arena_claim_abandoned(size_t slice_index, void* arg1, void* arg2, bool* keep_abandoned) {
|
||||
// found an abandoned page of the right size
|
||||
// it is set busy for now so we can read safely even with concurrent mi_free reclaiming
|
||||
// try to claim ownership atomically
|
||||
mi_page_t* page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
|
||||
if (subproc != page->subproc) return false;
|
||||
if (!mi_page_try_claim_ownership(page)) return false;
|
||||
mi_arena_t* const arena = (mi_arena_t*)arg1;
|
||||
mi_subproc_t* const subproc = (mi_subproc_t*)arg2;
|
||||
mi_page_t* const page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
|
||||
// can we claim ownership?
|
||||
if (!mi_page_try_claim_ownership(page)) {
|
||||
*keep_abandoned = true;
|
||||
return false;
|
||||
}
|
||||
if (subproc != page->subproc) {
|
||||
// wrong sub-process.. we need to unown again, and perhaps not keep it abandoned
|
||||
const bool freed = _mi_page_unown(page);
|
||||
*keep_abandoned = !freed;
|
||||
return false;
|
||||
}
|
||||
// yes, we can reclaim it
|
||||
*keep_abandoned = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -505,9 +513,9 @@ static mi_page_t* mi_arena_page_try_find_abandoned(size_t slice_count, size_t bl
|
|||
mi_forall_arenas(req_arena_id, allow_large, tseq, arena_id, arena)
|
||||
{
|
||||
size_t slice_index;
|
||||
mi_pairmap_t* const pairmap = &arena->pages_abandoned[bin];
|
||||
mi_bitmap_t* const bitmap = arena->pages_abandoned[bin];
|
||||
|
||||
if (mi_pairmap_try_find_and_set_busy(pairmap, tseq, &slice_index, &mi_arena_claim_abandoned, arena, subproc)) {
|
||||
if (mi_bitmap_try_find_and_claim(bitmap, tseq, &slice_index, &mi_arena_claim_abandoned, arena, subproc)) {
|
||||
// found an abandoned page of the right size
|
||||
// and claimed ownership.
|
||||
mi_page_t* page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
|
||||
|
@ -694,7 +702,7 @@ void _mi_arena_page_free(mi_page_t* page) {
|
|||
mi_assert_internal(mi_bitmap_is_clearN(arena->slices_free, slice_index, slice_count));
|
||||
mi_assert_internal(mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count));
|
||||
mi_assert_internal(mi_bitmap_is_clearN(arena->slices_purge, slice_index, slice_count));
|
||||
mi_assert_internal(mi_pairmap_is_clear(&arena->pages_abandoned[bin], slice_index));
|
||||
mi_assert_internal(mi_bitmap_is_clearN(arena->pages_abandoned[bin], slice_index, 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -728,8 +736,8 @@ static void mi_arena_page_abandon_no_stat(mi_page_t* page) {
|
|||
mi_assert_internal(mi_bitmap_is_setN(arena->slices_dirty, slice_index, slice_count));
|
||||
|
||||
mi_page_set_abandoned_mapped(page);
|
||||
bool were_zero = mi_pairmap_set(&arena->pages_abandoned[bin], slice_index);
|
||||
MI_UNUSED(were_zero); mi_assert_internal(were_zero);
|
||||
const bool wasclear = mi_bitmap_set(arena->pages_abandoned[bin], slice_index);
|
||||
MI_UNUSED(wasclear); mi_assert_internal(wasclear);
|
||||
mi_atomic_increment_relaxed(&subproc->abandoned_count[bin]);
|
||||
}
|
||||
else {
|
||||
|
@ -783,7 +791,7 @@ void _mi_arena_page_unabandon(mi_page_t* page) {
|
|||
mi_assert_internal(mi_bitmap_is_clearN(arena->slices_purge, slice_index, slice_count));
|
||||
|
||||
// this busy waits until a concurrent reader (from alloc_abandoned) is done
|
||||
mi_pairmap_clear_once_not_busy(&arena->pages_abandoned[bin], slice_index);
|
||||
mi_bitmap_clear_once_set(arena->pages_abandoned[bin], slice_index);
|
||||
mi_page_clear_abandoned_mapped(page);
|
||||
mi_atomic_decrement_relaxed(&page->subproc->abandoned_count[bin]);
|
||||
}
|
||||
|
@ -956,12 +964,12 @@ static bool mi_arena_add(mi_arena_t* arena, mi_arena_id_t* arena_id, mi_stats_t*
|
|||
}
|
||||
|
||||
static size_t mi_arena_info_slices_needed(size_t slice_count, size_t* bitmap_base) {
|
||||
if (slice_count == 0) slice_count = MI_BITMAP_CHUNK_BITS;
|
||||
mi_assert_internal((slice_count % MI_BITMAP_CHUNK_BITS) == 0);
|
||||
const size_t base_size = _mi_align_up(sizeof(mi_arena_t), MI_BITMAP_CHUNK_SIZE);
|
||||
const size_t bitmaps_size = 4 * mi_bitmap_size(slice_count,NULL);
|
||||
const size_t pairmaps_size = MI_BIN_COUNT * 2 * mi_bitmap_size(slice_count,NULL);
|
||||
const size_t size = base_size + bitmaps_size + pairmaps_size;
|
||||
if (slice_count == 0) slice_count = MI_BCHUNK_BITS;
|
||||
mi_assert_internal((slice_count % MI_BCHUNK_BITS) == 0);
|
||||
const size_t base_size = _mi_align_up(sizeof(mi_arena_t), MI_BCHUNK_SIZE);
|
||||
const size_t bitmaps_count = 4 + MI_BIN_COUNT; // free, commit, dirty, purge, and abandonded
|
||||
const size_t bitmaps_size = bitmaps_count * mi_bitmap_size(slice_count,NULL);
|
||||
const size_t size = base_size + bitmaps_size;
|
||||
|
||||
const size_t os_page_size = _mi_os_page_size();
|
||||
const size_t info_size = _mi_align_up(size, os_page_size) + os_page_size; // + guard page
|
||||
|
@ -992,7 +1000,7 @@ static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int
|
|||
|
||||
if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); }
|
||||
|
||||
const size_t slice_count = _mi_align_down(size / MI_ARENA_SLICE_SIZE, MI_BITMAP_CHUNK_BITS);
|
||||
const size_t slice_count = _mi_align_down(size / MI_ARENA_SLICE_SIZE, MI_BCHUNK_BITS);
|
||||
if (slice_count > MI_BITMAP_MAX_BIT_COUNT) { // 16 GiB for now
|
||||
// todo: allow larger areas (either by splitting it up in arena's or having larger arena's)
|
||||
_mi_warning_message("cannot use OS memory since it is too large (size %zu MiB, maximum is %zu MiB)", size/MI_MiB, mi_size_of_slices(MI_BITMAP_MAX_BIT_COUNT)/MI_MiB);
|
||||
|
@ -1034,7 +1042,7 @@ static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int
|
|||
arena->slices_dirty = mi_arena_bitmap_init(slice_count,&base);
|
||||
arena->slices_purge = mi_arena_bitmap_init(slice_count,&base);
|
||||
for( size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) {
|
||||
mi_pairmap_init(&arena->pages_abandoned[i], mi_arena_bitmap_init(slice_count, &base), mi_arena_bitmap_init(slice_count, &base));
|
||||
arena->pages_abandoned[i] = mi_arena_bitmap_init(slice_count,&base);
|
||||
}
|
||||
mi_assert_internal(mi_size_of_slices(info_slices) >= (size_t)(base - mi_arena_start(arena)));
|
||||
|
||||
|
@ -1112,9 +1120,9 @@ static size_t mi_debug_show_bitmap(const char* prefix, const char* header, size_
|
|||
size_t bit_count = 0;
|
||||
size_t bit_set_count = 0;
|
||||
for (size_t i = 0; i < mi_bitmap_chunk_count(bitmap) && bit_count < slice_count; i++) {
|
||||
char buf[MI_BITMAP_CHUNK_BITS + 64]; _mi_memzero(buf, sizeof(buf));
|
||||
mi_bitmap_chunk_t* chunk = &bitmap->chunks[i];
|
||||
for (size_t j = 0, k = 0; j < MI_BITMAP_CHUNK_FIELDS; j++) {
|
||||
char buf[MI_BCHUNK_BITS + 64]; _mi_memzero(buf, sizeof(buf));
|
||||
mi_bchunk_t* chunk = &bitmap->chunks[i];
|
||||
for (size_t j = 0, k = 0; j < MI_BCHUNK_FIELDS; j++) {
|
||||
if (j > 0 && (j % 4) == 0) {
|
||||
buf[k++] = '\n';
|
||||
_mi_memcpy(buf+k, prefix, strlen(prefix)); k += strlen(prefix);
|
||||
|
|
929
src/bitmap.c
929
src/bitmap.c
File diff suppressed because it is too large
Load diff
148
src/bitmap.h
148
src/bitmap.h
|
@ -19,35 +19,34 @@ Concurrent bitmap that can set/reset sequences of bits atomically
|
|||
each bit usually represents a single MI_ARENA_SLICE_SIZE in an arena (64 KiB).
|
||||
We need 16K bits to represent a 1GiB arena.
|
||||
|
||||
`mi_bitmap_chunk_t`: a chunk of bfield's of a total of MI_BITMAP_CHUNK_BITS (= 512)
|
||||
`mi_bchunk_t`: a chunk of bfield's of a total of MI_BCHUNK_BITS (= 512 on 64-bit, 256 on 32-bit)
|
||||
allocations never span across chunks -- so MI_ARENA_MAX_OBJ_SIZE is the number
|
||||
of bits in a chunk times the MI_ARENA_SLICE_SIZE (512 * 64KiB = 32 MiB).
|
||||
These chunks are cache-aligned and we can use AVX2/AVX512/SVE/SVE2/etc. instructions
|
||||
These chunks are cache-aligned and we can use AVX2/AVX512/NEON/SVE/SVE2/etc. instructions
|
||||
to scan for bits (perhaps) more efficiently.
|
||||
|
||||
`mi_chunkmap_t`: for each chunk we track if it has (potentially) any bit set.
|
||||
`mi_bchunkmap_t` == `mi_bchunk_t`: for each chunk we track if it has (potentially) any bit set.
|
||||
The chunkmap has 1 bit per chunk that is set if the chunk potentially has a bit set.
|
||||
This is used to avoid scanning every chunk. (and thus strictly an optimization)
|
||||
It is conservative: it is fine to a bit in the chunk map even if the chunk turns out
|
||||
to have no bits set.
|
||||
to have no bits set. It is also allowed to briefly have a clear bit even if the
|
||||
chunk has bits set, as long as we guarantee that we set the bit later on -- this
|
||||
allows us to set the chunkmap bit after we set a bit in the corresponding chunk.
|
||||
|
||||
When we (potentially) set a bit in a chunk, we first update the chunkmap.
|
||||
However, when we clear a bit in a chunk, and the chunk is indeed all clear, we
|
||||
cannot safely clear the bit corresponding to the chunk in the chunkmap since it
|
||||
may race with another thread setting a bit in the same chunk (and we may clear the
|
||||
bit even though a bit is set in the chunk which is not allowed).
|
||||
may race with another thread setting a bit in the same chunk. Therefore, when
|
||||
clearing, we first test if a chunk is clear, then clear the chunkmap bit, and
|
||||
then test again to catch any set bits that we missed.
|
||||
|
||||
To fix this, the chunkmap contains 32-bits of bits for chunks, and a 32-bit "epoch"
|
||||
counter that is increased everytime a bit is set. We only clear a bit if the epoch
|
||||
stayed the same over our clear operation (so we know no other thread in the mean
|
||||
time set a bit in any of the chunks corresponding to the chunkmap).
|
||||
Since increasing the epoch and setting a bit must be atomic, we use only half-word
|
||||
bits (32) (we could use 128-bit atomics if needed since modern hardware supports this)
|
||||
Since the chunkmap may thus be briefly out-of-sync, this means that we may sometimes
|
||||
not find a free page even though it's there (but we accept this as we avoid taking
|
||||
full locks). (Another way to do this is to use an epoch but we like to avoid that complexity
|
||||
for now).
|
||||
|
||||
`mi_bitmap_t`: a bitmap with N chunks. A bitmap always has MI_BITMAP_MAX_CHUNK_FIELDS (=16)
|
||||
and can support arena's from few chunks up to 16 chunkmap's = 16 * 32 chunks = 16 GiB
|
||||
The `chunk_count` can be anything from 1 to the max supported by the chunkmap's but
|
||||
each chunk is always complete (512 bits, so 512 * 64KiB = 32MiB memory area's).
|
||||
`mi_bitmap_t`: a bitmap with N chunks. A bitmap has a chunkmap of MI_BCHUNK_BITS (512)
|
||||
and thus has at most 512 chunks (=2^18 bits x 64 KiB slices = 16 GiB max arena size).
|
||||
The minimum is 1 chunk which is a 32 MiB arena.
|
||||
|
||||
For now, the implementation assumes MI_HAS_FAST_BITSCAN and uses trailing-zero-count
|
||||
and pop-count (but we think it can be adapted work reasonably well on older hardware too)
|
||||
|
@ -59,57 +58,46 @@ typedef size_t mi_bfield_t;
|
|||
#define MI_BFIELD_BITS_SHIFT (MI_SIZE_SHIFT+3)
|
||||
#define MI_BFIELD_BITS (1 << MI_BFIELD_BITS_SHIFT)
|
||||
#define MI_BFIELD_SIZE (MI_BFIELD_BITS/8)
|
||||
#define MI_BFIELD_BITS_MOD_MASK (MI_BFIELD_BITS - 1)
|
||||
#define MI_BFIELD_LO_BIT8 (((~(mi_bfield_t)0))/0xFF) // 0x01010101 ..
|
||||
#define MI_BFIELD_HI_BIT8 (MI_BFIELD_LO_BIT8 << 7) // 0x80808080 ..
|
||||
|
||||
#define MI_BITMAP_CHUNK_SIZE (MI_BITMAP_CHUNK_BITS / 8)
|
||||
#define MI_BITMAP_CHUNK_FIELDS (MI_BITMAP_CHUNK_BITS / MI_BFIELD_BITS)
|
||||
#define MI_BITMAP_CHUNK_BITS_MOD_MASK (MI_BITMAP_CHUNK_BITS - 1)
|
||||
|
||||
// A bitmap chunk contains 512 bits of bfields on 64_bit (256 on 32-bit)
|
||||
typedef mi_decl_align(MI_BITMAP_CHUNK_SIZE) struct mi_bitmap_chunk_s {
|
||||
_Atomic(mi_bfield_t) bfields[MI_BITMAP_CHUNK_FIELDS];
|
||||
} mi_bitmap_chunk_t;
|
||||
#define MI_BCHUNK_SIZE (MI_BCHUNK_BITS / 8)
|
||||
#define MI_BCHUNK_FIELDS (MI_BCHUNK_BITS / MI_BFIELD_BITS) // 8 on both 64- and 32-bit
|
||||
|
||||
|
||||
// for now 32-bit epoch + 32-bit bit-set (note: with ABA instructions we can double this)
|
||||
typedef uint64_t mi_chunkmap_t;
|
||||
typedef uint32_t mi_epoch_t;
|
||||
typedef uint32_t mi_cmap_t;
|
||||
// A bitmap chunk contains 512 bits on 64-bit (256 on 32-bit)
|
||||
typedef mi_decl_align(MI_BCHUNK_SIZE) struct mi_bchunk_s {
|
||||
_Atomic(mi_bfield_t) bfields[MI_BCHUNK_FIELDS];
|
||||
} mi_bchunk_t;
|
||||
|
||||
|
||||
#define MI_CHUNKMAP_BITS (32) // 1 chunkmap tracks 32 chunks
|
||||
// The chunkmap has one bit per corresponding chunk that is set if the chunk potentially has bits set.
|
||||
// The chunkmap is itself a chunk.
|
||||
typedef mi_bchunk_t mi_bchunkmap_t;
|
||||
|
||||
#define MI_BITMAP_MAX_CHUNKMAPS (16)
|
||||
#define MI_BITMAP_MAX_CHUNK_COUNT (MI_BITMAP_MAX_CHUNKMAPS * MI_CHUNKMAP_BITS)
|
||||
#define MI_BITMAP_MIN_CHUNK_COUNT (1 * MI_CHUNKMAP_BITS) // 1 GiB arena
|
||||
#define MI_BCHUNKMAP_BITS MI_BCHUNK_BITS
|
||||
|
||||
#define MI_BITMAP_MAX_BIT_COUNT (MI_BITMAP_MAX_CHUNK_COUNT * MI_BITMAP_CHUNK_BITS) // 16 GiB arena
|
||||
#define MI_BITMAP_MIN_BIT_COUNT (MI_BITMAP_MIN_CHUNK_COUNT * MI_BITMAP_CHUNK_BITS) // 1 GiB arena
|
||||
#define MI_BITMAP_MAX_CHUNK_COUNT (MI_BCHUNKMAP_BITS)
|
||||
#define MI_BITMAP_MIN_CHUNK_COUNT (1)
|
||||
#define MI_BITMAP_MAX_BIT_COUNT (MI_BITMAP_MAX_CHUNK_COUNT * MI_BCHUNK_BITS) // 16 GiB arena
|
||||
#define MI_BITMAP_MIN_BIT_COUNT (MI_BITMAP_MIN_CHUNK_COUNT * MI_BCHUNK_BITS) // 32 MiB arena
|
||||
|
||||
|
||||
// An atomic bitmap
|
||||
typedef mi_decl_align(MI_BITMAP_CHUNK_SIZE) struct mi_bitmap_s {
|
||||
_Atomic(size_t) chunk_map_count; // valid chunk_maps entries
|
||||
_Atomic(size_t) chunk_count; // total count of chunks
|
||||
size_t padding[MI_BITMAP_CHUNK_SIZE/MI_SIZE_SIZE - 2]; // suppress warning on msvc
|
||||
_Atomic(mi_chunkmap_t) chunk_maps[MI_BITMAP_MAX_CHUNKMAPS];
|
||||
|
||||
mi_bitmap_chunk_t chunks[MI_BITMAP_MIN_BIT_COUNT]; // or more, up to MI_BITMAP_MAX_CHUNK_COUNT
|
||||
typedef mi_decl_align(MI_BCHUNK_SIZE) struct mi_bitmap_s {
|
||||
_Atomic(size_t) chunk_count; // total count of chunks (0 < N <= MI_BCHUNKMAP_BITS)
|
||||
size_t _padding[MI_BCHUNK_SIZE/MI_SIZE_SIZE - 1]; // suppress warning on msvc
|
||||
mi_bchunkmap_t chunkmap;
|
||||
mi_bchunk_t chunks[1]; // or more, up to MI_BITMAP_MAX_CHUNK_COUNT
|
||||
} mi_bitmap_t;
|
||||
|
||||
|
||||
static inline size_t mi_bitmap_chunk_map_count(const mi_bitmap_t* bitmap) {
|
||||
return mi_atomic_load_relaxed(&bitmap->chunk_map_count);
|
||||
}
|
||||
|
||||
static inline size_t mi_bitmap_chunk_count(const mi_bitmap_t* bitmap) {
|
||||
return mi_atomic_load_relaxed(&bitmap->chunk_count);
|
||||
}
|
||||
|
||||
static inline size_t mi_bitmap_max_bits(const mi_bitmap_t* bitmap) {
|
||||
return (mi_bitmap_chunk_count(bitmap) * MI_BITMAP_CHUNK_BITS);
|
||||
return (mi_bitmap_chunk_count(bitmap) * MI_BCHUNK_BITS);
|
||||
}
|
||||
|
||||
|
||||
|
@ -134,9 +122,22 @@ size_t mi_bitmap_init(mi_bitmap_t* bitmap, size_t bit_count, bool already_zero);
|
|||
// Set/clear a sequence of `n` bits in the bitmap (and can cross chunks). Not atomic so only use if local to a thread.
|
||||
void mi_bitmap_unsafe_setN(mi_bitmap_t* bitmap, size_t idx, size_t n);
|
||||
|
||||
|
||||
// Set/clear a bit in the bitmap; returns `true` if atomically transitioned from 0 to 1 (or 1 to 0)
|
||||
bool mi_bitmap_xset(mi_xset_t set, mi_bitmap_t* bitmap, size_t idx);
|
||||
|
||||
static inline bool mi_bitmap_set(mi_bitmap_t* bitmap, size_t idx) {
|
||||
return mi_bitmap_xset(MI_BIT_SET, bitmap, idx);
|
||||
}
|
||||
|
||||
static inline bool mi_bitmap_clear(mi_bitmap_t* bitmap, size_t idx) {
|
||||
return mi_bitmap_xset(MI_BIT_CLEAR, bitmap, idx);
|
||||
}
|
||||
|
||||
|
||||
// Set/clear a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from all 0's to 1's (or all 1's to 0's).
|
||||
// `n` cannot cross chunk boundaries (and `n <= MI_BITMAP_CHUNK_BITS`)!
|
||||
// If `already_xset` is not NULL, it is set to true if all the bits were already all set/cleared.
|
||||
// `n` cannot cross chunk boundaries (and `n <= MI_BCHUNK_BITS`)!
|
||||
// If `already_xset` is not NULL, it is to all the bits were already all set/cleared.
|
||||
bool mi_bitmap_xsetN(mi_xset_t set, mi_bitmap_t* bitmap, size_t idx, size_t n, size_t* already_xset);
|
||||
|
||||
static inline bool mi_bitmap_setN(mi_bitmap_t* bitmap, size_t idx, size_t n, size_t* already_set) {
|
||||
|
@ -162,7 +163,7 @@ static inline bool mi_bitmap_is_clearN(mi_bitmap_t* bitmap, size_t idx, size_t n
|
|||
|
||||
// Try to set/clear a sequence of `n` bits in the bitmap; returns `true` if atomically transitioned from 0's to 1's (or 1's to 0's)
|
||||
// and false otherwise leaving the bitmask as is.
|
||||
// `n` cannot cross chunk boundaries (and `n <= MI_BITMAP_CHUNK_BITS`)!
|
||||
// `n` cannot cross chunk boundaries (and `n <= MI_BCHUNK_BITS`)!
|
||||
mi_decl_nodiscard bool mi_bitmap_try_xsetN(mi_xset_t set, mi_bitmap_t* bitmap, size_t idx, size_t n);
|
||||
|
||||
static inline bool mi_bitmap_try_setN(mi_bitmap_t* bitmap, size_t idx, size_t n) {
|
||||
|
@ -177,48 +178,11 @@ static inline bool mi_bitmap_try_clearN(mi_bitmap_t* bitmap, size_t idx, size_t
|
|||
// Returns true on success, and in that case sets the index: `0 <= *pidx <= MI_BITMAP_MAX_BITS-n`.
|
||||
mi_decl_nodiscard bool mi_bitmap_try_find_and_clearN(mi_bitmap_t* bitmap, size_t n, size_t tseq, size_t* pidx);
|
||||
|
||||
typedef bool (mi_claim_fun_t)(size_t slice_index, void* arg1, void* arg2, bool* keep_set);
|
||||
|
||||
mi_decl_nodiscard bool mi_bitmap_try_find_and_claim(mi_bitmap_t* bitmap, size_t tseq, size_t* pidx,
|
||||
mi_claim_fun_t* claim, void* arg1, void* arg2);
|
||||
|
||||
/* --------------------------------------------------------------------------------
|
||||
Atomic bitmap for a pair of bits.
|
||||
|
||||
The valid pairs are CLEAR (0), SET (3), or BUSY (2).
|
||||
|
||||
These bit pairs are used in the abandoned pages maps: when set, the entry has
|
||||
an available page. When we scan for an available abandoned page and find an entry SET,
|
||||
we first set it to BUSY, and try to claim the page atomically (since it can race
|
||||
with a concurrent `mi_free` which also tries to claim the page). However, unlike `mi_free`,
|
||||
we cannot be sure that a concurrent `mi_free` also didn't free (and decommit) the page
|
||||
just when we got the entry. Therefore, a page can only be freed after `mi_arena_unabandon`
|
||||
which (busy) waits until the BUSY flag is cleared to ensure all readers are done.
|
||||
(and pair-bit operations must therefore be release_acquire).
|
||||
-------------------------------------------------------------------------------- */
|
||||
|
||||
#define MI_PAIR_CLEAR (0)
|
||||
#define MI_PAIR_UNUSED (1) // should never occur
|
||||
#define MI_PAIR_BUSY (2)
|
||||
#define MI_PAIR_SET (3)
|
||||
|
||||
// 0b....0101010101010101
|
||||
#define MI_BFIELD_LO_BIT2 ((MI_BFIELD_LO_BIT8 << 6)|(MI_BFIELD_LO_BIT8 << 4)|(MI_BFIELD_LO_BIT8 << 2)|MI_BFIELD_LO_BIT8)
|
||||
|
||||
// A pairmap manipulates pairs of bits (and consists of 2 bitmaps)
|
||||
typedef struct mi_pairmap_s {
|
||||
mi_bitmap_t* bitmap1;
|
||||
mi_bitmap_t* bitmap2;
|
||||
} mi_pairmap_t;
|
||||
|
||||
// initialize a pairmap to all clear; avoid a mem_zero if `already_zero` is true
|
||||
void mi_pairmap_init(mi_pairmap_t* pairmap, mi_bitmap_t* bm1, mi_bitmap_t* bm2);
|
||||
bool mi_pairmap_set(mi_pairmap_t* pairmap, size_t pair_idx);
|
||||
bool mi_pairmap_clear(mi_pairmap_t* pairmap, size_t pair_idx);
|
||||
bool mi_pairmap_is_clear(mi_pairmap_t* pairmap, size_t pair_idx);
|
||||
void mi_pairmap_clear_once_not_busy(mi_pairmap_t* pairmap, size_t pair_idx);
|
||||
|
||||
typedef bool (mi_bitmap_claim_while_busy_fun_t)(size_t pair_index, void* arg1, void* arg2);
|
||||
mi_decl_nodiscard bool mi_pairmap_try_find_and_set_busy(mi_pairmap_t* pairmap, size_t tseq, size_t* pidx,
|
||||
mi_bitmap_claim_while_busy_fun_t* claim, void* arg1 ,void* arg2
|
||||
);
|
||||
|
||||
void mi_bitmap_clear_once_set(mi_bitmap_t* bitmap, size_t idx);
|
||||
|
||||
#endif // MI_BITMAP_H
|
||||
|
|
Loading…
Add table
Reference in a new issue