@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.
Files changed (88) hide show
  1. package/.github/skills/yo-async-effects/async-effects-recipes.md +74 -1
  2. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +115 -0
  3. package/.github/skills/yo-syntax/syntax-cheatsheet.md +626 -6
  4. package/out/cjs/index.cjs +563 -564
  5. package/out/cjs/yo-cli.cjs +722 -715
  6. package/out/cjs/yo-lsp.cjs +601 -602
  7. package/out/esm/index.mjs +506 -507
  8. package/out/types/src/codegen/utils/index.d.ts +1 -0
  9. package/out/types/src/expr.d.ts +1 -0
  10. package/out/types/src/parser.d.ts +1 -0
  11. package/out/types/src/test-runner.d.ts +1 -0
  12. package/out/types/tsconfig.tsbuildinfo +1 -1
  13. package/package.json +1 -1
  14. package/std/build.yo +2 -2
  15. package/std/collections/array_list.yo +26 -0
  16. package/std/imm/map.yo +15 -15
  17. package/std/imm/sorted_map.yo +14 -14
  18. package/std/imm/string.yo +4 -4
  19. package/std/prelude.yo +18 -23
  20. package/std/process/command.yo +8 -8
  21. package/std/string/string.yo +76 -55
  22. package/std/string/unicode.yo +6 -6
  23. package/std/sys/signal.yo +6 -6
  24. package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
  25. package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
  26. package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
  27. package/vendor/mimalloc/CMakeLists.txt +52 -33
  28. package/vendor/mimalloc/azure-pipelines.yml +4 -3
  29. package/vendor/mimalloc/bin/bundle.bat +74 -0
  30. package/vendor/mimalloc/bin/bundle.sh +232 -0
  31. package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
  32. package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
  33. package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
  34. package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
  35. package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
  36. package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
  37. package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
  38. package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
  39. package/vendor/mimalloc/doc/release-notes.md +15 -0
  40. package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
  41. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
  42. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
  43. package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
  44. package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
  45. package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
  46. package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
  47. package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
  48. package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
  49. package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
  50. package/vendor/mimalloc/include/mimalloc/track.h +7 -2
  51. package/vendor/mimalloc/include/mimalloc/types.h +57 -29
  52. package/vendor/mimalloc/include/mimalloc-override.h +10 -10
  53. package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
  54. package/vendor/mimalloc/include/mimalloc.h +22 -12
  55. package/vendor/mimalloc/readme.md +42 -17
  56. package/vendor/mimalloc/src/alloc-aligned.c +13 -11
  57. package/vendor/mimalloc/src/alloc-override.c +97 -17
  58. package/vendor/mimalloc/src/alloc-posix.c +44 -27
  59. package/vendor/mimalloc/src/alloc.c +73 -23
  60. package/vendor/mimalloc/src/arena-meta.c +3 -3
  61. package/vendor/mimalloc/src/arena.c +380 -192
  62. package/vendor/mimalloc/src/bitmap.c +68 -18
  63. package/vendor/mimalloc/src/bitmap.h +8 -4
  64. package/vendor/mimalloc/src/free.c +83 -47
  65. package/vendor/mimalloc/src/heap.c +94 -40
  66. package/vendor/mimalloc/src/init.c +273 -102
  67. package/vendor/mimalloc/src/libc.c +53 -8
  68. package/vendor/mimalloc/src/options.c +43 -40
  69. package/vendor/mimalloc/src/os.c +110 -45
  70. package/vendor/mimalloc/src/page-map.c +14 -8
  71. package/vendor/mimalloc/src/page-queue.c +9 -6
  72. package/vendor/mimalloc/src/page.c +26 -16
  73. package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
  74. package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
  75. package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
  76. package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
  77. package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
  78. package/vendor/mimalloc/src/random.c +8 -3
  79. package/vendor/mimalloc/src/stats.c +59 -48
  80. package/vendor/mimalloc/src/theap.c +85 -44
  81. package/vendor/mimalloc/src/threadlocal.c +102 -41
  82. package/vendor/mimalloc/test/main-override-static.c +31 -2
  83. package/vendor/mimalloc/test/main-override.c +27 -14
  84. package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
  85. package/vendor/mimalloc/test/main-static-dep.h +11 -0
  86. package/vendor/mimalloc/test/test-api-fill.c +2 -2
  87. package/vendor/mimalloc/test/test-stress.c +3 -3
  88. package/vendor/mimalloc/test/test-wrong.c +11 -7
@@ -33,21 +33,31 @@ mi_arena_id_t _mi_arena_id_none(void) {
33
33
  }
34
34
 
35
35
  mi_arena_t* _mi_arena_from_id(mi_arena_id_t id) {
36
- return (mi_arena_t*)id;
36
+ mi_arena_t* const arena = (mi_arena_t*)id;
37
+ mi_assert_internal(arena==NULL || arena->parent==NULL); // id's should never point to sub-arena's
38
+ return arena;
39
+ }
40
+
41
+ mi_arena_id_t mi_arena_id_from_arena(mi_arena_t* arena) {
42
+ mi_assert_internal(arena==NULL || arena->parent==NULL);
43
+ return (arena==NULL ? _mi_arena_id_none() : (mi_arena_id_t)arena);
37
44
  }
38
45
 
39
46
 
40
- static bool mi_arena_id_is_suitable(mi_arena_t* arena, mi_arena_t* req_arena) {
41
- return ((arena == req_arena) || // they match,
42
- (req_arena == NULL && !arena->is_exclusive)); // or the arena is not exclusive, and we didn't request a specific one
47
+ static bool mi_arena_is_suitable(mi_arena_t* arena, mi_arena_t* req_arena) {
48
+ if (arena == req_arena) return true; // they match
49
+ if (arena == NULL) return false;
50
+ if (req_arena == NULL && !arena->is_exclusive) return true; // or the arena is not exclusive, and we didn't request a specific one
51
+ if (arena->parent != NULL && arena->parent == req_arena) return true; // sub-arena? (note that req_arena is never a sub arena)
52
+ return false;
43
53
  }
44
54
 
45
55
  bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_t* request_arena) {
46
56
  if (memid.memkind == MI_MEM_ARENA) {
47
- return mi_arena_id_is_suitable(memid.mem.arena.arena, request_arena);
57
+ return mi_arena_is_suitable(memid.mem.arena.arena, request_arena);
48
58
  }
49
59
  else {
50
- return mi_arena_id_is_suitable(NULL, request_arena);
60
+ return mi_arena_is_suitable(NULL, request_arena);
51
61
  }
52
62
  }
53
63
 
@@ -57,7 +67,7 @@ size_t mi_arenas_get_count(mi_subproc_t* subproc) {
57
67
 
58
68
  mi_arena_t* mi_arena_from_index(mi_subproc_t* subproc, size_t idx) {
59
69
  mi_assert_internal(idx < mi_arenas_get_count(subproc));
60
- return mi_atomic_load_ptr_relaxed(mi_arena_t, &subproc->arenas[idx]);
70
+ return mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[idx]);
61
71
  }
62
72
 
