@shd101wyy/yo 0.1.24 → 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.
Files changed (76) hide show
  1. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +30 -0
  2. package/.github/skills/yo-syntax/syntax-cheatsheet.md +436 -2
  3. package/out/cjs/index.cjs +551 -553
  4. package/out/cjs/yo-cli.cjs +638 -632
  5. package/out/cjs/yo-lsp.cjs +595 -597
  6. package/out/esm/index.mjs +487 -489
  7. package/out/types/src/codegen/utils/index.d.ts +1 -0
  8. package/out/types/src/expr.d.ts +1 -0
  9. package/out/types/src/test-runner.d.ts +1 -0
  10. package/out/types/tsconfig.tsbuildinfo +1 -1
  11. package/package.json +1 -1
  12. package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
  13. package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
  14. package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
  15. package/vendor/mimalloc/CMakeLists.txt +52 -33
  16. package/vendor/mimalloc/azure-pipelines.yml +4 -3
  17. package/vendor/mimalloc/bin/bundle.bat +74 -0
  18. package/vendor/mimalloc/bin/bundle.sh +232 -0
  19. package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
  20. package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
  21. package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
  22. package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
  23. package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
  24. package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
  25. package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
  26. package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
  27. package/vendor/mimalloc/doc/release-notes.md +15 -0
  28. package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
  29. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
  30. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
  31. package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
  32. package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
  33. package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
  34. package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
  35. package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
  36. package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
  37. package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
  38. package/vendor/mimalloc/include/mimalloc/track.h +7 -2
  39. package/vendor/mimalloc/include/mimalloc/types.h +57 -29
  40. package/vendor/mimalloc/include/mimalloc-override.h +10 -10
  41. package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
  42. package/vendor/mimalloc/include/mimalloc.h +22 -12
  43. package/vendor/mimalloc/readme.md +42 -17
  44. package/vendor/mimalloc/src/alloc-aligned.c +13 -11
  45. package/vendor/mimalloc/src/alloc-override.c +97 -17
  46. package/vendor/mimalloc/src/alloc-posix.c +44 -27
  47. package/vendor/mimalloc/src/alloc.c +73 -23
  48. package/vendor/mimalloc/src/arena-meta.c +3 -3
  49. package/vendor/mimalloc/src/arena.c +380 -192
  50. package/vendor/mimalloc/src/bitmap.c +68 -18
  51. package/vendor/mimalloc/src/bitmap.h +8 -4
  52. package/vendor/mimalloc/src/free.c +83 -47
  53. package/vendor/mimalloc/src/heap.c +94 -40
  54. package/vendor/mimalloc/src/init.c +273 -102
  55. package/vendor/mimalloc/src/libc.c +53 -8
  56. package/vendor/mimalloc/src/options.c +43 -40
  57. package/vendor/mimalloc/src/os.c +110 -45
  58. package/vendor/mimalloc/src/page-map.c +14 -8
  59. package/vendor/mimalloc/src/page-queue.c +9 -6
  60. package/vendor/mimalloc/src/page.c +26 -16
  61. package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
  62. package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
  63. package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
  64. package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
  65. package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
  66. package/vendor/mimalloc/src/random.c +8 -3
  67. package/vendor/mimalloc/src/stats.c +59 -48
  68. package/vendor/mimalloc/src/theap.c +85 -44
  69. package/vendor/mimalloc/src/threadlocal.c +102 -41
  70. package/vendor/mimalloc/test/main-override-static.c +31 -2
  71. package/vendor/mimalloc/test/main-override.c +27 -14
  72. package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
  73. package/vendor/mimalloc/test/main-static-dep.h +11 -0
  74. package/vendor/mimalloc/test/test-api-fill.c +2 -2
  75. package/vendor/mimalloc/test/test-stress.c +3 -3
  76. 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
- mi_atomic_yield();
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 = mi_atomic_load_relaxed(b);
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 = mi_atomic_load_relaxed(b);
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 = mi_atomic_load_relaxed(b);
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 = mi_atomic_load_relaxed(b);
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 = mi_atomic_load_relaxed(b);
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)) return false;
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); // atomic clear
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
- if (skipped != 0) {
1493
- mi_atomic_or_relaxed(&chunk->bfields[j], skipped);
1494
- return false;
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 n, size_t tseq, size_t* pidx); // < MI_BFIELD_BITS
323
- bool mi_bbitmap_try_find_and_clearNC(mi_bbitmap_t* bbitmap, size_t n, size_t tseq, size_t* pidx); // > MI_BFIELD_BITS <= MI_BCHUNK_BITS
324
- bool mi_bbitmap_try_find_and_clearN_(mi_bbitmap_t* bbitmap, size_t n, size_t tseq, size_t* pidx); // > MI_BCHUNK_BITS
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 n, size_t tseq, size_t* pidx) {
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
- // checks
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 && !MI_GUARDED
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
- size_t dbgsize = mi_usable_size(block);
65
- if (dbgsize > MI_MiB) { dbgsize = MI_MiB; }
66
- _mi_memset_aligned(block, MI_DEBUG_FREED, dbgsize);
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 void mi_block_check_unguard(mi_page_t* page, mi_block_t* block, void* p) {
103
- if (mi_block_ptr_is_guarded(block, p)) { mi_block_unguard(page, 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 void mi_block_check_unguard(mi_page_t* page, mi_block_t* block, void* p) {
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
- mi_free_ex(p, NULL);
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
- mi_free_ex(p, usable);
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 reabandond the page if other blocks are concurrently
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 <= MI_SMALL_SIZE_MAX);
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->heap->exclusive_arena)) { // and it fits our memory
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 <= MI_SMALL_SIZE_MAX && reclaim_on_free >= 0) { // early test for better codegen
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 size_t size = mi_page_usable_size_of(page, block);
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
- size_t bsize;
494
- size_t delta;
495
- bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
496
- mi_assert_internal(ok); mi_assert_internal(delta <= bsize);
497
- return (ok ? bsize - delta : 0);
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