Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/mimalloc/src/arena-meta.c
14369 views
1
/* ----------------------------------------------------------------------------
2
Copyright (c) 2019-2024, Microsoft Research, Daan Leijen
3
This is free software; you can redistribute it and/or modify it under the
4
terms 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
/* ----------------------------------------------------------------------------
9
We have a special "mini" allocator just for allocation of meta-data like
10
the theap (`mi_theap_t`) or thread-local data (`mi_tld_t`).
11
12
We reuse the bitmap of the arena's for allocation of 64b blocks inside
13
an arena slice (64KiB).
14
We always ensure that meta data is zero'd (we zero on `free`)
15
-----------------------------------------------------------------------------*/
16
17
#include "mimalloc.h"
18
#include "mimalloc/internal.h"
19
#include "bitmap.h"
20
21
/* -----------------------------------------------------------
22
Meta data allocation
23
----------------------------------------------------------- */
24
25
#define MI_META_PAGE_SIZE MI_ARENA_SLICE_SIZE
26
#define MI_META_PAGE_ALIGN MI_ARENA_SLICE_ALIGN
27
28
// large enough such that META_MAX_SIZE > 4k (even on 32-bit)
29
#define MI_META_BLOCK_SIZE (1 << (16 - MI_BCHUNK_BITS_SHIFT)) // 128 on 64-bit
30
#define MI_META_BLOCK_ALIGN MI_META_BLOCK_SIZE
31
#define MI_META_BLOCKS_PER_PAGE (MI_META_PAGE_SIZE / MI_META_BLOCK_SIZE) // 512
32
#define MI_META_MAX_SIZE (MI_BCHUNK_SIZE * MI_META_BLOCK_SIZE)
33
34
#if MI_META_MAX_SIZE <= 4096
35
#error "max meta object size should be at least 4KiB"
36
#endif
37
38
typedef struct mi_meta_page_s {
39
_Atomic(struct mi_meta_page_s*) next; // a linked list of meta-data pages (never released)
40
mi_memid_t memid; // provenance of the meta-page memory itself
41
mi_bbitmap_t blocks_free; // a small bitmap with 1 bit per block.
42
} mi_meta_page_t;
43
44
static mi_decl_cache_align _Atomic(mi_meta_page_t*) mi_meta_pages = MI_ATOMIC_VAR_INIT(NULL);
45
46
47
#if MI_DEBUG > 1
48
static mi_meta_page_t* mi_meta_page_of_ptr(void* p, size_t* block_idx) {
49
mi_meta_page_t* mpage = (mi_meta_page_t*)((uint8_t*)_mi_align_down_ptr(p,MI_META_PAGE_ALIGN) + _mi_os_secure_guard_page_size());
50
if (block_idx != NULL) {
51
*block_idx = ((uint8_t*)p - (uint8_t*)mpage) / MI_META_BLOCK_SIZE;
52
}
53
return mpage;
54
}
55
#endif
56
57
static mi_meta_page_t* mi_meta_page_next( mi_meta_page_t* mpage ) {
58
return mi_atomic_load_ptr_acquire(mi_meta_page_t, &mpage->next);
59
}
60
61
static void* mi_meta_block_start( mi_meta_page_t* mpage, size_t block_idx ) {
62
mi_assert_internal(_mi_is_aligned((uint8_t*)mpage - _mi_os_secure_guard_page_size(), MI_META_PAGE_ALIGN));
63
mi_assert_internal(block_idx < MI_META_BLOCKS_PER_PAGE);
64
void* p = ((uint8_t*)mpage - _mi_os_secure_guard_page_size() + (block_idx * MI_META_BLOCK_SIZE));
65
mi_assert_internal(mpage == mi_meta_page_of_ptr(p,NULL));
66
return p;
67
}
68
69
// allocate a fresh meta page and add it to the global list.
70
static mi_meta_page_t* mi_meta_page_zalloc(void) {
71
// allocate a fresh arena slice
72
// note: careful with _mi_subproc as it may recurse into mi_tld and meta_page_zalloc again.. (same with _mi_os_numa_node()...)
73
mi_memid_t memid;
74
uint8_t* base = (uint8_t*)_mi_arenas_alloc_aligned(mi_heap_main(), MI_META_PAGE_SIZE, MI_META_PAGE_ALIGN, 0,
75
true /* commit*/, (MI_SECURE==0) /* allow large? */,
76
NULL /* req arena */, 0 /* thread_seq */, -1 /* numa node */, &memid);
77
if (base == NULL) return NULL;
78
mi_assert_internal(_mi_is_aligned(base,MI_META_PAGE_ALIGN));
79
if (!memid.initially_zero) {
80
_mi_memzero_aligned(base, MI_ARENA_SLICE_SIZE);
81
}
82
83
// guard pages
84
#if MI_SECURE >= 1
85
_mi_os_secure_guard_page_set_at(base, memid);
86
_mi_os_secure_guard_page_set_before(base + MI_META_PAGE_SIZE, memid);
87
#endif
88
89
// initialize the page and free block bitmap
90
mi_meta_page_t* mpage = (mi_meta_page_t*)(base + _mi_os_secure_guard_page_size());
91
mpage->memid = memid;
92
mi_bbitmap_init(&mpage->blocks_free, MI_META_BLOCKS_PER_PAGE, true /* already_zero */);
93
const size_t mpage_size = offsetof(mi_meta_page_t,blocks_free) + mi_bbitmap_size(MI_META_BLOCKS_PER_PAGE, NULL);
94
const size_t info_blocks = _mi_divide_up(mpage_size,MI_META_BLOCK_SIZE);
95
const size_t guard_blocks = _mi_divide_up(_mi_os_secure_guard_page_size(), MI_META_BLOCK_SIZE);
96
mi_assert_internal(info_blocks + 2*guard_blocks < MI_META_BLOCKS_PER_PAGE);
97
mi_bbitmap_unsafe_setN(&mpage->blocks_free, info_blocks + guard_blocks, MI_META_BLOCKS_PER_PAGE - info_blocks - 2*guard_blocks);
98
99
// push atomically in front of the meta page list
100
// (note: there is no ABA issue since we never free meta-pages)
101
mi_meta_page_t* old = mi_atomic_load_ptr_acquire(mi_meta_page_t,&mi_meta_pages);
102
do {
103
mi_atomic_store_ptr_release(mi_meta_page_t, &mpage->next, old);
104
} while(!mi_atomic_cas_ptr_weak_acq_rel(mi_meta_page_t,&mi_meta_pages,&old,mpage));
105
return mpage;
106
}
107
108
109
// allocate meta-data
110
mi_decl_noinline void* _mi_meta_zalloc( size_t size, mi_memid_t* pmemid )
111
{
112
mi_assert_internal(pmemid != NULL);
113
size = _mi_align_up(size,MI_META_BLOCK_SIZE);
114
if (size == 0 || size > MI_META_MAX_SIZE) return NULL;
115
const size_t block_count = _mi_divide_up(size,MI_META_BLOCK_SIZE);
116
mi_assert_internal(block_count > 0 && block_count < MI_BCHUNK_BITS);
117
mi_meta_page_t* mpage0 = mi_atomic_load_ptr_acquire(mi_meta_page_t,&mi_meta_pages);
118
mi_meta_page_t* mpage = mpage0;
119
while (mpage != NULL) {
120
size_t block_idx;
121
if (mi_bbitmap_try_find_and_clearN(&mpage->blocks_free, 0, block_count, &block_idx)) {
122
// found and claimed `block_count` blocks
123
*pmemid = _mi_memid_create_meta(mpage, block_idx, block_count);
124
return mi_meta_block_start(mpage,block_idx);
125
}
126
else {
127
mpage = mi_meta_page_next(mpage);
128
}
129
}
130
// failed to find space in existing pages
131
if (mi_atomic_load_ptr_acquire(mi_meta_page_t,&mi_meta_pages) != mpage0) {
132
// the page list was updated by another thread in the meantime, retry
133
return _mi_meta_zalloc(size,pmemid);
134
}
135
// otherwise, allocate a fresh metapage and try once more
136
mpage = mi_meta_page_zalloc();
137
if (mpage != NULL) {
138
size_t block_idx;
139
if (mi_bbitmap_try_find_and_clearN(&mpage->blocks_free, 0, block_count, &block_idx)) {
140
// found and claimed `block_count` blocks
141
*pmemid = _mi_memid_create_meta(mpage, block_idx, block_count);
142
return mi_meta_block_start(mpage,block_idx);
143
}
144
}
145
// if all this failed, allocate from the OS
146
return _mi_os_alloc(size, pmemid);
147
}
148
149
// free meta-data
150
mi_decl_noinline void _mi_meta_free(void* p, size_t size, mi_memid_t memid) {
151
if (p==NULL) return;
152
if (memid.memkind == MI_MEM_META) {
153
mi_assert_internal(_mi_divide_up(size, MI_META_BLOCK_SIZE) == memid.mem.meta.block_count);
154
const size_t block_count = memid.mem.meta.block_count;
155
const size_t block_idx = memid.mem.meta.block_index;
156
mi_meta_page_t* mpage = (mi_meta_page_t*)memid.mem.meta.meta_page;
157
mi_assert_internal(mi_meta_page_of_ptr(p,NULL) == mpage);
158
mi_assert_internal(block_idx + block_count <= MI_META_BLOCKS_PER_PAGE);
159
mi_assert_internal(mi_bbitmap_is_clearN(&mpage->blocks_free, block_idx, block_count));
160
// we zero on free (and on the initial page allocation) so we don't need a "dirty" map
161
_mi_memzero_aligned(mi_meta_block_start(mpage, block_idx), block_count*MI_META_BLOCK_SIZE);
162
mi_bbitmap_setN(&mpage->blocks_free, block_idx, block_count);
163
}
164
else {
165
_mi_arenas_free(p,size,memid);
166
}
167
}
168
169
// used for debug output
170
bool _mi_meta_is_meta_page(void* p)
171
{
172
mi_meta_page_t* mpage0 = mi_atomic_load_ptr_acquire(mi_meta_page_t, &mi_meta_pages);
173
mi_meta_page_t* mpage = mpage0;
174
while (mpage != NULL) {
175
if ((void*)mpage == p) return true;
176
mpage = mi_meta_page_next(mpage);
177
}
178
return false;
179
}
180
181