@shd101wyy/yo 0.1.23 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/yo-async-effects/async-effects-recipes.md +74 -1
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +115 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +626 -6
- package/out/cjs/index.cjs +563 -564
- package/out/cjs/yo-cli.cjs +722 -715
- package/out/cjs/yo-lsp.cjs +601 -602
- package/out/esm/index.mjs +506 -507
- package/out/types/src/codegen/utils/index.d.ts +1 -0
- package/out/types/src/expr.d.ts +1 -0
- package/out/types/src/parser.d.ts +1 -0
- package/out/types/src/test-runner.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/build.yo +2 -2
- package/std/collections/array_list.yo +26 -0
- package/std/imm/map.yo +15 -15
- package/std/imm/sorted_map.yo +14 -14
- package/std/imm/string.yo +4 -4
- package/std/prelude.yo +18 -23
- package/std/process/command.yo +8 -8
- package/std/string/string.yo +76 -55
- package/std/string/unicode.yo +6 -6
- package/std/sys/signal.yo +6 -6
- package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
- package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
- package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
- package/vendor/mimalloc/CMakeLists.txt +52 -33
- package/vendor/mimalloc/azure-pipelines.yml +4 -3
- package/vendor/mimalloc/bin/bundle.bat +74 -0
- package/vendor/mimalloc/bin/bundle.sh +232 -0
- package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
- package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
- package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
- package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
- package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
- package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
- package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
- package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
- package/vendor/mimalloc/doc/release-notes.md +15 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
- package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
- package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
- package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
- package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
- package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
- package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
- package/vendor/mimalloc/include/mimalloc/track.h +7 -2
- package/vendor/mimalloc/include/mimalloc/types.h +57 -29
- package/vendor/mimalloc/include/mimalloc-override.h +10 -10
- package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
- package/vendor/mimalloc/include/mimalloc.h +22 -12
- package/vendor/mimalloc/readme.md +42 -17
- package/vendor/mimalloc/src/alloc-aligned.c +13 -11
- package/vendor/mimalloc/src/alloc-override.c +97 -17
- package/vendor/mimalloc/src/alloc-posix.c +44 -27
- package/vendor/mimalloc/src/alloc.c +73 -23
- package/vendor/mimalloc/src/arena-meta.c +3 -3
- package/vendor/mimalloc/src/arena.c +380 -192
- package/vendor/mimalloc/src/bitmap.c +68 -18
- package/vendor/mimalloc/src/bitmap.h +8 -4
- package/vendor/mimalloc/src/free.c +83 -47
- package/vendor/mimalloc/src/heap.c +94 -40
- package/vendor/mimalloc/src/init.c +273 -102
- package/vendor/mimalloc/src/libc.c +53 -8
- package/vendor/mimalloc/src/options.c +43 -40
- package/vendor/mimalloc/src/os.c +110 -45
- package/vendor/mimalloc/src/page-map.c +14 -8
- package/vendor/mimalloc/src/page-queue.c +9 -6
- package/vendor/mimalloc/src/page.c +26 -16
- package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
- package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
- package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
- package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
- package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
- package/vendor/mimalloc/src/random.c +8 -3
- package/vendor/mimalloc/src/stats.c +59 -48
- package/vendor/mimalloc/src/theap.c +85 -44
- package/vendor/mimalloc/src/threadlocal.c +102 -41
- package/vendor/mimalloc/test/main-override-static.c +31 -2
- package/vendor/mimalloc/test/main-override.c +27 -14
- package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
- package/vendor/mimalloc/test/main-static-dep.h +11 -0
- package/vendor/mimalloc/test/test-api-fill.c +2 -2
- package/vendor/mimalloc/test/test-stress.c +3 -3
- package/vendor/mimalloc/test/test-wrong.c +11 -7
|
@@ -12,6 +12,7 @@ Concurrent bitmap that can set/reset sequences of bits atomically
|
|
|
12
12
|
#include "mimalloc.h"
|
|
13
13
|
#include "mimalloc/internal.h"
|
|
14
14
|
#include "mimalloc/bits.h"
|
|
15
|
+
#include "mimalloc/prim.h" // _mi_prim_thread_yield
|
|
15
16
|
#include "bitmap.h"
|
|
16
17
|
|
|
17
18
|
#ifndef MI_OPT_SIMD
|
|
@@ -119,7 +120,7 @@ static inline void mi_bfield_atomic_clear_once_set(_Atomic(mi_bfield_t)*b, size_
|
|
|
119
120
|
mi_subproc_stat_counter_increase(_mi_subproc(), pages_unabandon_busy_wait, 1);
|
|
120
121
|
}
|
|
121
122
|
while ((old&mask)==0) { // busy wait
|
|
122
|
-
|
|
123
|
+
_mi_prim_thread_yield();
|
|
123
124
|
old = mi_atomic_load_acquire(b);
|
|
124
125
|
}
|
|
125
126
|
}
|
|
@@ -220,13 +221,13 @@ static inline bool mi_bfield_atomic_try_clearX(_Atomic(mi_bfield_t)*b, bool* all
|
|
|
220
221
|
|
|
221
222
|
// Check if a bit is set
|
|
222
223
|
static inline bool mi_bfield_atomic_is_set(const _Atomic(mi_bfield_t)*b, const size_t idx) {
|
|
223
|
-
const mi_bfield_t x =
|
|
224
|
+
const mi_bfield_t x = mi_atomic_load_acquire(b);
|
|
224
225
|
return ((x & mi_bfield_mask(1,idx)) != 0);
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
// Check if a bit is clear
|
|
228
229
|
static inline bool mi_bfield_atomic_is_clear(const _Atomic(mi_bfield_t)*b, const size_t idx) {
|
|
229
|
-
const mi_bfield_t x =
|
|
230
|
+
const mi_bfield_t x = mi_atomic_load_acquire(b);
|
|
230
231
|
return ((x & mi_bfield_mask(1, idx)) == 0);
|
|
231
232
|
}
|
|
232
233
|
|
|
@@ -239,14 +240,14 @@ static inline bool mi_bfield_atomic_is_xset(mi_xset_t set, const _Atomic(mi_bfie
|
|
|
239
240
|
// Check if all bits corresponding to a mask are set.
|
|
240
241
|
static inline bool mi_bfield_atomic_is_set_mask(const _Atomic(mi_bfield_t)* b, mi_bfield_t mask) {
|
|
241
242
|
mi_assert_internal(mask != 0);
|
|
242
|
-
const mi_bfield_t x =
|
|
243
|
+
const mi_bfield_t x = mi_atomic_load_acquire(b);
|
|
243
244
|
return ((x & mask) == mask);
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
// Check if all bits corresponding to a mask are clear.
|
|
247
248
|
static inline bool mi_bfield_atomic_is_clear_mask(const _Atomic(mi_bfield_t)* b, mi_bfield_t mask) {
|
|
248
249
|
mi_assert_internal(mask != 0);
|
|
249
|
-
const mi_bfield_t x =
|
|
250
|
+
const mi_bfield_t x = mi_atomic_load_acquire(b);
|
|
250
251
|
return ((x & mask) == 0);
|
|
251
252
|
}
|
|
252
253
|
|
|
@@ -259,7 +260,7 @@ static inline bool mi_bfield_atomic_is_xset_mask(mi_xset_t set, const _Atomic(mi
|
|
|
259
260
|
|
|
260
261
|
// Count bits in a mask
|
|
261
262
|
static inline size_t mi_bfield_atomic_popcount_mask(_Atomic(mi_bfield_t)*b, mi_bfield_t mask) {
|
|
262
|
-
const mi_bfield_t x =
|
|
263
|
+
const mi_bfield_t x = mi_atomic_load_acquire(b);
|
|
263
264
|
return mi_bfield_popcount(x & mask);
|
|
264
265
|
}
|
|
265
266
|
|
|
@@ -962,6 +963,19 @@ static bool mi_bchunk_bsr(mi_bchunk_t* chunk, size_t* pidx) {
|
|
|
962
963
|
return false;
|
|
963
964
|
}
|
|
964
965
|
|
|
966
|
+
static bool mi_bchunk_bsr_inv(mi_bchunk_t* chunk, size_t* pidx) {
|
|
967
|
+
for (size_t i = MI_BCHUNK_FIELDS; i > 0; ) {
|
|
968
|
+
i--;
|
|
969
|
+
mi_bfield_t b = mi_atomic_load_relaxed(&chunk->bfields[i]);
|
|
970
|
+
size_t idx;
|
|
971
|
+
if (mi_bsr(~b, &idx)) {
|
|
972
|
+
*pidx = (i*MI_BFIELD_BITS) + idx;
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
|
|
965
979
|
static size_t mi_bchunk_popcount(mi_bchunk_t* chunk) {
|
|
966
980
|
size_t popcount = 0;
|
|
967
981
|
for (size_t i = 0; i < MI_BCHUNK_FIELDS; i++) {
|
|
@@ -1059,7 +1073,6 @@ static void mi_bchunks_unsafe_setN(mi_bchunk_t* chunks, mi_bchunkmap_t* cmap, si
|
|
|
1059
1073
|
// last chunk
|
|
1060
1074
|
if (n > 0) {
|
|
1061
1075
|
mi_assert_internal(n < MI_BCHUNK_BITS);
|
|
1062
|
-
mi_assert_internal(chunk_idx < MI_BCHUNK_FIELDS);
|
|
1063
1076
|
mi_bchunk_setN(&chunks[chunk_idx], 0, n, NULL);
|
|
1064
1077
|
}
|
|
1065
1078
|
}
|
|
@@ -1428,22 +1441,28 @@ bool _mi_bitmap_forall_setc_ranges(mi_bitmap_t* bitmap, mi_forall_set_fun_t* vis
|
|
|
1428
1441
|
mi_bchunk_t* const chunk = &bitmap->chunks[chunk_idx];
|
|
1429
1442
|
for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) {
|
|
1430
1443
|
const size_t base_idx = (chunk_idx*MI_BCHUNK_BITS) + (j*MI_BFIELD_BITS);
|
|
1431
|
-
mi_bfield_t b = mi_atomic_exchange_relaxed(&chunk->bfields[j], 0);
|
|
1432
|
-
#if MI_DEBUG > 1
|
|
1444
|
+
mi_bfield_t b = mi_atomic_exchange_relaxed(&chunk->bfields[j], (mi_bfield_t)0);
|
|
1445
|
+
#if MI_DEBUG > 1
|
|
1433
1446
|
const size_t bpopcount = mi_popcount(b);
|
|
1434
1447
|
size_t rngcount = 0;
|
|
1435
|
-
#endif
|
|
1448
|
+
#endif
|
|
1436
1449
|
size_t bidx;
|
|
1437
1450
|
while (mi_bfield_find_least_bit(b, &bidx)) {
|
|
1438
1451
|
size_t rng = mi_ctz(~(b>>bidx)); // all the set bits from bidx
|
|
1439
|
-
#if MI_DEBUG > 1
|
|
1452
|
+
#if MI_DEBUG > 1
|
|
1440
1453
|
rngcount += rng;
|
|
1441
|
-
#endif
|
|
1454
|
+
#endif
|
|
1442
1455
|
const size_t idx = base_idx + bidx;
|
|
1443
1456
|
mi_assert_internal(rng>=1 && rng<=MI_BFIELD_BITS);
|
|
1444
1457
|
mi_assert_internal((idx % MI_BFIELD_BITS) + rng <= MI_BFIELD_BITS);
|
|
1445
1458
|
mi_assert_internal((idx / MI_BCHUNK_BITS) < mi_bitmap_chunk_count(bitmap));
|
|
1446
|
-
if (!visit(idx, rng, arena, arg))
|
|
1459
|
+
if (!visit(idx, rng, arena, arg)) {
|
|
1460
|
+
// break early: reset the non-visited bits
|
|
1461
|
+
if (b!=0) {
|
|
1462
|
+
mi_atomic_or_relaxed(&chunk->bfields[j], b);
|
|
1463
|
+
}
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1447
1466
|
// clear rng bits in b
|
|
1448
1467
|
b = b & ~mi_bfield_mask(rng, bidx);
|
|
1449
1468
|
}
|
|
@@ -1481,18 +1500,23 @@ bool _mi_bitmap_forall_setc_rangesn(mi_bitmap_t* bitmap, size_t rngslices, mi_fo
|
|
|
1481
1500
|
mi_bchunk_t* const chunk = &bitmap->chunks[chunk_idx];
|
|
1482
1501
|
for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) {
|
|
1483
1502
|
const size_t base_idx = (chunk_idx*MI_BCHUNK_BITS) + (j*MI_BFIELD_BITS);
|
|
1484
|
-
mi_bfield_t b = mi_atomic_exchange_relaxed(&chunk->bfields[j], 0);
|
|
1503
|
+
mi_bfield_t b = mi_atomic_exchange_relaxed(&chunk->bfields[j], (mi_bfield_t)0); // atomic clear
|
|
1485
1504
|
mi_bfield_t skipped = 0; // but track which bits we skip so we can restore them
|
|
1486
1505
|
for(size_t shift = 0; rngslices + shift <= MI_BFIELD_BITS; shift += rngslices) { // per `rngslices` to keep alignment
|
|
1487
1506
|
const mi_bfield_t rngmask = mi_bfield_mask(rngslices, shift);
|
|
1488
1507
|
if ((b & rngmask) == rngmask) {
|
|
1489
1508
|
const size_t idx = base_idx + shift;
|
|
1490
1509
|
if (!visit(idx, rngslices, arena, arg)) {
|
|
1491
|
-
// break early
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1510
|
+
// break early: restore non-visited entries
|
|
1511
|
+
mi_bfield_t notyet_visited = 0;
|
|
1512
|
+
if (shift + rngslices < MI_BFIELD_BITS) {
|
|
1513
|
+
notyet_visited = (b & (~(mi_bfield_t)0 << (shift + rngslices)));
|
|
1495
1514
|
}
|
|
1515
|
+
mi_assert_internal((notyet_visited & skipped) == 0);
|
|
1516
|
+
if ((notyet_visited | skipped) != 0) {
|
|
1517
|
+
mi_atomic_or_relaxed(&chunk->bfields[j], notyet_visited | skipped);
|
|
1518
|
+
}
|
|
1519
|
+
return false;
|
|
1496
1520
|
}
|
|
1497
1521
|
}
|
|
1498
1522
|
else {
|
|
@@ -1547,6 +1571,32 @@ void mi_bbitmap_unsafe_setN(mi_bbitmap_t* bbitmap, size_t idx, size_t n) {
|
|
|
1547
1571
|
mi_bchunks_unsafe_setN(&bbitmap->chunks[0], &bbitmap->chunkmap, idx, n);
|
|
1548
1572
|
}
|
|
1549
1573
|
|
|
1574
|
+
bool mi_bbitmap_bsr_inv(mi_bbitmap_t* bbitmap, size_t* idx) {
|
|
1575
|
+
const size_t chunk_count = mi_bbitmap_chunk_count(bbitmap);
|
|
1576
|
+
const size_t chunkmap_max = _mi_divide_up(chunk_count, MI_BFIELD_BITS);
|
|
1577
|
+
size_t skip_at_top = chunk_count % MI_BFIELD_BITS;
|
|
1578
|
+
for (size_t i = chunkmap_max; i > 0; ) {
|
|
1579
|
+
i--;
|
|
1580
|
+
mi_bfield_t cmap = mi_atomic_load_relaxed(&bbitmap->chunkmap.bfields[i]);
|
|
1581
|
+
size_t cmap_idx;
|
|
1582
|
+
// don't consider top 0 bits; set those to 1 here
|
|
1583
|
+
if (skip_at_top > 0) {
|
|
1584
|
+
const size_t mask_top = (~mi_bfield_zero()) << (MI_BFIELD_BITS - skip_at_top);
|
|
1585
|
+
skip_at_top = 0; // only for the first iteration
|
|
1586
|
+
cmap |= mask_top;
|
|
1587
|
+
}
|
|
1588
|
+
if (mi_bsr(~cmap, &cmap_idx)) {
|
|
1589
|
+
// highest chunk
|
|
1590
|
+
const size_t chunk_idx = i*MI_BFIELD_BITS + cmap_idx;
|
|
1591
|
+
size_t cidx;
|
|
1592
|
+
if (mi_bchunk_bsr_inv(&bbitmap->chunks[chunk_idx], &cidx)) {
|
|
1593
|
+
*idx = (chunk_idx * MI_BCHUNK_BITS) + cidx;
|
|
1594
|
+
return true;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return false;
|
|
1599
|
+
}
|
|
1550
1600
|
|
|
1551
1601
|
|
|
1552
1602
|
/* --------------------------------------------------------------------------------
|
|
@@ -281,6 +281,10 @@ mi_chunkbin_t mi_bbitmap_debug_get_bin(const mi_bchunk_t* chunkmap_bins, size_t
|
|
|
281
281
|
|
|
282
282
|
size_t mi_bbitmap_size(size_t bit_count, size_t* chunk_count);
|
|
283
283
|
|
|
284
|
+
// If a bit is clear in the bitmap, return `true` and set `idx` to the index of the highest bit that was clear.
|
|
285
|
+
// Otherwise return `false` (and `*idx` is undefined).
|
|
286
|
+
// Used for debug output.
|
|
287
|
+
bool mi_bbitmap_bsr_inv(mi_bbitmap_t* bbitmap, size_t* idx);
|
|
284
288
|
|
|
285
289
|
// Initialize a bitmap to all clear; avoid a mem_zero if `already_zero` is true
|
|
286
290
|
// returns the size of the bitmap.
|
|
@@ -319,13 +323,13 @@ bool mi_bbitmap_try_clearNC(mi_bbitmap_t* bbitmap, size_t idx, size_t n);
|
|
|
319
323
|
bool mi_bbitmap_try_find_and_clear(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx); // 1-bit
|
|
320
324
|
bool mi_bbitmap_try_find_and_clear8(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx); // 8-bits
|
|
321
325
|
// bool mi_bbitmap_try_find_and_clearX(mi_bbitmap_t* bbitmap, size_t tseq, size_t* pidx); // MI_BFIELD_BITS
|
|
322
|
-
bool mi_bbitmap_try_find_and_clearNX(mi_bbitmap_t* bbitmap, size_t
|
|
323
|
-
bool mi_bbitmap_try_find_and_clearNC(mi_bbitmap_t* bbitmap, size_t
|
|
324
|
-
bool mi_bbitmap_try_find_and_clearN_(mi_bbitmap_t* bbitmap, size_t
|
|
326
|
+
bool mi_bbitmap_try_find_and_clearNX(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx); // < MI_BFIELD_BITS
|
|
327
|
+
bool mi_bbitmap_try_find_and_clearNC(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx); // > MI_BFIELD_BITS <= MI_BCHUNK_BITS
|
|
328
|
+
bool mi_bbitmap_try_find_and_clearN_(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx); // > MI_BCHUNK_BITS
|
|
325
329
|
|
|
326
330
|
// Find a sequence of `n` bits in the bbitmap with all bits set, and try to atomically clear all.
|
|
327
331
|
// Returns true on success, and in that case sets the index: `0 <= *pidx <= MI_BITMAP_MAX_BITS-n`.
|
|
328
|
-
mi_decl_nodiscard static inline bool mi_bbitmap_try_find_and_clearN(mi_bbitmap_t* bbitmap, size_t
|
|
332
|
+
mi_decl_nodiscard static inline bool mi_bbitmap_try_find_and_clearN(mi_bbitmap_t* bbitmap, size_t tseq, size_t n, size_t* pidx) {
|
|
329
333
|
if (n==1) return mi_bbitmap_try_find_and_clear(bbitmap, tseq, pidx); // small pages
|
|
330
334
|
if (n==8) return mi_bbitmap_try_find_and_clear8(bbitmap, tseq, pidx); // medium pages
|
|
331
335
|
// if (n==MI_BFIELD_BITS) return mi_bbitmap_try_find_and_clearX(bbitmap, tseq, pidx); // large pages
|
|
@@ -15,7 +15,7 @@ terms of the MIT license. A copy of the license can be found in the file
|
|
|
15
15
|
// forward declarations
|
|
16
16
|
static void mi_check_padding(const mi_page_t* page, const mi_block_t* block);
|
|
17
17
|
static bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block);
|
|
18
|
-
static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block);
|
|
18
|
+
static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block, bool was_guarded);
|
|
19
19
|
static void mi_stat_free(const mi_page_t* page, const mi_block_t* block);
|
|
20
20
|
|
|
21
21
|
|
|
@@ -25,16 +25,17 @@ static void mi_stat_free(const mi_page_t* page, const mi_block_t* block);
|
|
|
25
25
|
|
|
26
26
|
// regular free of a (thread local) block pointer
|
|
27
27
|
// fast path written carefully to prevent spilling on the stack
|
|
28
|
-
static inline void mi_free_block_local(mi_page_t* page, mi_block_t* block, bool track_stats, bool check_full)
|
|
28
|
+
static inline void mi_free_block_local(mi_page_t* page, mi_block_t* block, bool was_guarded, bool track_stats, bool check_full)
|
|
29
29
|
{
|
|
30
|
-
|
|
30
|
+
MI_UNUSED(was_guarded);
|
|
31
|
+
// checks
|
|
31
32
|
if mi_unlikely(mi_check_is_double_free(page, block)) return;
|
|
32
|
-
mi_check_padding(page, block);
|
|
33
|
+
if (!was_guarded) { mi_check_padding(page, block); }
|
|
33
34
|
if (track_stats) { mi_stat_free(page, block); }
|
|
34
|
-
#if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN
|
|
35
|
+
#if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN
|
|
35
36
|
memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
|
|
36
37
|
#endif
|
|
37
|
-
if (track_stats) { mi_track_free_size(block, mi_page_usable_size_of(page, block)); } // faster then mi_usable_size as we already know the page and that p is unaligned
|
|
38
|
+
if (track_stats) { mi_track_free_size(block, mi_page_usable_size_of(page, block, was_guarded)); } // faster then mi_usable_size as we already know the page and that p is unaligned
|
|
38
39
|
|
|
39
40
|
// actual free: push on the local free list
|
|
40
41
|
mi_block_set_next(page, block, page->local_free);
|
|
@@ -53,17 +54,20 @@ static inline void mi_free_block_local(mi_page_t* page, mi_block_t* block, bool
|
|
|
53
54
|
static void mi_decl_noinline mi_free_try_collect_mt(mi_page_t* page, mi_block_t* mt_free) mi_attr_noexcept;
|
|
54
55
|
|
|
55
56
|
// Free a block multi-threaded
|
|
56
|
-
static inline void mi_free_block_mt(mi_page_t* page, mi_block_t* block) mi_attr_noexcept
|
|
57
|
+
static inline void mi_free_block_mt(mi_page_t* page, mi_block_t* block, bool was_guarded) mi_attr_noexcept
|
|
57
58
|
{
|
|
59
|
+
MI_UNUSED(was_guarded);
|
|
58
60
|
// adjust stats (after padding check and potentially recursive `mi_free` above)
|
|
59
61
|
mi_stat_free(page, block); // stat_free may access the padding
|
|
60
|
-
mi_track_free_size(block, mi_page_usable_size_of(page, block));
|
|
62
|
+
mi_track_free_size(block, mi_page_usable_size_of(page, block, was_guarded));
|
|
61
63
|
|
|
62
64
|
// _mi_padding_shrink(page, block, sizeof(mi_block_t));
|
|
63
65
|
#if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN // note: when tracking, cannot use mi_usable_size with multi-threading
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
if (!was_guarded) {
|
|
67
|
+
size_t dbgsize = mi_usable_size(block);
|
|
68
|
+
if (dbgsize > MI_MiB) { dbgsize = MI_MiB; }
|
|
69
|
+
_mi_memset_aligned(block, MI_DEBUG_FREED, dbgsize);
|
|
70
|
+
}
|
|
67
71
|
#endif
|
|
68
72
|
|
|
69
73
|
// push atomically on the page thread free list
|
|
@@ -99,12 +103,19 @@ mi_block_t* _mi_page_ptr_unalign(const mi_page_t* page, const void* p) {
|
|
|
99
103
|
// forward declaration for a MI_GUARDED build
|
|
100
104
|
#if MI_GUARDED
|
|
101
105
|
static void mi_block_unguard(mi_page_t* page, mi_block_t* block, void* p); // forward declaration
|
|
102
|
-
static inline
|
|
103
|
-
if (mi_block_ptr_is_guarded(block, p)) {
|
|
106
|
+
static inline bool mi_block_check_unguard(mi_page_t* page, mi_block_t* block, void* p) {
|
|
107
|
+
if (mi_block_ptr_is_guarded(block, p)) {
|
|
108
|
+
mi_block_unguard(page, block, p);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
#else
|
|
106
|
-
static inline
|
|
116
|
+
static inline bool mi_block_check_unguard(mi_page_t* page, mi_block_t* block, void* p) {
|
|
107
117
|
MI_UNUSED(page); MI_UNUSED(block); MI_UNUSED(p);
|
|
118
|
+
return false;
|
|
108
119
|
}
|
|
109
120
|
#endif
|
|
110
121
|
|
|
@@ -124,16 +135,16 @@ static inline mi_block_t* mi_validate_block_from_ptr( const mi_page_t* page, voi
|
|
|
124
135
|
static void mi_decl_noinline mi_free_generic_local(mi_page_t* page, void* p) mi_attr_noexcept {
|
|
125
136
|
mi_assert_internal(p!=NULL && page != NULL);
|
|
126
137
|
mi_block_t* const block = (mi_page_has_interior_pointers(page) ? _mi_page_ptr_unalign(page, p) : mi_validate_block_from_ptr(page,p));
|
|
127
|
-
mi_block_check_unguard(page, block, p);
|
|
128
|
-
mi_free_block_local(page, block, true /* track stats */, true /* check for a full page */);
|
|
138
|
+
const bool was_guarded = mi_block_check_unguard(page, block, p);
|
|
139
|
+
mi_free_block_local(page, block, was_guarded, true /* track stats */, true /* check for a full page */);
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
// free a pointer owned by another thread (page parameter comes first for better codegen)
|
|
132
143
|
static void mi_decl_noinline mi_free_generic_mt(mi_page_t* page, void* p) mi_attr_noexcept {
|
|
133
144
|
mi_assert_internal(p!=NULL && page != NULL);
|
|
134
145
|
mi_block_t* const block = (mi_page_has_interior_pointers(page) ? _mi_page_ptr_unalign(page, p) : mi_validate_block_from_ptr(page,p));
|
|
135
|
-
mi_block_check_unguard(page, block, p);
|
|
136
|
-
mi_free_block_mt(page, block);
|
|
146
|
+
const bool was_guarded = mi_block_check_unguard(page, block, p);
|
|
147
|
+
mi_free_block_mt(page, block, was_guarded);
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
// generic free (for runtime integration)
|
|
@@ -165,9 +176,8 @@ static inline mi_page_t* mi_validate_ptr_page(const void* p, const char* msg)
|
|
|
165
176
|
|
|
166
177
|
// Free a block
|
|
167
178
|
// Fast path written carefully to prevent register spilling on the stack
|
|
168
|
-
static mi_decl_forceinline void mi_free_ex(void* p, size_t* usable)
|
|
179
|
+
static mi_decl_forceinline void mi_free_ex(void* p, size_t* usable, mi_page_t* page)
|
|
169
180
|
{
|
|
170
|
-
mi_page_t* const page = mi_validate_ptr_page(p,"mi_free");
|
|
171
181
|
if mi_unlikely(page==NULL) return; // page will be NULL if p==NULL
|
|
172
182
|
mi_assert_internal(p!=NULL && page!=NULL);
|
|
173
183
|
if (usable!=NULL) { *usable = mi_page_usable_block_size(page); }
|
|
@@ -176,7 +186,7 @@ static mi_decl_forceinline void mi_free_ex(void* p, size_t* usable)
|
|
|
176
186
|
if mi_likely(xtid == 0) { // `tid == mi_page_thread_id(page) && mi_page_flags(page) == 0`
|
|
177
187
|
// thread-local, aligned, and not a full page
|
|
178
188
|
mi_block_t* const block = mi_validate_block_from_ptr(page,p);
|
|
179
|
-
mi_free_block_local(page, block, true /* track stats */, false /* no need to check if the page is full */);
|
|
189
|
+
mi_free_block_local(page, block, false /* was guarded */, true /* track stats */, false /* no need to check if the page is full */);
|
|
180
190
|
}
|
|
181
191
|
else if (xtid <= MI_PAGE_FLAG_MASK) { // `tid == mi_page_thread_id(page) && mi_page_flags(page) != 0`
|
|
182
192
|
// page is local, but is full or contains (inner) aligned blocks; use generic path
|
|
@@ -186,7 +196,7 @@ static mi_decl_forceinline void mi_free_ex(void* p, size_t* usable)
|
|
|
186
196
|
else if ((xtid & MI_PAGE_FLAG_MASK) == 0) { // `tid != mi_page_thread_id(page) && mi_page_flags(page) == 0`
|
|
187
197
|
// blocks are aligned (and not a full page); push on the thread_free list
|
|
188
198
|
mi_block_t* const block = mi_validate_block_from_ptr(page,p);
|
|
189
|
-
mi_free_block_mt(page,block);
|
|
199
|
+
mi_free_block_mt(page,block,false /* was_guarded */);
|
|
190
200
|
}
|
|
191
201
|
else {
|
|
192
202
|
// page is full or contains (inner) aligned blocks; use generic multi-thread path
|
|
@@ -195,13 +205,39 @@ static mi_decl_forceinline void mi_free_ex(void* p, size_t* usable)
|
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
void mi_free(void* p) mi_attr_noexcept {
|
|
198
|
-
|
|
208
|
+
mi_page_t* const page = mi_validate_ptr_page(p,"mi_free");
|
|
209
|
+
mi_free_ex(p, NULL, page);
|
|
199
210
|
}
|
|
200
211
|
|
|
201
212
|
void mi_ufree(void* p, size_t* usable) mi_attr_noexcept {
|
|
202
|
-
|
|
213
|
+
mi_page_t* const page = mi_validate_ptr_page(p,"mi_ufree");
|
|
214
|
+
mi_free_ex(p, usable, page);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
void mi_free_small(void* p) mi_attr_noexcept {
|
|
218
|
+
// We can only call `mi_free_small` for pointers allocated with `mi_(heap_)malloc_small`.
|
|
219
|
+
// If we keep page info in front of the page area for small objects, we can find the info
|
|
220
|
+
// just by aligning down the pointer instead of looking it up in the page map.
|
|
221
|
+
#if MI_PAGE_META_ALIGNED_FREE_SMALL
|
|
222
|
+
#if MI_GUARDED
|
|
223
|
+
#warning "MI_PAGE_META_ALIGNED_FREE_SMALL ignored as MI_GUARDED is defined"
|
|
224
|
+
mi_free(p);
|
|
225
|
+
#elif MI_ARENA_SLICE_ALIGN < MI_SMALL_PAGE_SIZE
|
|
226
|
+
#warning "MI_PAGE_META_ALIGNED_FREE_SMALL ignored as the MI_ARENA_SLICE_ALIGN is less than the small page size"
|
|
227
|
+
mi_free(p);
|
|
228
|
+
#else
|
|
229
|
+
mi_page_t* const page = (mi_page_t*)_mi_align_down_ptr(p,MI_SMALL_PAGE_SIZE);
|
|
230
|
+
mi_assert(page == mi_validate_ptr_page(p,"mi_free_small"));
|
|
231
|
+
mi_assert((void*)page == _mi_align_down_ptr(page->page_start,MI_SMALL_PAGE_SIZE));
|
|
232
|
+
mi_assert(page->block_size <= MI_SMALL_SIZE_MAX); // note: not `MI_SMALL_MAX_OBJ_SIZE` as we need to match `mi_(heap_)malloc_small`
|
|
233
|
+
mi_free_ex(p, NULL, page);
|
|
234
|
+
#endif
|
|
235
|
+
#else
|
|
236
|
+
mi_free(p);
|
|
237
|
+
#endif
|
|
203
238
|
}
|
|
204
239
|
|
|
240
|
+
|
|
205
241
|
// --------------------------------------------------------------------------------------------
|
|
206
242
|
// `mi_free_try_collect_mt`: Potentially collect a page in a free in an abandoned page.
|
|
207
243
|
// 1. if the page becomes empty, free it
|
|
@@ -231,7 +267,7 @@ static bool mi_abandoned_page_try_reabandon_to_mapped(mi_page_t* page)
|
|
|
231
267
|
return _mi_arenas_page_try_reabandon_to_mapped(page);
|
|
232
268
|
}
|
|
233
269
|
|
|
234
|
-
// Release ownership of a page. This may free or
|
|
270
|
+
// Release ownership of a page. This may free or reabandoned the page if other blocks are concurrently
|
|
235
271
|
// freed in the meantime. Returns `true` if the page was freed.
|
|
236
272
|
// By passing the captured `expected_thread_free`, we can often avoid calling `mi_page_free_collect`.
|
|
237
273
|
static void mi_abandoned_page_unown_from_free(mi_page_t* page, mi_block_t* expected_thread_free) {
|
|
@@ -277,7 +313,7 @@ static mi_decl_noinline bool mi_abandoned_page_try_reclaim(mi_page_t* page, long
|
|
|
277
313
|
mi_assert_internal(mi_page_is_owned(page));
|
|
278
314
|
mi_assert_internal(mi_page_is_abandoned(page));
|
|
279
315
|
mi_assert_internal(!mi_page_all_free(page));
|
|
280
|
-
mi_assert_internal(page->block_size <=
|
|
316
|
+
mi_assert_internal(page->block_size <= MI_MEDIUM_MAX_OBJ_SIZE);
|
|
281
317
|
mi_assert_internal(reclaim_on_free >= 0);
|
|
282
318
|
|
|
283
319
|
// dont reclaim if we just have terminated this thread and we should
|
|
@@ -298,7 +334,7 @@ static mi_decl_noinline bool mi_abandoned_page_try_reclaim(mi_page_t* page, long
|
|
|
298
334
|
else if (reclaim_on_free == 1 && // if cross-thread is allowed
|
|
299
335
|
!theap->tld->is_in_threadpool && // and we are not part of a threadpool
|
|
300
336
|
!mi_page_is_mostly_used(page) && // and the page is not too full
|
|
301
|
-
_mi_arena_memid_is_suitable(page->memid, theap->
|
|
337
|
+
_mi_arena_memid_is_suitable(page->memid, _mi_theap_heap(theap)->exclusive_arena)) { // and it fits our memory
|
|
302
338
|
// across threads
|
|
303
339
|
max_reclaim = _mi_option_get_fast(mi_option_page_cross_thread_max_reclaim);
|
|
304
340
|
}
|
|
@@ -344,7 +380,7 @@ static void mi_decl_noinline mi_free_try_collect_mt(mi_page_t* page, mi_block_t*
|
|
|
344
380
|
|
|
345
381
|
// try to: 1. free it, 2. reclaim it, or 3. reabandon it to be mapped
|
|
346
382
|
if (mi_abandoned_page_try_free(page)) return;
|
|
347
|
-
if (page->block_size <=
|
|
383
|
+
if (page->block_size <= MI_MEDIUM_MAX_OBJ_SIZE && reclaim_on_free >= 0) { // early test for better codegen
|
|
348
384
|
if (mi_abandoned_page_try_reclaim(page, reclaim_on_free)) return;
|
|
349
385
|
}
|
|
350
386
|
if (mi_abandoned_page_try_reabandon_to_mapped(page)) return;
|
|
@@ -361,15 +397,11 @@ static void mi_decl_noinline mi_free_try_collect_mt(mi_page_t* page, mi_block_t*
|
|
|
361
397
|
// Bytes available in a block
|
|
362
398
|
static size_t mi_decl_noinline mi_page_usable_aligned_size_of(const mi_page_t* page, const void* p) mi_attr_noexcept {
|
|
363
399
|
const mi_block_t* block = _mi_page_ptr_unalign(page, p);
|
|
364
|
-
const
|
|
400
|
+
const bool is_guarded = mi_block_ptr_is_guarded(block,p);
|
|
401
|
+
const size_t size = mi_page_usable_size_of(page, block, is_guarded);
|
|
365
402
|
const ptrdiff_t adjust = (uint8_t*)p - (uint8_t*)block;
|
|
366
403
|
mi_assert_internal(adjust >= 0 && (size_t)adjust <= size);
|
|
367
|
-
const size_t aligned_size = (size - adjust);
|
|
368
|
-
#if MI_GUARDED
|
|
369
|
-
if (mi_block_ptr_is_guarded(block, p)) {
|
|
370
|
-
return aligned_size - _mi_os_page_size();
|
|
371
|
-
}
|
|
372
|
-
#endif
|
|
404
|
+
const size_t aligned_size = (size - adjust);
|
|
373
405
|
return aligned_size;
|
|
374
406
|
}
|
|
375
407
|
|
|
@@ -377,7 +409,7 @@ static inline size_t _mi_usable_size(const void* p, const mi_page_t* page) mi_at
|
|
|
377
409
|
if mi_unlikely(page==NULL) return 0;
|
|
378
410
|
if mi_likely(!mi_page_has_interior_pointers(page)) {
|
|
379
411
|
const mi_block_t* block = (const mi_block_t*)p;
|
|
380
|
-
return mi_page_usable_size_of(page, block);
|
|
412
|
+
return mi_page_usable_size_of(page, block, false /* is guarded */);
|
|
381
413
|
}
|
|
382
414
|
else {
|
|
383
415
|
// split out to separate routine for improved code generation
|
|
@@ -489,12 +521,18 @@ static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* bloc
|
|
|
489
521
|
}
|
|
490
522
|
|
|
491
523
|
// Return the exact usable size of a block.
|
|
492
|
-
static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
524
|
+
static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block, bool is_guarded) {
|
|
525
|
+
if (is_guarded) {
|
|
526
|
+
const size_t bsize = mi_page_block_size(page);
|
|
527
|
+
return (bsize - _mi_os_page_size());
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
size_t bsize;
|
|
531
|
+
size_t delta;
|
|
532
|
+
bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
|
|
533
|
+
mi_assert_internal(ok); mi_assert_internal(delta <= bsize);
|
|
534
|
+
return (ok ? bsize - delta : 0);
|
|
535
|
+
}
|
|
498
536
|
}
|
|
499
537
|
|
|
500
538
|
// When a non-thread-local block is freed, it becomes part of the thread delayed free
|
|
@@ -517,15 +555,13 @@ void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const si
|
|
|
517
555
|
mi_track_mem_noaccess(padding,sizeof(mi_padding_t));
|
|
518
556
|
}
|
|
519
557
|
#else
|
|
520
|
-
static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) {
|
|
521
|
-
MI_UNUSED(block);
|
|
558
|
+
static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block, bool is_guarded) {
|
|
559
|
+
MI_UNUSED(is_guarded); MI_UNUSED(block);
|
|
522
560
|
return mi_page_usable_block_size(page);
|
|
523
561
|
}
|
|
524
562
|
|
|
525
563
|
void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) {
|
|
526
|
-
MI_UNUSED(page);
|
|
527
|
-
MI_UNUSED(block);
|
|
528
|
-
MI_UNUSED(min_size);
|
|
564
|
+
MI_UNUSED(page); MI_UNUSED(block); MI_UNUSED(min_size);
|
|
529
565
|
}
|
|
530
566
|
#endif
|
|
531
567
|
|