63
73
  static size_t mi_arena_info_slices(mi_arena_t* arena) {
@@ -79,6 +89,10 @@ size_t mi_arena_min_alignment(void) {
79
89
  return MI_ARENA_SLICE_ALIGN;
80
90
  }
81
91
 
92
+ size_t mi_arena_min_size(void) {
93
+ return MI_ARENA_MIN_SIZE;
94
+ }
95
+
82
96
  static size_t mi_arena_max_object_size(void) {
83
97
  size_t max_size = mi_option_get_size(mi_option_arena_max_object_size);
84
98
  max_size = _mi_align_up(max_size, MI_ARENA_SLICE_SIZE);
@@ -124,15 +138,34 @@ static uint8_t* mi_arena_start(mi_arena_t* arena) {
124
138
 
125
139
  // Start of a slice
126
140
  uint8_t* mi_arena_slice_start(mi_arena_t* arena, size_t slice_index) {
141
+ mi_assert_internal(slice_index < arena->slice_count);
127
142
  return (mi_arena_start(arena) + mi_size_of_slices(slice_index));
128
143
  }
129
144
 
145
+ mi_page_t* mi_arena_page_at_slice(mi_arena_t* arena, size_t slice_index) {
146
+ mi_assert_internal(slice_index < arena->slice_count);
147
+ if (arena->pages_meta != NULL) {
148
+ mi_page_t* const page = &arena->pages_meta[slice_index];
149
+ #if MI_PAGE_META_ALIGNED_FREE_SMALL
150
+ // pages with small blocks still have the page at the start of the slice (and set the `block_size` in pages_meta to 0)
151
+ if (page->block_size>0) return page;
152
+ #else
153
+ return page;
154
+ #endif
155
+ }
156
+ // fall through (for MI_PAGE_META_ALIGNED_FREE_SMALL)
157
+ return (mi_page_t*)mi_arena_slice_start(arena,slice_index);
158
+ }
159
+
130
160
  // Arena area
131
161
  void* mi_arena_area(mi_arena_id_t arena_id, size_t* size) {
132
162
  if (size != NULL) *size = 0;
133
163
  mi_arena_t* arena = _mi_arena_from_id(arena_id);
134
164
  if (arena == NULL) return NULL;
135
- if (size != NULL) { *size = mi_size_of_slices(arena->slice_count); }
165
+ if (size != NULL) {
166
+ mi_assert_internal(mi_size_of_slices(arena->slice_count) <= arena->total_size);
167
+ *size = arena->total_size;
168
+ }
136
169
  return mi_arena_start(arena);
137
170
  }
138
171
 
@@ -183,7 +216,7 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(
183
216
  mi_arena_t* arena, size_t slice_count, bool commit, size_t tseq, mi_memid_t* memid)
184
217
  {
185
218
  size_t slice_index;
186
- if (!mi_bbitmap_try_find_and_clearN(arena->slices_free, slice_count, tseq, &slice_index)) return NULL;
219
+ if (!mi_bbitmap_try_find_and_clearN(arena->slices_free, tseq, slice_count, &slice_index)) return NULL;
187
220
 
188
221
  // claimed it!
189
222
  void* p = mi_arena_slice_start(arena, slice_index);
@@ -206,7 +239,7 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(
206
239
  if (already_committed < slice_count) {
207
240
  // not all committed, try to commit now
208
241
  bool commit_zero = false;
209
- if (!_mi_os_commit_ex(p, mi_size_of_slices(slice_count), &commit_zero, mi_size_of_slices(slice_count - already_committed))) {
242
+ if (!mi_arena_commit(arena, p, mi_size_of_slices(slice_count), &commit_zero, mi_size_of_slices(slice_count - already_committed))) {
210
243
  // if the commit fails, release ownership, and return NULL;
211
244
  // note: this does not roll back dirty bits but that is ok.
212
245
  mi_bbitmap_setN(arena->slices_free, slice_index, slice_count);
@@ -234,7 +267,7 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(
234
267
  _mi_os_reuse(p, mi_size_of_slices(slice_count));
235
268
  // if the OS has overcommit, and this is the first time we access these pages, then
236
269
  // count the commit now (as at arena reserve we didn't count those commits as these are on-demand)
237
- if (_mi_os_has_overcommit() && touched_slices > 0) {
270
+ if (_mi_os_has_overcommit() && touched_slices > 0 && !arena->memid.is_pinned /* huge pages, issue #1236 */) {
238
271
  mi_subproc_stat_increase( arena->subproc, committed, mi_size_of_slices(touched_slices));
239
272
  }
240
273
  }
@@ -319,7 +352,7 @@ static bool mi_arena_reserve(mi_subproc_t* subproc, size_t req_size, bool allow_
319
352
  // commit eagerly?
320
353
  bool arena_commit = false;
321
354
  const bool overcommit = _mi_os_has_overcommit();
322
- if (mi_option_get(mi_option_arena_eager_commit) == 2) { arena_commit = overcommit; }
355
+ if (mi_option_get(mi_option_arena_eager_commit) == 2) { arena_commit = overcommit || mi_option_is_enabled(mi_option_allow_large_os_pages); }
323
356
  else if (mi_option_get(mi_option_arena_eager_commit) == 1) { arena_commit = true; }
324
357
 
325
358
  // on an OS with overcommit (Linux) we don't count the commit yet as it is on-demand. Once a slice
@@ -349,9 +382,9 @@ static bool mi_arena_reserve(mi_subproc_t* subproc, size_t req_size, bool allow_
349
382
  Arena iteration
350
383
  ----------------------------------------------------------- */
351
384
 
352
- static inline bool mi_arena_is_suitable(mi_arena_t* arena, mi_arena_t* req_arena, bool match_numa, int numa_node, bool allow_pinned) {
385
+ static inline bool mi_arena_is_suitable_ex(mi_arena_t* arena, mi_arena_t* req_arena, bool match_numa, int numa_node, bool allow_pinned) {
353
386
  if (!allow_pinned && arena->memid.is_pinned) return false;
354
- if (!mi_arena_id_is_suitable(arena, req_arena)) return false;
387
+ if (!mi_arena_is_suitable(arena, req_arena)) return false;
355
388
  if (req_arena == NULL) { // if not specific, check numa affinity
356
389
  const bool numa_suitable = (numa_node < 0 || arena->numa_node < 0 || arena->numa_node == numa_node);
357
390
  if (match_numa) { if (!numa_suitable) return false; }
@@ -366,7 +399,7 @@ static size_t mi_arena_start_idx(mi_heap_t* heap, size_t tseq, size_t arena_cycl
366
399
  const size_t hseq = heap->heap_seq;
367
400
  const size_t hcount = mi_atomic_load_relaxed(&heap->subproc->heap_count);
368
401
  if (arena_cycle <= 1) return 0;
369
- if (hseq==0 || hcount<=1) return (tseq % arena_cycle); // common for single heap programs
402
+ if (hseq==0 || hcount<=1 || arena_cycle > 0x8FF) return (tseq % arena_cycle); // common for single heap programs
370
403
 
371
404
  // spread heaps evenly among arena's, and then evenly for threads in their fraction
372
405
  size_t start;
@@ -377,8 +410,8 @@ static size_t mi_arena_start_idx(mi_heap_t* heap, size_t tseq, size_t arena_cycl
377
410
  start = (hseq % arena_cycle);
378
411
  }
379
412
  else {
380
- const size_t hspot = (hseq % hcount);
381
- start = (frac * hspot) / 256;
413
+ const size_t hspot = (hseq % hcount);
414
+ start = (frac * hspot) / 256; // (arena_cycle * (hseq % hcount)) / hcount
382
415
  if (frac >= 512) { // at least 2 arena's per heap?
383
416
  start = start + (tseq % (frac/256));
384
417
  }
@@ -419,7 +452,7 @@ static size_t mi_arena_start_idx(mi_heap_t* heap, size_t tseq, size_t arena_cycl
419
452
 
420
453
  #define mi_forall_suitable_arenas(heap, req_arena, tseq, match_numa, numa_node, allow_large, name_arena) \
421
454
  mi_forall_arenas(heap, req_arena,tseq,name_arena) { \
422
- if (mi_arena_is_suitable(name_arena, req_arena, match_numa, numa_node, allow_large)) { \
455
+ if (mi_arena_is_suitable_ex(name_arena, req_arena, match_numa, numa_node, allow_large)) { \
423
456
 
424
457
  #define mi_forall_suitable_arenas_end() \
425
458
  }} \
@@ -481,13 +514,16 @@ static mi_decl_noinline void* mi_arenas_try_alloc(
481
514
  // don't create arena's while preloading (todo: or should we?)
482
515
  if (_mi_preloading()) return NULL;
483
516
 
517
+ // don't create arena's if OS allocation is disallowed
518
+ if (mi_option_is_enabled(mi_option_disallow_os_alloc)) return NULL;
519
+
484
520
  // otherwise, try to reserve a new arena -- but one thread at a time.. (todo: allow 2 or 4 to reduce contention?)
485
521
  mi_subproc_t* const subproc = heap->subproc;
486
522
  const size_t arena_count = mi_arenas_get_count(subproc);
487
523
  mi_lock(&subproc->arena_reserve_lock) {
488
524
  if (arena_count == mi_arenas_get_count(subproc)) {
489
525
  // we are the first to enter the lock, reserve a fresh arena
490
- mi_arena_id_t arena_id = 0;
526
+ mi_arena_id_t arena_id = _mi_arena_id_none();
491
527
  mi_arena_reserve(subproc, mi_size_of_slices(slice_count), allow_large, &arena_id);
492
528
  }
493
529
  else {
@@ -586,7 +622,7 @@ static bool mi_abandoned_page_unown(mi_page_t* page, mi_theap_t* current_theap)
586
622
 
587
623
  static bool mi_arena_try_claim_abandoned(size_t slice_index, mi_arena_t* arena, bool* keep_abandoned) {
588
624
  // found an abandoned page of the right size
589
- mi_page_t* const page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
625
+ mi_page_t* const page = mi_arena_page_at_slice(arena, slice_index);
590
626
  // can we claim ownership?
591
627
  if (!mi_page_claim_ownership(page)) {
592
628
  // there was a concurrent free that reclaims this page ..
@@ -610,7 +646,7 @@ static mi_arena_pages_t* mi_heap_arena_pages(mi_heap_t* heap, mi_arena_t* arena)
610
646
  mi_assert_internal(arena!=NULL);
611
647
  mi_assert_internal(heap!=NULL);
612
648
  mi_assert(arena->arena_idx < MI_MAX_ARENAS);
613
- return mi_atomic_load_ptr_relaxed(mi_arena_pages_t, &heap->arena_pages[arena->arena_idx]);
649
+ return mi_atomic_load_ptr_acquire(mi_arena_pages_t, &heap->arena_pages[arena->arena_idx]);
614
650
  }
615
651
 
616
652
  static mi_arena_t* mi_page_arena_pages(mi_page_t* page, size_t* slice_index, size_t* slice_count, mi_arena_pages_t** parena_pages) {
@@ -655,7 +691,7 @@ static mi_arena_pages_t* mi_heap_ensure_arena_pages(mi_heap_t* heap, mi_arena_t*
655
691
 
656
692
  static mi_page_t* mi_arenas_page_try_find_abandoned(mi_theap_t* theap, size_t slice_count, size_t block_size)
657
693
  {
658
- mi_heap_t* const heap = theap->heap;
694
+ mi_heap_t* const heap = _mi_theap_heap(theap);
659
695
  const size_t tseq = theap->tld->thread_seq;
660
696
  mi_arena_t* const req_arena = heap->exclusive_arena;
661
697
 
@@ -683,7 +719,7 @@ static mi_page_t* mi_arenas_page_try_find_abandoned(mi_theap_t* theap, size_t sl
683
719
  if (mi_bitmap_try_find_and_claim(bitmap, tseq, &slice_index, &mi_arena_try_claim_abandoned, arena)) {
684
720
  // found an abandoned page of the right size
685
721
  // and claimed ownership.
686
- mi_page_t* page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
722
+ mi_page_t* page = mi_arena_page_at_slice(arena, slice_index);
687
723
  mi_assert_internal(mi_page_is_owned(page));
688
724
  mi_assert_internal(mi_page_is_abandoned(page));
689
725
  mi_assert_internal(mi_heap_has_page(heap, arena, page));
@@ -695,8 +731,7 @@ static mi_page_t* mi_arenas_page_try_find_abandoned(mi_theap_t* theap, size_t sl
695
731
  mi_assert_internal(mi_bbitmap_is_clearN(arena->slices_free, slice_index, slice_count));
696
732
  mi_assert_internal(page->slice_committed > 0 || mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count));
697
733
  mi_assert_internal(mi_bitmap_is_setN(arena->slices_dirty, slice_index, slice_count));
698
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
699
- mi_assert_internal(_mi_ptr_page(page)==page);
734
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN));
700
735
  mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
701
736
  mi_assert_internal(mi_page_block_size(page) == block_size);
702
737
  mi_assert_internal(!mi_page_is_full(page));
@@ -708,146 +743,196 @@ static mi_page_t* mi_arenas_page_try_find_abandoned(mi_theap_t* theap, size_t sl
708
743
  return NULL;
709
744
  }
710
745
 
711
-
712
- // Allocate a fresh page
713
- static mi_page_t* mi_arenas_page_alloc_fresh(mi_theap_t* theap, size_t slice_count, size_t block_size, size_t block_alignment, bool commit)
714
- {
715
- const bool allow_large = (MI_SECURE < 2); // 2 = guard page at end of each arena page
716
- const bool os_align = (block_alignment > MI_PAGE_MAX_OVERALLOC_ALIGN);
746
+ static uint8_t* mi_arenas_page_alloc_fresh_area(mi_theap_t* theap, size_t slice_count, size_t block_size, size_t block_alignment, bool os_align, bool commit, mi_memid_t* memid) {
747
+ MI_UNUSED_RELEASE(block_size);
748
+ const bool allow_large = (MI_SECURE < 5); // 5 = guard page at end of each arena page
717
749
  const size_t page_alignment = MI_ARENA_SLICE_ALIGN;
718
750
 
719
- mi_heap_t* const heap = theap->heap;
751
+ mi_heap_t* const heap = _mi_theap_heap(theap);
720
752
  mi_tld_t* const tld = theap->tld;
721
753
  mi_arena_t* const req_arena = heap->exclusive_arena;
722
754
  const int numa_node = (heap->numa_node >= 0 ? heap->numa_node : tld->numa_node);
723
755
 
724
-
725
756
  // try to allocate from free space in arena's
726
- mi_memid_t memid = _mi_memid_none();
727
- mi_page_t* page = NULL;
757
+ uint8_t* start = NULL;
758
+ *memid = _mi_memid_none();
728
759
  const size_t alloc_size = mi_size_of_slices(slice_count);
729
760
  if (!mi_option_is_enabled(mi_option_disallow_arena_alloc) && // allowed to allocate from arena's?
730
761
  !os_align && // not large alignment
731
762
  slice_count <= mi_arena_max_object_size()/MI_ARENA_SLICE_SIZE) // and not too large
732
763
  {
733
- page = (mi_page_t*)mi_arenas_try_alloc(heap, slice_count, page_alignment, commit, allow_large, req_arena, tld->thread_seq, numa_node, &memid);
734
- if (page != NULL) {
735
- mi_arena_pages_t* const arena_pages = mi_heap_ensure_arena_pages(heap, memid.mem.arena.arena);
764
+ start = (uint8_t*)mi_arenas_try_alloc(heap, slice_count, page_alignment, commit, allow_large, req_arena, tld->thread_seq, numa_node, memid);
765
+ if (start != NULL) {
766
+ mi_arena_pages_t* const arena_pages = mi_heap_ensure_arena_pages(heap, memid->mem.arena.arena);
736
767
  if (arena_pages==NULL) {
737
- _mi_arenas_free(page, mi_size_of_slices(slice_count), page->memid); // roll back
738
- page = NULL;
768
+ _mi_arenas_free(start, mi_size_of_slices(slice_count), *memid); // roll back
769
+ start = NULL;
739
770
  }
740
771
  else {
741
- mi_assert_internal(mi_bitmap_is_clearN(arena_pages->pages, memid.mem.arena.slice_index, memid.mem.arena.slice_count));
742
- mi_bitmap_set(arena_pages->pages, memid.mem.arena.slice_index);
772
+ // note: the following assert should hold if we could check it atomically, but in a concurrent setting we may already allocate in slice_count
773
+ // mi_assert_internal(mi_bitmap_is_clearN(arena_pages->pages, memid->mem.arena.slice_index, memid->mem.arena.slice_count));
774
+ mi_assert_internal(mi_bitmap_is_clear(arena_pages->pages, memid->mem.arena.slice_index));
775
+ mi_bitmap_set(arena_pages->pages, memid->mem.arena.slice_index);
743
776
  }
744
777
  }
745
778
  }
746
779
 
747
780
  // otherwise fall back to the OS
748
- if (page == NULL) {
781
+ if (start == NULL) {
749
782
  if (os_align) {
750
783
  // note: slice_count already includes the page
751
784
  mi_assert_internal(slice_count >= mi_slice_count_of_size(block_size) + mi_slice_count_of_size(page_alignment));
752
- page = (mi_page_t*)mi_arena_os_alloc_aligned(alloc_size, block_alignment, page_alignment /* align offset */, commit, allow_large, req_arena, &memid);
785
+ start = (uint8_t*)mi_arena_os_alloc_aligned(alloc_size, block_alignment, page_alignment /* align offset */, commit, allow_large, req_arena, memid);
753
786
  }
754
787
  else {
755
- page = (mi_page_t*)mi_arena_os_alloc_aligned(alloc_size, page_alignment, 0 /* align offset */, commit, allow_large, req_arena, &memid);
788
+ start = (uint8_t*)mi_arena_os_alloc_aligned(alloc_size, page_alignment, 0 /* align offset */, commit, allow_large, req_arena, memid);
756
789
  }
757
790
  }
758
791
 
759
- if (page == NULL) return NULL;
760
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
761
- mi_assert_internal(!os_align || _mi_is_aligned((uint8_t*)page + page_alignment, block_alignment));
762
-
763
- // guard page at the end of mimalloc page?
764
- #if MI_SECURE < 2
765
- const size_t page_noguard_size = alloc_size;
766
- #else
767
- mi_assert(alloc_size > _mi_os_secure_guard_page_size());
768
- const size_t page_noguard_size = alloc_size - _mi_os_secure_guard_page_size();
769
- if (memid.initially_committed) {
770
- _mi_os_secure_guard_page_set_at((uint8_t*)page + page_noguard_size, memid);
771
- }
772
- #endif
773
-
774
- // claimed free slices: initialize the page partly
775
- if (!memid.initially_zero && memid.initially_committed) {
776
- mi_track_mem_undefined(page, slice_count * MI_ARENA_SLICE_SIZE);
777
- _mi_memzero_aligned(page, sizeof(*page));
778
- }
779
- else if (memid.initially_committed) {
780
- mi_track_mem_defined(page, slice_count * MI_ARENA_SLICE_SIZE);
781
- }
782
- #if MI_DEBUG > 1
783
- if (memid.initially_zero && memid.initially_committed) {
784
- if (!mi_mem_is_zero(page, page_noguard_size)) {
785
- _mi_error_message(EFAULT, "internal error: page memory was not zero initialized.\n");
786
- memid.initially_zero = false;
787
- _mi_memzero_aligned(page, sizeof(*page));
788
- }
789
- }
790
- #endif
791
- mi_assert(MI_PAGE_INFO_SIZE >= mi_page_info_size());
792
+ if (start == NULL) return NULL;
793
+ mi_assert_internal(_mi_is_aligned(start, MI_PAGE_ALIGN));
794
+ mi_assert_internal(!os_align || _mi_is_aligned(start + page_alignment, block_alignment));
795
+ return start;
796
+ }
792
797
 
793
- size_t block_start;
798
+ static size_t mi_page_block_start(size_t block_size, bool os_align)
799
+ {
794
800
  #if MI_GUARDED
795
801
  // in a guarded build, we align pages with blocks a multiple of an OS page size, to the OS page size
796
802
  // this ensures that all blocks in such pages are OS page size aligned (which is needed for the guard pages)
797
803
  const size_t os_page_size = _mi_os_page_size();
798
804
  mi_assert_internal(MI_PAGE_ALIGN >= os_page_size);
799
805
  if (!os_align && block_size % os_page_size == 0 && block_size > os_page_size /* at least 2 or more */ ) {
800
- block_start = _mi_align_up(mi_page_info_size(), os_page_size);
806
+ return _mi_align_up(mi_page_info_size(), os_page_size);
801
807
  }
802
808
  else
803
809
  #endif
804
810
  if (os_align) {
805
- block_start = MI_PAGE_ALIGN;
811
+ return MI_PAGE_ALIGN;
806
812
  }
807
813
  else if (_mi_is_power_of_two(block_size) && block_size <= MI_PAGE_MAX_START_BLOCK_ALIGN2) {
808
814
  // naturally align power-of-2 blocks up to MI_PAGE_MAX_START_BLOCK_ALIGN2 size (4KiB)
809
- block_start = _mi_align_up(mi_page_info_size(), block_size);
815
+ return _mi_align_up(mi_page_info_size(), block_size);
810
816
  }
811
817
  else if (block_size != 0 && (block_size % MI_PAGE_OSPAGE_BLOCK_ALIGN2) == 0) {
812
818
  // also align large pages that are a multiple of MI_PAGE_OSPAGE_BLOCK_ALIGN2 (4KiB)
813
- block_start = _mi_align_up(mi_page_info_size(), MI_PAGE_OSPAGE_BLOCK_ALIGN2);
819
+ return _mi_align_up(mi_page_info_size(), MI_PAGE_OSPAGE_BLOCK_ALIGN2);
814
820
  }
815
821
  else {
816
822
  // otherwise start after the info
817
- block_start = mi_page_info_size();
823
+ return mi_page_info_size();
824
+ }
825
+ }
826
+
827
+ // Allocate a fresh page
828
+ static mi_page_t* mi_arenas_page_alloc_fresh(mi_theap_t* theap, size_t slice_count, size_t block_size, size_t block_alignment, bool commit)
829
+ {
830
+ const bool os_align = (block_alignment > MI_PAGE_MAX_OVERALLOC_ALIGN);
831
+ const size_t alloc_size = mi_size_of_slices(slice_count);
832
+ mi_memid_t memid = _mi_memid_none();
833
+ uint8_t* const slice_start = mi_arenas_page_alloc_fresh_area(theap,slice_count,block_size,block_alignment,os_align,commit,&memid);
834
+ if (!slice_start) return NULL;
835
+
836
+ // guard page at the end of mimalloc page?
837
+ #if MI_SECURE>=5
838
+ mi_assert(alloc_size > _mi_os_secure_guard_page_size());
839
+ const size_t page_noguard_size = alloc_size - _mi_os_secure_guard_page_size();
840
+ if (memid.initially_committed) {
841
+ _mi_os_secure_guard_page_set_at(slice_start + page_noguard_size, memid);
842
+ }
843
+ #else
844
+ const size_t page_noguard_size = alloc_size;
845
+ #endif
846
+
847
+ // allocate the page meta info
848
+ mi_page_t* page = NULL;
849
+ bool page_meta_is_separate = false;
850
+ size_t block_start = 0;
851
+
852
+ // allocate page meta info at the arena start?
853
+ if (memid.memkind == MI_MEM_ARENA) {
854
+ mi_arena_t* const arena = memid.mem.arena.arena;
855
+ if (arena->pages_meta != NULL) {
856
+ mi_assert_internal(MI_PAGE_META_IS_SEPARATED!=0);
857
+ mi_page_t* const page_meta = &arena->pages_meta[memid.mem.arena.slice_index];
858
+ mi_assert_internal(page_meta->block_size == 0);
859
+ #if MI_PAGE_META_ALIGNED_FREE_SMALL
860
+ // if `block_size <= MI_SMALL_SIZE_MAX` we put the page info in front of the slice,
861
+ // (note: it is important that `page_meta->block_size == 0` for `mi_arena_page_at_slice`)
862
+ if (block_size > MI_SMALL_SIZE_MAX)
863
+ #endif
864
+ {
865
+ page = page_meta;
866
+ page_meta_is_separate = true;
867
+ block_start = 0;
868
+ #if !defined(MI_PAGE_BLOCK_START_MAX_OFFSET)
869
+ #define MI_PAGE_BLOCK_START_MAX_OFFSET (8*MI_INTPTR_BITS) /* 512 */
870
+ #endif
871
+ if (block_size >= MI_INTPTR_SIZE && block_size <= MI_PAGE_BLOCK_START_MAX_OFFSET && _mi_is_power_of_two(block_size)) {
872
+ block_start += block_size;
873
+ }
874
+ mi_assert_internal(page->block_size == 0);
875
+ _mi_memzero_aligned(page, sizeof(*page));
876
+ }
877
+ }
878
+ }
879
+ if (page == NULL) {
880
+ // put page meta info in front of the slice
881
+ page = (mi_page_t*)slice_start;
882
+ block_start = mi_page_block_start(block_size, os_align);
818
883
  }
819
- const size_t reserved = (os_align ? 1 : (page_noguard_size - block_start) / block_size);
820
- mi_assert_internal(reserved > 0 && reserved <= UINT16_MAX);
821
884
 
822
885
  // commit first block?
823
886
  size_t commit_size = 0;
824
887
  if (!memid.initially_committed) {
825
888
  commit_size = _mi_align_up(block_start + block_size, MI_PAGE_MIN_COMMIT_SIZE);
826
889
  if (commit_size > page_noguard_size) { commit_size = page_noguard_size; }
827
- bool is_zero;
828
- if mi_unlikely(!mi_arena_commit( mi_memid_arena(memid), page, commit_size, &is_zero, 0)) {
829
- _mi_arenas_free(page, alloc_size, memid);
890
+ bool is_zero = false;
891
+ if mi_unlikely(!mi_arena_commit( mi_memid_arena(memid), slice_start, commit_size, &is_zero, 0)) {
892
+ _mi_arenas_free(slice_start, alloc_size, memid);
830
893
  return NULL;
831
894
  }
832
- if (!memid.initially_zero && !is_zero) {
833
- _mi_memzero_aligned(page, commit_size);
895
+ }
896
+ if (!memid.initially_zero && !page_meta_is_separate) {
897
+ _mi_memzero_aligned(page, sizeof(*page));
898
+ }
899
+
900
+ // claimed free slices: initialize the page partly
901
+ if (!memid.initially_zero && memid.initially_committed) {
902
+ mi_track_mem_undefined(slice_start, slice_count * MI_ARENA_SLICE_SIZE);
903
+ }
904
+ else if (memid.initially_committed) {
905
+ mi_track_mem_defined(slice_start, slice_count * MI_ARENA_SLICE_SIZE);
906
+ }
907
+ #if MI_DEBUG > 1
908
+ if (memid.initially_zero && memid.initially_committed) {
909
+ if (!mi_mem_is_zero(slice_start, page_noguard_size)) {
910
+ _mi_error_message(EFAULT, "internal error: page memory was not zero initialized.\n");
911
+ memid.initially_zero = false;
912
+ if (block_start > 0) { _mi_memzero_aligned(page, sizeof(*page)); }
834
913
  }
835
914
  }
915
+ #endif
916
+ const size_t reserved = (os_align ? 1 : (page_noguard_size - block_start) / block_size);
917
+ mi_assert_internal(reserved > 0 && reserved <= UINT16_MAX);
836
918
 
837
919
  // initialize
838
920
  page->reserved = (uint16_t)reserved;
839
- page->page_start = (uint8_t*)page + block_start;
921
+ page->page_start = slice_start + block_start;
840
922
  page->block_size = block_size;
841
923
  page->slice_committed = commit_size;
842
924
  page->memid = memid;
843
925
  page->free_is_zero = memid.initially_zero;
926
+ mi_assert_internal(page->free==NULL);
927
+ mi_assert_internal(page_meta_is_separate == mi_page_meta_is_separated(page));
928
+ mi_assert_internal(mi_page_slice_start(page) == slice_start);
844
929
 
845
930
  // and own it
846
931
  mi_page_claim_ownership(page);
847
932
 
848
933
  // register in the page map
849
934
  if mi_unlikely(!_mi_page_map_register(page)) {
850
- _mi_arenas_free( page, alloc_size, memid );
935
+ _mi_arenas_free( slice_start, alloc_size, memid );
851
936
  return NULL;
852
937
  }
853
938
 
@@ -855,7 +940,7 @@ static mi_page_t* mi_arenas_page_alloc_fresh(mi_theap_t* theap, size_t slice_cou
855
940
  mi_theap_stat_increase(theap, pages, 1);
856
941
  mi_theap_stat_increase(theap, page_bins[_mi_page_stats_bin(page)], 1);
857
942
 
858
- mi_assert_internal(_mi_ptr_page(page)==page);
943
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page),MI_PAGE_ALIGN));
859
944
  mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
860
945
  mi_assert_internal(mi_page_block_size(page) == block_size);
861
946
  mi_assert_internal(mi_page_is_abandoned(page));
@@ -937,8 +1022,7 @@ mi_page_t* _mi_arenas_page_alloc(mi_theap_t* theap, size_t block_size, size_t bl
937
1022
  return NULL;
938
1023
  }
939
1024
  // mi_assert_internal(page == NULL || _mi_page_segment(page)->subproc == tld->subproc);
940
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
941
- mi_assert_internal(_mi_ptr_page(page)==page);
1025
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN));
942
1026
  mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
943
1027
  mi_assert_internal(block_alignment <= MI_PAGE_MAX_OVERALLOC_ALIGN || _mi_is_aligned(mi_page_start(page), block_alignment));
944
1028
 
@@ -946,8 +1030,8 @@ mi_page_t* _mi_arenas_page_alloc(mi_theap_t* theap, size_t block_size, size_t bl
946
1030
  }
947
1031
 
948
1032
  void _mi_arenas_page_free(mi_page_t* page, mi_theap_t* current_theapx) {
949
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
950
- mi_assert_internal(_mi_ptr_page(page)==page);
1033
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN));
1034
+ mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
951
1035
  mi_assert_internal(mi_page_is_owned(page));
952
1036
  mi_assert_internal(mi_page_all_free(page));
953
1037
  mi_assert_internal(mi_page_is_abandoned(page));
@@ -983,9 +1067,9 @@ void _mi_arenas_page_free(mi_page_t* page, mi_theap_t* current_theapx) {
983
1067
 
984
1068
  // recommit guard page at the end?
985
1069
  // we must do this since we may later allocate large spans over this page and cannot have a guard page in between
986
- #if MI_SECURE >= 2
1070
+ #if MI_SECURE >= 5
987
1071
  if (!page->memid.is_pinned) {
988
- _mi_os_secure_guard_page_reset_before((uint8_t*)page + mi_page_full_size(page), page->memid);
1072
+ _mi_os_secure_guard_page_reset_before(mi_page_slice_start(page) + mi_page_full_size(page), page->memid);
989
1073
  }
990
1074
  #endif
991
1075
 
@@ -1018,7 +1102,8 @@ void _mi_arenas_page_free(mi_page_t* page, mi_theap_t* current_theapx) {
1018
1102
  mi_assert_internal(mi_bitmap_is_setN(arena->slices_committed, slice_index, slice_count));
1019
1103
  }
1020
1104
  }
1021
- _mi_arenas_free(page, mi_page_full_size(page), page->memid);
1105
+ if (mi_page_meta_is_separated(page)) { page->block_size = 0; } // for assertion checking
1106
+ _mi_arenas_free( mi_page_slice_start(page), mi_page_full_size(page), page->memid);
1022
1107
  }
1023
1108
 
1024
1109
  /* -----------------------------------------------------------
@@ -1026,8 +1111,8 @@ void _mi_arenas_page_free(mi_page_t* page, mi_theap_t* current_theapx) {
1026
1111
  ----------------------------------------------------------- */
1027
1112
 
1028
1113
  void _mi_arenas_page_abandon(mi_page_t* page, mi_theap_t* current_theap) {
1029
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
1030
- mi_assert_internal(_mi_ptr_page(page)==page);
1114
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN));
1115
+ mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
1031
1116
  mi_assert_internal(mi_page_is_owned(page));
1032
1117
  mi_assert_internal(mi_page_is_abandoned(page));
1033
1118
  mi_assert_internal(!mi_page_all_free(page));
@@ -1035,7 +1120,7 @@ void _mi_arenas_page_abandon(mi_page_t* page, mi_theap_t* current_theap) {
1035
1120
  mi_assert_internal(_mi_thread_id()==current_theap->tld->thread_id);
1036
1121
  // mi_assert_internal(current_theap == _mi_page_associated_theap(page));
1037
1122
 
1038
- mi_heap_t* heap = mi_page_heap(page); mi_assert_internal(heap==current_theap->heap);
1123
+ mi_heap_t* heap = mi_page_heap(page); mi_assert_internal(heap==_mi_theap_heap(current_theap));
1039
1124
  if (page->memid.memkind==MI_MEM_ARENA && !mi_page_is_full(page)) {
1040
1125
  // make available for allocations
1041
1126
  size_t bin = _mi_bin(mi_page_block_size(page));
@@ -1053,7 +1138,7 @@ void _mi_arenas_page_abandon(mi_page_t* page, mi_theap_t* current_theap) {
1053
1138
  const bool was_clear = mi_bitmap_set(arena_pages->pages_abandoned[bin], slice_index);
1054
1139
  MI_UNUSED(was_clear); mi_assert_internal(was_clear);
1055
1140
  mi_atomic_increment_relaxed(&heap->abandoned_count[bin]);
1056
- mi_theap_stat_increase(current_theap, pages_abandoned, 1);
1141
+ mi_theap_stat_increase(current_theap, pages_abandoned, 1);
1057
1142
  }
1058
1143
  else {
1059
1144
  // page is full (or a singleton), or the page is OS/externally allocated
@@ -1076,8 +1161,8 @@ void _mi_arenas_page_abandon(mi_page_t* page, mi_theap_t* current_theap) {
1076
1161
 
1077
1162
  // this is called from `free.c:mi_free_try_collect_mt` only.
1078
1163
  bool _mi_arenas_page_try_reabandon_to_mapped(mi_page_t* page) {
1079
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
1080
- mi_assert_internal(_mi_ptr_page(page)==page);
1164
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN));
1165
+ mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
1081
1166
  mi_assert_internal(mi_page_is_owned(page));
1082
1167
  mi_assert_internal(mi_page_is_abandoned(page));
1083
1168
  mi_assert_internal(!mi_page_is_abandoned_mapped(page));
@@ -1104,8 +1189,8 @@ bool _mi_arenas_page_try_reabandon_to_mapped(mi_page_t* page) {
1104
1189
 
1105
1190
  // called from `mi_free` if trying to unabandon an abandoned page
1106
1191
  void _mi_arenas_page_unabandon(mi_page_t* page, mi_theap_t* current_theapx) {
1107
- mi_assert_internal(_mi_is_aligned(page, MI_PAGE_ALIGN));
1108
- mi_assert_internal(_mi_ptr_page(page)==page);
1192
+ mi_assert_internal(_mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN));
1193
+ mi_assert_internal(_mi_ptr_page(mi_page_start(page))==page);
1109
1194
  mi_assert_internal(mi_page_is_owned(page));
1110
1195
  mi_assert_internal(mi_page_is_abandoned(page));
1111
1196
  mi_assert_internal(current_theapx==NULL || _mi_thread_id()==current_theapx->tld->thread_id);
@@ -1183,7 +1268,7 @@ void _mi_arenas_free(void* p, size_t size, mi_memid_t memid) {
1183
1268
  }
1184
1269
  mi_assert_internal(slice_index < arena->slice_count);
1185
1270
  mi_assert_internal(slice_index >= mi_arena_info_slices(arena));
1186
- if (slice_index < mi_arena_info_slices(arena) || slice_index > arena->slice_count) {
1271
+ if (slice_index < mi_arena_info_slices(arena) || slice_index >= arena->slice_count) {
1187
1272
  _mi_error_message(EINVAL, "trying to free from an invalid arena block: %p, size %zu, memid: 0x%zx\n", p, size, memid);
1188
1273
  return;
1189
1274
  }
@@ -1220,25 +1305,41 @@ void _mi_arenas_collect(bool force_purge, bool visit_all, mi_tld_t* tld) {
1220
1305
 
1221
1306
 
1222
1307
  // Is a pointer contained in the given arena area?
1223
- bool mi_arena_contains(mi_arena_id_t arena_id, const void* p) {
1224
- mi_arena_t* arena = _mi_arena_from_id(arena_id);
1225
- return (mi_arena_start(arena) <= (const uint8_t*)p &&
1308
+ static bool mi_arena_strictly_contains(mi_arena_t* arena, const void* p) {
1309
+ return (arena != NULL &&
1310
+ mi_arena_start(arena) <= (const uint8_t*)p &&
1226
1311
  mi_arena_start(arena) + mi_size_of_slices(arena->slice_count) >(const uint8_t*)p);
1227
1312
  }
1228
1313
 
1229
1314
  // Is a pointer inside any of our arenas?
1230
- bool _mi_arenas_contain(const void* p) {
1315
+ static bool mi_arenas_contain_ex(const void* p, mi_arena_t* parent) {
1231
1316
  mi_subproc_t* subproc = _mi_subproc();
1232
1317
  const size_t max_arena = mi_arenas_get_count(subproc);
1233
1318
  for (size_t i = 0; i < max_arena; i++) {
1234
1319
  mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[i]);
1235
- if (arena != NULL && mi_arena_contains(arena,p)) {
1236
- return true;
1320
+ if (arena != NULL) {
1321
+ if (parent==NULL || arena==parent || arena->parent==parent) {
1322
+ if (mi_arena_strictly_contains(arena, p)) {
1323
+ return true;
1324
+ }
1325
+ }
1237
1326
  }
1238
1327
  }
1239
1328
  return false;
1240
1329
  }
1241
1330
 
1331
+ // Is a pointer inside any of our arenas?
1332
+ bool _mi_arenas_contain(const void* p) {
1333
+ return mi_arenas_contain_ex(p, NULL);
1334
+ }
1335
+
1336
+ // Is a pointer contained in the given arena area?
1337
+ bool mi_arena_contains(mi_arena_id_t arena_id, const void* p) {
1338
+ mi_arena_t* arena = _mi_arena_from_id(arena_id);
1339
+ if (arena==NULL) return false;
1340
+ else if (mi_arena_strictly_contains(arena, p)) return true;
1341
+ else return mi_arenas_contain_ex(p, arena); // maybe a subarena?
1342
+ }
1242
1343
 
1243
1344
 
1244
1345
  /* -----------------------------------------------------------
@@ -1262,7 +1363,7 @@ static void mi_arenas_unsafe_destroy(mi_subproc_t* subproc) {
1262
1363
  }
1263
1364
  // try to lower the max arena.
1264
1365
  size_t expected = arena_count;
1265
- mi_atomic_cas_strong_acq_rel(&subproc->arena_count, &expected, 0);
1366
+ mi_atomic_cas_strong_acq_rel(&subproc->arena_count, &expected, (size_t)0);
1266
1367
  }
1267
1368
 
1268
1369
 
@@ -1282,7 +1383,7 @@ static bool mi_arenas_add(mi_subproc_t* subproc, mi_arena_t* arena, mi_arena_id_
1282
1383
  {
1283
1384
  mi_assert_internal(arena != NULL);
1284
1385
  mi_assert_internal(arena->slice_count > 0);
1285
- if (arena_id != NULL) { *arena_id = NULL; }
1386
+ if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); }
1286
1387
 
1287
1388
  // try to find a NULL entry
1288
1389
  mi_arena_t* expected;
@@ -1293,7 +1394,7 @@ static bool mi_arenas_add(mi_subproc_t* subproc, mi_arena_t* arena, mi_arena_id_
1293
1394
  expected = NULL;
1294
1395
  if (mi_atomic_cas_ptr_strong_release(mi_arena_t, &subproc->arenas[i], &expected, arena)) {
1295
1396
  // success
1296
- if (arena_id != NULL) { *arena_id = arena; }
1397
+ if (arena_id != NULL) { *arena_id = mi_arena_id_from_arena(arena); }
1297
1398
  return true;
1298
1399
  }
1299
1400
  }
@@ -1306,7 +1407,7 @@ static bool mi_arenas_add(mi_subproc_t* subproc, mi_arena_t* arena, mi_arena_id_
1306
1407
  expected = NULL;
1307
1408
  if (mi_atomic_cas_ptr_strong_release(mi_arena_t, &subproc->arenas[count], &expected, arena)) {
1308
1409
  mi_subproc_stat_counter_increase(arena->subproc, arena_count, 1);
1309
- if (arena_id != NULL) { *arena_id = arena; }
1410
+ if (arena_id != NULL) { *arena_id = mi_arena_id_from_arena(arena); }
1310
1411
  return true;
1311
1412
  }
1312
1413
  }
@@ -1335,7 +1436,12 @@ static size_t mi_arena_info_slices_needed(size_t slice_count, size_t* bitmap_bas
1335
1436
  const size_t base_size = _mi_align_up(sizeof(mi_arena_t), MI_BCHUNK_SIZE);
1336
1437
  const size_t bitmaps_count = 4 + MI_ARENA_BIN_COUNT; // commit, dirty, purge, pages, and abandoned
1337
1438
  const size_t bitmaps_size = bitmaps_count * mi_bitmap_size(slice_count, NULL) + mi_bbitmap_size(slice_count, NULL); // + free
1338
- const size_t size = base_size + bitmaps_size;
1439
+ #if MI_PAGE_META_IS_SEPARATED
1440
+ const size_t pages_size = slice_count * sizeof(mi_page_t);
1441
+ #else
1442
+ const size_t pages_size = 0;
1443
+ #endif
1444
+ const size_t size = base_size + bitmaps_size + pages_size;
1339
1445
 
1340
1446
  const size_t os_page_size = _mi_os_page_size();
1341
1447
  const size_t info_size = _mi_align_up(size, os_page_size) + _mi_os_secure_guard_page_size();
@@ -1372,41 +1478,29 @@ static mi_arena_pages_t* mi_arena_pages_alloc(mi_arena_t* arena) {
1372
1478
  return arena_pages;
1373
1479
  }
1374
1480
 
1375
-
1376
- static bool mi_manage_os_memory_ex2(mi_subproc_t* subproc, void* start, size_t size, int numa_node, bool exclusive,
1377
- mi_memid_t memid, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept
1481
+ static mi_arena_t* mi_arena_initialize(mi_subproc_t* subproc, void* start,
1482
+ size_t slice_count, mi_arena_t* parent, size_t total_size,
1483
+ int numa_node, bool exclusive,
1484
+ mi_memid_t memid, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id)
1378
1485
  {
1379
- mi_assert(_mi_is_aligned(start,MI_ARENA_SLICE_SIZE));
1380
- mi_assert(start!=NULL);
1381
- if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); }
1382
- if (start==NULL) return false;
1383
- if (!_mi_is_aligned(start,MI_ARENA_SLICE_SIZE)) {
1384
- // we can align the start since the memid tracks the real base of the memory.
1385
- void* const aligned_start = _mi_align_up_ptr(start, MI_ARENA_SLICE_SIZE);
1386
- const size_t diff = (uint8_t*)aligned_start - (uint8_t*)start;
1387
- if (diff >= size || (size - diff) < MI_ARENA_SLICE_SIZE) {
1388
- _mi_warning_message("after alignment, the size of the arena becomes too small (memory at %p with size %zu)\n", start, size);
1389
- return false;
1390
- }
1391
- start = aligned_start;
1392
- size = size - diff;
1393
- }
1486
+ mi_assert_internal(_mi_is_aligned(start,MI_ARENA_SLICE_ALIGN));
1487
+ mi_assert_internal(mi_size_of_slices(slice_count)>=MI_ARENA_MIN_SIZE);
1394
1488
 
1395
- const size_t slice_count = _mi_align_down(size / MI_ARENA_SLICE_SIZE, MI_BCHUNK_BITS);
1396
1489
  if (slice_count > MI_BITMAP_MAX_BIT_COUNT) { // 16 GiB for now
1397
- // todo: allow larger areas (either by splitting it up in arena's or having larger arena's)
1398
- _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);
1399
- return false;
1490
+ // note: this should never happen if called from `mi_manage_os_memory` (as that allocates sub-arenas when needed)
1491
+ _mi_warning_message("cannot use OS memory since it is too large (size %zu MiB, maximum is %zu MiB)", mi_size_of_slices(slice_count)/MI_MiB, mi_size_of_slices(MI_BITMAP_MAX_BIT_COUNT)/MI_MiB);
1492
+ return NULL;
1400
1493
  }
1494
+
1401
1495
  size_t bitmap_base;
1402
1496
  const size_t info_slices = mi_arena_info_slices_needed(slice_count, &bitmap_base);
1403
1497
  if (slice_count < info_slices+1) {
1404
- _mi_warning_message("cannot use OS memory since it is not large enough (size %zu KiB, minimum required is %zu KiB)", size/MI_KiB, mi_size_of_slices(info_slices+1)/MI_KiB);
1405
- return false;
1498
+ _mi_warning_message("cannot use OS memory since it is not large enough (size %zu KiB, minimum required is %zu KiB)", mi_size_of_slices(slice_count)/MI_KiB, mi_size_of_slices(info_slices+1)/MI_KiB);
1499
+ return NULL;
1406
1500
  }
1407
1501
  else if (info_slices >= MI_ARENA_MAX_CHUNK_OBJ_SLICES) {
1408
- _mi_warning_message("cannot use OS memory since it is too large with respect to the maximum object size (size %zu MiB, meta-info slices %zu, maximum object slices are %zu)", size/MI_MiB, info_slices, MI_ARENA_MAX_CHUNK_OBJ_SLICES);
1409
- return false;
1502
+ _mi_warning_message("cannot use OS memory since it is too large with respect to the maximum object size (size %zu MiB, meta-info slices %zu, maximum object slices are %zu)", mi_size_of_slices(slice_count)/MI_MiB, info_slices, MI_ARENA_MAX_CHUNK_OBJ_SLICES);
1503
+ return NULL;
1410
1504
  }
1411
1505
 
1412
1506
  mi_arena_t* arena = (mi_arena_t*)start;
@@ -1425,11 +1519,11 @@ static bool mi_manage_os_memory_ex2(mi_subproc_t* subproc, void* start, size_t s
1425
1519
  }
1426
1520
  if (!ok) {
1427
1521
  _mi_warning_message("unable to commit meta-data for OS memory");
1428
- return false;
1522
+ return NULL;
1429
1523
  }
1430
1524
  }
1431
1525
  else if (!memid.is_pinned) {
1432
- // if MI_SECURE, set a guard page at the end
1526
+ // if MI_SECURE, set a guard page at the end of the arena info
1433
1527
  // todo: this does not respect the commit_fun as the memid is of external memory
1434
1528
  _mi_os_secure_guard_page_set_before((uint8_t*)arena + mi_size_of_slices(info_slices), memid);
1435
1529
  }
@@ -1438,27 +1532,39 @@ static bool mi_manage_os_memory_ex2(mi_subproc_t* subproc, void* start, size_t s
1438
1532
  }
1439
1533
 
1440
1534
  // init
1441
- arena->subproc = subproc;
1442
- arena->memid = memid;
1535
+ arena->subproc = subproc;
1536
+ arena->memid = memid;
1443
1537
  arena->is_exclusive = exclusive;
1444
- arena->slice_count = slice_count;
1445
- arena->info_slices = info_slices;
1446
- arena->numa_node = numa_node; // TODO: or get the current numa node if -1? (now it allows anyone to allocate on -1)
1538
+ arena->slice_count = slice_count;
1539
+ arena->info_slices = info_slices;
1540
+ if (numa_node<0 && mi_option_is_enabled(mi_option_arena_is_numa_local)) {
1541
+ arena->numa_node = _mi_os_numa_node();
1542
+ }
1543
+ else {
1544
+ arena->numa_node = numa_node;
1545
+ }
1447
1546
  arena->purge_expire = 0;
1448
- arena->commit_fun = commit_fun;
1547
+ arena->commit_fun = commit_fun;
1449
1548
  arena->commit_fun_arg = commit_fun_arg;
1450
- // mi_lock_init(&arena->abandoned_visit_lock);
1549
+ arena->parent = parent;
1550
+ arena->total_size = total_size;
1451
1551
 
1452
1552
  // init bitmaps
1453
1553
  uint8_t* base = mi_arena_start(arena) + bitmap_base;
1454
- arena->slices_free = mi_arena_bbitmap_init(slice_count,&base);
1455
- arena->slices_committed = mi_arena_bitmap_init(slice_count,&base);
1456
- arena->slices_dirty = mi_arena_bitmap_init(slice_count,&base);
1554
+ arena->slices_free = mi_arena_bbitmap_init(slice_count, &base);
1555
+ arena->slices_committed = mi_arena_bitmap_init(slice_count, &base);
1556
+ arena->slices_dirty = mi_arena_bitmap_init(slice_count, &base);
1457
1557
  arena->slices_purge = mi_arena_bitmap_init(slice_count, &base);
1458
1558
  arena->pages_main.pages = mi_arena_bitmap_init(slice_count, &base);
1459
- for( size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) {
1460
- arena->pages_main.pages_abandoned[i] = mi_arena_bitmap_init(slice_count,&base);
1559
+ for (size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) {
1560
+ arena->pages_main.pages_abandoned[i] = mi_arena_bitmap_init(slice_count, &base);
1461
1561
  }
1562
+ #if MI_PAGE_META_IS_SEPARATED
1563
+ arena->pages_meta = (mi_page_t*)base;
1564
+ base += (slice_count * sizeof(mi_page_t));
1565
+ #else
1566
+ arena->pages_meta = NULL;
1567
+ #endif
1462
1568
  mi_assert_internal(mi_size_of_slices(info_slices) >= (size_t)(base - mi_arena_start(arena)));
1463
1569
 
1464
1570
  // reserve our meta info (and reserve slices outside the memory area)
@@ -1466,19 +1572,83 @@ static bool mi_manage_os_memory_ex2(mi_subproc_t* subproc, void* start, size_t s
1466
1572
  if (memid.initially_committed) {
1467
1573
  mi_bitmap_unsafe_setN(arena->slices_committed, 0, arena->slice_count);
1468
1574
  }
1469
- else {
1470
- mi_bitmap_setN(arena->slices_committed, 0, info_slices, NULL);
1471
- }
1472
1575
  if (!memid.initially_zero) {
1473
1576
  mi_bitmap_unsafe_setN(arena->slices_dirty, 0, arena->slice_count);
1474
1577
  }
1475
- else {
1476
- mi_bitmap_setN(arena->slices_dirty, 0, info_slices, NULL);
1477
- }
1478
1578
 
1479
- return mi_arenas_add(subproc, arena, arena_id);
1579
+ if (!mi_arenas_add(subproc, arena, arena_id)) { return NULL; }
1580
+ return arena;
1480
1581
  }
1481
1582
 
1583
+ static bool mi_manage_os_memory_ex2(mi_subproc_t* subproc, void* start, size_t size, int numa_node, bool exclusive,
1584
+ mi_memid_t memid, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept
1585
+ {
1586
+ // checks
1587
+ mi_assert(_mi_is_aligned(start, MI_ARENA_SLICE_SIZE));
1588
+ mi_assert(start!=NULL);
1589
+ if (arena_id != NULL) { *arena_id = _mi_arena_id_none(); }
1590
+ if (start==NULL) return false;
1591
+ if (!_mi_is_aligned(start, MI_ARENA_SLICE_SIZE)) {
1592
+ // we can align the start since the memid tracks the real base of the memory.
1593
+ void* const aligned_start = _mi_align_up_ptr(start, MI_ARENA_SLICE_SIZE);
1594
+ const size_t diff = (uint8_t*)aligned_start - (uint8_t*)start;
1595
+ if (diff >= size || (size - diff) < MI_ARENA_SLICE_SIZE) {
1596
+ _mi_warning_message("after alignment, the size of the arena becomes too small (memory at %p with size %zu)\n", start, size);
1597
+ return false;
1598
+ }
1599
+ start = aligned_start;
1600
+ size = size - diff;
1601
+ }
1602
+
1603
+ // allocate enough arena's to span the full memory area
1604
+ // the first arena is the owner, the rest are "sub-arena" (with `parent` pointing to the first one)
1605
+ size_t total_slice_count = _mi_align_down(size / MI_ARENA_SLICE_SIZE, MI_BCHUNK_BITS);
1606
+ size_t total_size = mi_size_of_slices(total_slice_count);
1607
+ if (total_size < MI_ARENA_MIN_SIZE) {
1608
+ _mi_warning_message("cannot use OS memory since it is not large enough (size %zu KiB, minimum required is %zu KiB)", size/MI_KiB, MI_ARENA_MIN_SIZE/MI_KiB);
1609
+ return false;
1610
+ }
1611
+
1612
+ mi_arena_t* parent = NULL;
1613
+ do {
1614
+ // counting down on the total_slice_count
1615
+ size_t slice_count = total_slice_count;
1616
+ if (slice_count > MI_BITMAP_MAX_BIT_COUNT) { // 16 GiB for now (with 64KiB slices)
1617
+ slice_count = MI_BITMAP_MAX_BIT_COUNT;
1618
+ }
1619
+
1620
+ // initialize
1621
+ mi_arena_t* arena = mi_arena_initialize( subproc, start, slice_count, parent,
1622
+ (parent==NULL ? total_size : 0), numa_node, exclusive,
1623
+ memid, commit_fun, commit_fun_arg,
1624
+ (parent==NULL ? arena_id : NULL));
1625
+ if (arena==NULL) {
1626
+ // failed to initialize due to failing commit or too many arena's
1627
+ if (parent==NULL) {
1628
+ return false;
1629
+ }
1630
+ else {
1631
+ // partial success, but failed to use the full area..
1632
+ // todo: roll-back in this case? that requires a lock on the arena's array though
1633
+ mi_assert(mi_size_of_slices(total_slice_count) <= parent->total_size);
1634
+ parent->total_size -= mi_size_of_slices(total_slice_count);
1635
+ return true;
1636
+ }
1637
+ }
1638
+
1639
+ // success
1640
+ if (parent==NULL) {
1641
+ parent = arena;
1642
+ memid.memkind = MI_MEM_NONE;
1643
+ }
1644
+ mi_assert(slice_count <= total_slice_count);
1645
+ total_slice_count -= slice_count;
1646
+ start = (uint8_t*)start + mi_size_of_slices(slice_count);
1647
+ }
1648
+ while (total_slice_count > 0);
1649
+
1650
+ return true;
1651
+ }
1482
1652
 
1483
1653
  bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_pinned, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept {
1484
1654
  mi_memid_t memid = _mi_memid_create(MI_MEM_EXTERNAL);
@@ -1490,7 +1660,7 @@ bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is
1490
1660
  return mi_manage_os_memory_ex2(_mi_subproc(), start, size, numa_node, exclusive, memid, NULL, NULL, arena_id);
1491
1661
  }
1492
1662
 
1493
- bool mi_manage_memory(void* start, size_t size, bool is_committed, bool is_zero, bool is_pinned, int numa_node, bool exclusive, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept
1663
+ bool mi_manage_memory(void* start, size_t size, bool is_committed, bool is_pinned, bool is_zero, int numa_node, bool exclusive, mi_commit_fun_t* commit_fun, void* commit_fun_arg, mi_arena_id_t* arena_id) mi_attr_noexcept
1494
1664
  {
1495
1665
  mi_memid_t memid = _mi_memid_create(MI_MEM_EXTERNAL);
1496
1666
  memid.mem.os.base = start;
@@ -1543,7 +1713,7 @@ int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noe
1543
1713
  // Return idx of the slice past the last used slice
1544
1714
  static size_t mi_arena_used_slices(mi_arena_t* arena) {
1545
1715
  size_t idx;
1546
- if (mi_bitmap_bsr(arena->slices_dirty, &idx)) {
1716
+ if (mi_bbitmap_bsr_inv(arena->slices_free, &idx)) {
1547
1717
  return (idx + 1);
1548
1718
  }
1549
1719
  else {
@@ -1602,7 +1772,7 @@ static size_t mi_debug_show_page_bfield(char* buf, size_t* k, mi_arena_t* arena,
1602
1772
  void* start = mi_arena_slice_start(arena, slice_index + bit);
1603
1773
  mi_page_t* page = _mi_safe_ptr_page(start);
1604
1774
  char c = ' ';
1605
- if (start==page) {
1775
+ if (page!=NULL && start==mi_page_slice_start(page)) {
1606
1776
  mi_assert_internal(bit_of_page <= 0);
1607
1777
  bit_set_count++;
1608
1778
  c = 'p';
@@ -1625,7 +1795,7 @@ static size_t mi_debug_show_page_bfield(char* buf, size_t* k, mi_arena_t* arena,
1625
1795
  // else if (mi_bitmap_is_setN(arena->pages_purge, slice_index + bit, NULL)) { c = '*'; }
1626
1796
  else if (mi_bbitmap_is_setN(arena->slices_free, slice_index+bit,1)) {
1627
1797
  if (mi_bitmap_is_set(arena->slices_purge, slice_index + bit)) { c = '~'; color = MI_ORANGE; }
1628
- else if (mi_bitmap_is_setN(arena->slices_committed, slice_index + bit, 1)) { c = '_'; color = MI_GRAY; }
1798
+ else if (mi_bitmap_is_set(arena->slices_committed, slice_index + bit)) { c = '_'; color = MI_GRAY; }
1629
1799
  else { c = '.'; color = MI_GRAY; }
1630
1800
  }
1631
1801
  if (bit==MI_BFIELD_BITS-1 && bit_of_page > 1) { c = '>'; }
@@ -1731,7 +1901,10 @@ static void mi_debug_show_arenas_ex(mi_heap_t* heap, bool show_pages, bool narro
1731
1901
  if (arena == NULL) break;
1732
1902
  mi_assert(arena->subproc == subproc);
1733
1903
  // slice_total += arena->slice_count;
1734
- _mi_raw_message("arena %zu at %p: %zu slices (%zu MiB)%s, subproc: %p\n", i, arena, arena->slice_count, (size_t)(mi_size_of_slices(arena->slice_count)/MI_MiB), (arena->memid.is_pinned ? ", pinned" : ""), arena->subproc);
1904
+ _mi_raw_message("%sarena %zu at %p: %zu slices (%zu MiB)%s%s, subproc: %p, numa: %i\n",
1905
+ (arena->parent==NULL ? "" : "(sub)"), i, arena, arena->slice_count, (size_t)(mi_size_of_slices(arena->slice_count)/MI_MiB),
1906
+ (arena->memid.is_pinned ? ", pinned" : ""), (arena->is_exclusive ? ", exclusive" : ""),
1907
+ arena->subproc, arena->numa_node);
1735
1908
  //if (show_inuse) {
1736
1909
  // free_total += mi_debug_show_bbitmap("in-use slices", arena->slice_count, arena->slices_free, true, NULL);
1737
1910
  //}
@@ -1953,15 +2126,21 @@ static bool mi_arena_try_purge_visitor(size_t slice_index, size_t slice_count, m
1953
2126
  return true; // continue
1954
2127
  }
1955
2128
 
1956
- // returns true if anything was purged
1957
- static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
2129
+ // returns
2130
+ // -1 = nothing was purged
2131
+ // 0 = nothing was purged yet because have not yet reached the expire time
2132
+ // 1 = some pages in the arena were purged
2133
+ static int mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
1958
2134
  {
1959
2135
  // check pre-conditions
1960
- if (arena->memid.is_pinned) return false;
2136
+ if (arena->memid.is_pinned) return -1;
1961
2137
 
1962
2138
  // expired yet?
1963
2139
  mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire);
1964
- if (!force && (expire == 0 || expire > now)) return false;
2140
+ if (!force) {
2141
+ if (expire==0) return -1;
2142
+ if (expire > now) return 0;
2143
+ }
1965
2144
 
1966
2145
  // reset expire
1967
2146
  mi_atomic_storei64_release(&arena->purge_expire, (mi_msecs_t)0);
@@ -1976,7 +2155,7 @@ static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
1976
2155
  const size_t minslices = mi_slice_count_of_size(_mi_os_minimal_purge_size());
1977
2156
  _mi_bitmap_forall_setc_rangesn(arena->slices_purge, minslices, &mi_arena_try_purge_visitor, arena, &vinfo);
1978
2157
 
1979
- return vinfo.any_purged;
2158
+ return (vinfo.any_purged ? 1 : -1);
1980
2159
  }
1981
2160
 
1982
2161
 
@@ -2009,18 +2188,21 @@ static void mi_arenas_try_purge(bool force, bool visit_all, mi_subproc_t* subpro
2009
2188
  if (i >= max_arena) { i -= max_arena; }
2010
2189
  mi_arena_t* arena = mi_arena_from_index(subproc,i);
2011
2190
  if (arena != NULL) {
2012
- if (mi_arena_try_purge(arena, now, force)) {
2191
+ const int purged = mi_arena_try_purge(arena, now, force);
2192
+ if (purged >= 0) { // purged, or arena expire is not yet reached
2013
2193
  any_purged = true;
2014
- if (max_purge_count <= 1) {
2015
- all_visited = false;
2016
- break;
2194
+ if (purged >= 1) { // purged
2195
+ if (max_purge_count <= 1) {
2196
+ all_visited = false;
2197
+ break;
2198
+ }
2199
+ max_purge_count--;
2017
2200
  }
2018
- max_purge_count--;
2019
2201
  }
2020
2202
  }
2021
2203
  }
2022
2204
  if (all_visited && !any_purged) {
2023
- mi_atomic_storei64_release(&subproc->purge_expire, 0);
2205
+ mi_atomic_storei64_release(&subproc->purge_expire, (mi_msecs_t)0);
2024
2206
  }
2025
2207
  }
2026
2208
  }
@@ -2055,7 +2237,7 @@ static bool mi_heap_visit_page(mi_page_t* page, mi_heap_visit_info_t* vinfo) {
2055
2237
  static bool mi_heap_visit_page_at(size_t slice_index, size_t slice_count, mi_arena_t* arena, void* arg) {
2056
2238
  MI_UNUSED(slice_count);
2057
2239
  mi_heap_visit_info_t* vinfo = (mi_heap_visit_info_t*)arg;
2058
- mi_page_t* page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
2240
+ mi_page_t* page = mi_arena_page_at_slice(arena, slice_index);
2059
2241
  return mi_heap_visit_page(page, vinfo);
2060
2242
  }
2061
2243
 
@@ -2121,7 +2303,7 @@ static bool mi_heap_delete_page(const mi_heap_t* heap, const mi_heap_area_t* are
2121
2303
  MI_UNUSED(block); MI_UNUSED(block_size); MI_UNUSED(heap);
2122
2304
  mi_heap_delete_visit_info_t* info = (mi_heap_delete_visit_info_t*)arg;
2123
2305
  mi_heap_t* heap_target = info->heap_target;
2124
- mi_theap_t* const theap = info->theap; mi_assert_internal(theap->heap == heap);
2306
+ mi_theap_t* const theap = NULL; // info->theap; mi_assert_internal(_mi_theap_heap(theap) == heap);
2125
2307
  mi_page_t* const page = (mi_page_t*)area->reserved1;
2126
2308
 
2127
2309
  mi_page_claim_ownership(page); // claim ownership
@@ -2154,8 +2336,14 @@ static bool mi_heap_delete_page(const mi_heap_t* heap, const mi_heap_area_t* are
2154
2336
  mi_arena_t* const arena = mi_page_arena_pages(page, &slice_index, &slice_count, &arena_pages);
2155
2337
  mi_assert_internal(mi_bitmap_is_set(arena_pages->pages, slice_index));
2156
2338
  mi_bitmap_clear(arena_pages->pages, slice_index);
2157
- mi_theap_stat_decrease(theap, page_bins[sbin], 1);
2158
- mi_theap_stat_decrease(theap, pages, 1);
2339
+ if (theap != NULL) {
2340
+ mi_theap_stat_decrease(theap, page_bins[sbin], 1);
2341
+ mi_theap_stat_decrease(theap, pages, 1);
2342
+ }
2343
+ else {
2344
+ mi_heap_stat_decrease((mi_heap_t*)heap, page_bins[_mi_page_stats_bin(page)], 1);
2345
+ mi_heap_stat_decrease((mi_heap_t*)heap, pages, 1);
2346
+ }
2159
2347
  mi_theap_t* theap_target = info->theap_target;
2160
2348
 
2161
2349
  // and then add it to the new target heap
@@ -2181,13 +2369,13 @@ static bool mi_heap_delete_page(const mi_heap_t* heap, const mi_heap_area_t* are
2181
2369
 
2182
2370
  static void mi_heap_delete_pages(mi_heap_t* heap, mi_heap_t* heap_target) {
2183
2371
  mi_theap_t* const theap_target = (heap_target != NULL ? _mi_heap_theap(heap_target) : NULL);
2184
- mi_theap_t* const theap = _mi_heap_theap(heap);
2185
- mi_heap_delete_visit_info_t info = { heap_target, theap_target, theap };
2372
+ // mi_theap_t* const theap = _mi_heap_theap(heap);
2373
+ mi_heap_delete_visit_info_t info = { heap_target, theap_target, NULL };
2186
2374
  _mi_heap_visit_blocks(heap, false, false, &mi_heap_delete_page, &info);
2187
2375
  #if MI_DEBUG>1
2188
2376
  // no more arena pages?
2189
2377
  for (size_t i = 0; i < MI_ARENA_BIN_COUNT; i++) {
2190
- mi_arena_pages_t* const arena_pages = mi_atomic_load_relaxed(&heap->arena_pages[i]);
2378
+ mi_arena_pages_t* const arena_pages = mi_atomic_load_ptr_relaxed(mi_arena_pages_t, &heap->arena_pages[i]);
2191
2379
  if (arena_pages!=NULL) {
2192
2380
  mi_assert_internal(mi_bitmap_is_all_clear(arena_pages->pages));
2193
2381
  }
@@ -2222,7 +2410,7 @@ void _mi_heap_destroy_pages(mi_heap_t* heap_from) {
2222
2410
  static bool mi_arena_page_register(size_t slice_index, size_t slice_count, mi_arena_t* arena, void* arg) {
2223
2411
  MI_UNUSED(arg); MI_UNUSED(slice_count);
2224
2412
  mi_assert_internal(slice_count == 1);
2225
- mi_page_t* page = (mi_page_t*)mi_arena_slice_start(arena, slice_index);
2413
+ mi_page_t* page = mi_arena_page_at_slice(arena, slice_index);
2226
2414
  mi_assert_internal(mi_bitmap_is_setN(page->memid.mem.arena.arena->pages, page->memid.mem.arena.slice_index, 1));
2227
2415
  if (!_mi_page_map_register(page)) return false; // break
2228
2416
  mi_assert_internal(_mi_ptr_page(page)==page);