@shd101wyy/yo 0.1.24 → 0.1.26

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 (104) hide show
  1. package/.github/skills/yo-async-effects/async-effects-recipes.md +6 -6
  2. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +34 -0
  3. package/.github/skills/yo-syntax/syntax-cheatsheet.md +441 -2
  4. package/out/cjs/index.cjs +563 -567
  5. package/out/cjs/yo-cli.cjs +656 -651
  6. package/out/cjs/yo-lsp.cjs +614 -618
  7. package/out/esm/index.mjs +582 -586
  8. package/out/types/src/codegen/codegen-c.d.ts +2 -2
  9. package/out/types/src/codegen/functions/collection.d.ts +2 -2
  10. package/out/types/src/codegen/functions/context.d.ts +3 -2
  11. package/out/types/src/codegen/types/collection.d.ts +2 -2
  12. package/out/types/src/codegen/utils/index.d.ts +4 -1
  13. package/out/types/src/doc/builder.d.ts +2 -2
  14. package/out/types/src/evaluator/calls/closure-type.d.ts +2 -2
  15. package/out/types/src/evaluator/calls/record-type.d.ts +11 -0
  16. package/out/types/src/evaluator/context.d.ts +8 -9
  17. package/out/types/src/evaluator/index.d.ts +3 -3
  18. package/out/types/src/evaluator/types/record.d.ts +14 -0
  19. package/out/types/src/evaluator/types/validation.d.ts +2 -2
  20. package/out/types/src/evaluator/values/anonymous-module.d.ts +5 -5
  21. package/out/types/src/evaluator/values/impl.d.ts +1 -1
  22. package/out/types/src/expr.d.ts +2 -4
  23. package/out/types/src/function-value.d.ts +1 -1
  24. package/out/types/src/lsp/document-manager.d.ts +1 -1
  25. package/out/types/src/module-manager.d.ts +3 -3
  26. package/out/types/src/test-runner.d.ts +1 -0
  27. package/out/types/src/types/creators.d.ts +3 -4
  28. package/out/types/src/types/definitions.d.ts +8 -19
  29. package/out/types/src/types/guards.d.ts +3 -3
  30. package/out/types/src/types/tags.d.ts +0 -1
  31. package/out/types/src/types/utils.d.ts +1 -1
  32. package/out/types/src/value-tag.d.ts +0 -1
  33. package/out/types/src/value.d.ts +6 -13
  34. package/out/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/std/error.yo +6 -6
  37. package/std/prelude.yo +1 -7
  38. package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
  39. package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
  40. package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
  41. package/vendor/mimalloc/CMakeLists.txt +52 -33
  42. package/vendor/mimalloc/azure-pipelines.yml +4 -3
  43. package/vendor/mimalloc/bin/bundle.bat +74 -0
  44. package/vendor/mimalloc/bin/bundle.sh +232 -0
  45. package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
  46. package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
  47. package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
  48. package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
  49. package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
  50. package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
  51. package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
  52. package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
  53. package/vendor/mimalloc/doc/release-notes.md +15 -0
  54. package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
  55. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
  56. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
  57. package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
  58. package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
  59. package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
  60. package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
  61. package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
  62. package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
  63. package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
  64. package/vendor/mimalloc/include/mimalloc/track.h +7 -2
  65. package/vendor/mimalloc/include/mimalloc/types.h +57 -29
  66. package/vendor/mimalloc/include/mimalloc-override.h +10 -10
  67. package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
  68. package/vendor/mimalloc/include/mimalloc.h +22 -12
  69. package/vendor/mimalloc/readme.md +42 -17
  70. package/vendor/mimalloc/src/alloc-aligned.c +13 -11
  71. package/vendor/mimalloc/src/alloc-override.c +97 -17
  72. package/vendor/mimalloc/src/alloc-posix.c +44 -27
  73. package/vendor/mimalloc/src/alloc.c +73 -23
  74. package/vendor/mimalloc/src/arena-meta.c +3 -3
  75. package/vendor/mimalloc/src/arena.c +380 -192
  76. package/vendor/mimalloc/src/bitmap.c +68 -18
  77. package/vendor/mimalloc/src/bitmap.h +8 -4
  78. package/vendor/mimalloc/src/free.c +83 -47
  79. package/vendor/mimalloc/src/heap.c +94 -40
  80. package/vendor/mimalloc/src/init.c +273 -102
  81. package/vendor/mimalloc/src/libc.c +53 -8
  82. package/vendor/mimalloc/src/options.c +43 -40
  83. package/vendor/mimalloc/src/os.c +110 -45
  84. package/vendor/mimalloc/src/page-map.c +14 -8
  85. package/vendor/mimalloc/src/page-queue.c +9 -6
  86. package/vendor/mimalloc/src/page.c +26 -16
  87. package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
  88. package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
  89. package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
  90. package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
  91. package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
  92. package/vendor/mimalloc/src/random.c +8 -3
  93. package/vendor/mimalloc/src/stats.c +59 -48
  94. package/vendor/mimalloc/src/theap.c +85 -44
  95. package/vendor/mimalloc/src/threadlocal.c +102 -41
  96. package/vendor/mimalloc/test/main-override-static.c +31 -2
  97. package/vendor/mimalloc/test/main-override.c +27 -14
  98. package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
  99. package/vendor/mimalloc/test/main-static-dep.h +11 -0
  100. package/vendor/mimalloc/test/test-api-fill.c +2 -2
  101. package/vendor/mimalloc/test/test-stress.c +3 -3
  102. package/vendor/mimalloc/test/test-wrong.c +11 -7
  103. package/out/types/src/evaluator/calls/module-type.d.ts +0 -11
  104. package/out/types/src/evaluator/types/module.d.ts +0 -19
@@ -67,17 +67,11 @@ const mi_page_t _mi_page_empty = {
67
67
  #define MI_STAT_COUNT_NULL() {0,0,0}
68
68
 
69
69
  // Empty statistics
70
+ #define MI_STAT_COUNT(stat) {0,0,0},
71
+ #define MI_STAT_COUNTER(stat) {0},
72
+
70
73
  #define MI_STATS_NULL \
71
- MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
72
- { 0 }, { 0 }, \
73
- MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
74
- MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
75
- { 0 }, { 0 }, { 0 }, { 0 }, \
76
- { 0 }, { 0 }, { 0 }, { 0 }, \
77
- \
78
- { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, \
79
- MI_INIT5(MI_STAT_COUNT_NULL), \
80
- { 0 }, { 0 }, { 0 }, { 0 }, \
74
+ MI_STAT_FIELDS() \
81
75
  \
82
76
  { MI_INIT4(MI_STAT_COUNT_NULL) }, \
83
77
  { { 0 }, { 0 }, { 0 }, { 0 } }, \
@@ -97,9 +91,9 @@ const mi_page_t _mi_page_empty = {
97
91
 
98
92
  static mi_decl_cache_align mi_subproc_t subproc_main
99
93
  #if __cplusplus
100
- = { }; // empty initializer to prevent running the constructor (with msvc)
94
+ = { }; // empty initializer to prevent running the constructor (with msvc)
101
95
  #else
102
- = { 0 }; // C zero initialize
96
+ = { 0 }; // C zero initialize
103
97
  #endif
104
98
 
105
99
  static mi_subproc_t* subprocs = &subproc_main;
@@ -111,6 +105,7 @@ static mi_decl_cache_align mi_tld_t tld_empty = {
111
105
  0, // default numa node
112
106
  &subproc_main, // subproc
113
107
  NULL, // theaps list
108
+ MI_LOCK_INITIALIZER, // theaps lock
114
109
  false, // recurse
115
110
  false, // is_in_threadpool
116
111
  MI_MEMID_STATIC // memid
@@ -118,12 +113,14 @@ static mi_decl_cache_align mi_tld_t tld_empty = {
118
113
 
119
114
  mi_decl_cache_align const mi_theap_t _mi_theap_empty = {
120
115
  &tld_empty, // tld
121
- NULL, // heap
116
+ MI_ATOMIC_VAR_INIT(NULL), // heap
117
+ MI_ATOMIC_VAR_INIT(1), // refcount
122
118
  0, // heartbeat
123
119
  0, // cookie
124
120
  { {0}, {0}, 0, true }, // random
125
121
  0, // page count
126
122
  MI_BIN_FULL, 0, // page retired min/max
123
+ 0, // pages_full_size
127
124
  0, 0, // generic count
128
125
  NULL, NULL, // tnext, tprev
129
126
  NULL, NULL, // hnext, hprev
@@ -141,12 +138,14 @@ mi_decl_cache_align const mi_theap_t _mi_theap_empty = {
141
138
 
142
139
  mi_decl_cache_align const mi_theap_t _mi_theap_empty_wrong = {
143
140
  &tld_empty, // tld
144
- NULL, // heap
141
+ MI_ATOMIC_VAR_INIT(NULL), // heap
142
+ MI_ATOMIC_VAR_INIT(1), // refcount
145
143
  0, // heartbeat
146
144
  0, // cookie
147
145
  { {0}, {0}, 0, true }, // random
148
146
  0, // page count
149
147
  MI_BIN_FULL, 0, // page retired min/max
148
+ 0, // pages_full_size
150
149
  0, 0, // generic count
151
150
  NULL, NULL, // tnext, tprev
152
151
  NULL, NULL, // hnext, hprev
@@ -173,6 +172,7 @@ static mi_decl_cache_align mi_tld_t tld_main = {
173
172
  0, // numa node
174
173
  &subproc_main, // subproc
175
174
  &theap_main, // theaps list
175
+ MI_LOCK_INITIALIZER, // theaps lock
176
176
  false, // recurse
177
177
  false, // is_in_threadpool
178
178
  MI_MEMID_STATIC // memid
@@ -180,12 +180,14 @@ static mi_decl_cache_align mi_tld_t tld_main = {
180
180
 
181
181
  mi_decl_cache_align mi_theap_t theap_main = {
182
182
  &tld_main, // thread local data
183
- &heap_main, // main heap
183
+ MI_ATOMIC_VAR_INIT(&heap_main), // main heap
184
+ MI_ATOMIC_VAR_INIT(1), // refcount
184
185
  0, // heartbeat
185
186
  0, // initial cookie
186
187
  { {0x846ca68b}, {0}, 0, true }, // random
187
188
  0, // page count
188
189
  MI_BIN_FULL, 0, // page retired min/max
190
+ 0, // pages_full_size
189
191
  0, 0, // generic count
190
192
  NULL, NULL, // tnext, tprev
191
193
  NULL, NULL, // hnext, hprev
@@ -208,13 +210,15 @@ mi_decl_cache_align mi_heap_t heap_main
208
210
  = { 0 }; // C zero initialize
209
211
  #endif
210
212
 
211
- mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
212
- return _mi_prim_thread_id();
213
- }
214
-
215
213
  // the theap belonging to the main heap
216
214
  mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_main = NULL;
217
215
 
216
+ mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
217
+ mi_threadid_t tid = _mi_prim_thread_id();
218
+ mi_assert_internal( (tid & 0x03) == 0 ); // mimalloc reserves the bottom 2 bits
219
+ return tid;
220
+ }
221
+
218
222
  #if MI_TLS_MODEL_THREAD_LOCAL
219
223
  // the thread-local main theap for allocation
220
224
  mi_decl_hidden mi_decl_thread mi_theap_t* __mi_theap_default = (mi_theap_t*)&_mi_theap_empty;
@@ -226,6 +230,10 @@ bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`.
226
230
 
227
231
  mi_stats_t _mi_stats_main = { sizeof(mi_stats_t), MI_STAT_VERSION, MI_STATS_NULL };
228
232
 
233
+ #undef MI_STAT_COUNT
234
+ #undef MI_STAT_COUNTER
235
+
236
+
229
237
  #if MI_GUARDED
230
238
  mi_decl_export void mi_theap_guarded_set_sample_rate(mi_theap_t* theap, size_t sample_rate, size_t seed) {
231
239
  theap->guarded_sample_rate = sample_rate;
@@ -280,9 +288,11 @@ static void mi_subproc_main_init(void) {
280
288
  subproc_main.heap_count = 1;
281
289
  mi_atomic_store_ptr_release(mi_heap_t, &subproc_main.heap_main, &heap_main);
282
290
  __mi_stat_increase_mt(&subproc_main.stats.heaps, 1);
291
+ mi_stats_header_init(&subproc_main.stats);
283
292
  mi_lock_init(&subproc_main.arena_reserve_lock);
284
293
  mi_lock_init(&subproc_main.heaps_lock);
285
294
  mi_lock_init(&subprocs_lock);
295
+ mi_lock_init(&tld_empty.theaps_lock);
286
296
  }
287
297
  }
288
298
 
@@ -290,6 +300,7 @@ static void mi_subproc_main_init(void) {
290
300
  static void mi_tld_main_init(void) {
291
301
  if (tld_main.thread_id == 0) {
292
302
  tld_main.thread_id = _mi_prim_thread_id();
303
+ mi_lock_init(&tld_main.theaps_lock);
293
304
  }
294
305
  }
295
306
 
@@ -354,6 +365,7 @@ static mi_tld_t* mi_tld_alloc(void) {
354
365
  }
355
366
  tld->memid = memid;
356
367
  tld->theaps = NULL;
368
+ mi_lock_init(&tld->theaps_lock);
357
369
  tld->subproc = &subproc_main;
358
370
  tld->numa_node = _mi_os_numa_node();
359
371
  tld->thread_id = _mi_prim_thread_id();
@@ -367,6 +379,7 @@ static mi_tld_t* mi_tld_alloc(void) {
367
379
  #define MI_TLD_INVALID ((mi_tld_t*)1)
368
380
 
369
381
  mi_decl_noinline static void mi_tld_free(mi_tld_t* tld) {
382
+ mi_lock_done(&tld->theaps_lock);
370
383
  if (tld != NULL && tld != MI_TLD_INVALID) {
371
384
  mi_atomic_decrement_relaxed(&tld->subproc->thread_count);
372
385
  _mi_meta_free(tld, sizeof(mi_tld_t), tld->memid);
@@ -388,6 +401,22 @@ mi_theap_t* _mi_theap_default_safe(void) {
388
401
  return _mi_theap_default();
389
402
  }
390
403
 
404
+ // return the main theap ensuring it is initialized.
405
+ mi_theap_t* _mi_theap_main_safe(void) {
406
+ mi_theap_t* theap = __mi_theap_main;
407
+ if mi_unlikely(theap==NULL) { // if thread_init or default_set was never called
408
+ mi_thread_init(); // sets the default slot to the main theap
409
+ theap = _mi_theap_default();
410
+ mi_assert_internal(theap!=NULL);
411
+ mi_assert_internal(_mi_is_theap_main(theap));
412
+ if (_mi_is_theap_main(theap)) {
413
+ __mi_theap_main = theap;
414
+ }
415
+ }
416
+ mi_assert_internal(theap!=NULL && _mi_is_theap_main(theap));
417
+ return theap;
418
+ }
419
+
391
420
 
392
421
  mi_subproc_t* _mi_subproc_main(void) {
393
422
  return &subproc_main;
@@ -420,7 +449,7 @@ mi_heap_t* _mi_subproc_heap_main(mi_subproc_t* subproc) {
420
449
  }
421
450
 
422
451
  mi_heap_t* mi_heap_main(void) {
423
- return _mi_subproc_heap_main(_mi_subproc()); // don't use _mi_theap_main() so this call works during process_init
452
+ return _mi_subproc_heap_main(_mi_subproc()); // don't use mi_theap_main_init_get() so this call works during process_init
424
453
  }
425
454
 
426
455
  bool _mi_is_heap_main(const mi_heap_t* heap) {
@@ -428,25 +457,40 @@ bool _mi_is_heap_main(const mi_heap_t* heap) {
428
457
  return (_mi_subproc_heap_main(heap->subproc) == heap);
429
458
  }
430
459
 
460
+ bool _mi_is_theap_main(const mi_theap_t* theap) {
461
+ return (mi_theap_is_initialized(theap) && _mi_is_heap_main(_mi_theap_heap(theap)));
462
+ }
463
+
431
464
  /* -----------------------------------------------------------
432
465
  Sub process
433
466
  ----------------------------------------------------------- */
434
467
 
468
+
469
+ mi_subproc_t* _mi_subproc_from_id(mi_subproc_id_t subproc_id) {
470
+ return (mi_subproc_t*)(subproc_id._mi_subproc_id);
471
+ }
472
+
473
+ mi_subproc_id_t _mi_subproc_to_id(mi_subproc_t* subproc) {
474
+ mi_subproc_id_t id = { subproc };
475
+ return id;
476
+ }
477
+
435
478
  mi_subproc_id_t mi_subproc_main(void) {
436
- return _mi_subproc_main();
479
+ return _mi_subproc_to_id(_mi_subproc_main());
437
480
  }
438
481
 
439
482
  mi_subproc_id_t mi_subproc_current(void) {
440
- return _mi_subproc();
483
+ return _mi_subproc_to_id(_mi_subproc());
441
484
  }
442
485
 
443
486
  mi_subproc_id_t mi_subproc_new(void) {
444
487
  static _Atomic(size_t) subproc_total_count;
445
488
  mi_memid_t memid;
446
489
  mi_subproc_t* subproc = (mi_subproc_t*)_mi_meta_zalloc(sizeof(mi_subproc_t),&memid);
447
- if (subproc == NULL) return NULL;
490
+ if (subproc == NULL) return _mi_subproc_to_id(NULL);
448
491
  subproc->memid = memid;
449
492
  subproc->subproc_seq = mi_atomic_increment_relaxed(&subproc_total_count) + 1;
493
+ mi_stats_header_init(&subproc->stats);
450
494
  mi_lock_init(&subproc->arena_reserve_lock);
451
495
  mi_lock_init(&subproc->heaps_lock);
452
496
  mi_lock(&subprocs_lock) {
@@ -455,18 +499,16 @@ mi_subproc_id_t mi_subproc_new(void) {
455
499
  if (subprocs!=NULL) { subprocs->prev = subproc; }
456
500
  subprocs = subproc;
457
501
  }
458
- return subproc;
459
- }
460
-
461
- mi_subproc_t* _mi_subproc_from_id(mi_subproc_id_t subproc_id) {
462
- return (subproc_id == NULL ? &subproc_main : (mi_subproc_t*)subproc_id);
502
+ return _mi_subproc_to_id(subproc);
463
503
  }
464
504
 
465
505
  // destroy all subproc resources including arena's, heap's etc.
466
- static void mi_subproc_unsafe_destroy(mi_subproc_t* subproc)
506
+ static void mi_subproc_unsafe_destroy(mi_subproc_t* subproc, bool acquire_subprocs_lock)
467
507
  {
508
+ if (subproc==NULL) return;
509
+
468
510
  // remove from the subproc list
469
- mi_lock(&subprocs_lock) {
511
+ mi_lock_maybe(&subprocs_lock, acquire_subprocs_lock) {
470
512
  if (subproc->next!=NULL) { subproc->next->prev = subproc->prev; }
471
513
  if (subproc->prev!=NULL) { subproc->prev->next = subproc->next; }
472
514
  else { mi_assert_internal(subprocs==subproc); subprocs = subproc->next; }
@@ -477,29 +519,38 @@ static void mi_subproc_unsafe_destroy(mi_subproc_t* subproc)
477
519
  mi_heap_t* heap = subproc->heaps;
478
520
  while (heap != NULL) {
479
521
  mi_heap_t* next = heap->next;
480
- if (heap!=subproc->heap_main) {mi_heap_destroy(heap); }
522
+ if (heap!=subproc->heap_main) { mi_heap_destroy(heap); }
481
523
  heap = next;
482
524
  }
483
525
  mi_assert_internal(subproc->heaps == subproc->heap_main);
484
- mi_heap_destroy(subproc->heap_main);
526
+ _mi_heap_force_destroy(subproc->heap_main); // no warning if destroying the main heap
485
527
  }
486
528
 
529
+ // remove associated arenas
530
+ _mi_arenas_unsafe_destroy_all(subproc);
531
+
487
532
  // merge stats back into the main subproc?
488
533
  if (subproc!=&subproc_main) {
489
- _mi_arenas_unsafe_destroy_all(subproc);
490
534
  _mi_stats_merge_into(&subproc_main.stats, &subproc->stats);
535
+ }
491
536
 
492
- // safe to release
493
- // todo: should we refcount subprocesses?
494
- mi_lock_done(&subproc->arena_reserve_lock);
495
- mi_lock_done(&subproc->heaps_lock);
537
+ // safe to release
538
+ // todo: should we refcount subprocesses?
539
+ mi_lock_done(&subproc->arena_reserve_lock);
540
+ mi_lock_done(&subproc->heaps_lock);
541
+ if (subproc!=&subproc_main) {
496
542
  _mi_meta_free(subproc, sizeof(mi_subproc_t), subproc->memid);
497
543
  }
544
+ else {
545
+ // for the main subproc, also release the global page map
546
+ _mi_page_map_unsafe_destroy(&subproc_main);
547
+ }
498
548
  }
499
549
 
500
550
  void mi_subproc_destroy(mi_subproc_id_t subproc_id) {
501
- if (subproc_id == NULL) return;
502
- mi_subproc_unsafe_destroy(_mi_subproc_from_id(subproc_id));
551
+ mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id);
552
+ if (subproc==NULL || subproc==&subproc_main) return;
553
+ mi_subproc_unsafe_destroy(subproc, true /* take lock */);
503
554
  }
504
555
 
505
556
  static void mi_subprocs_unsafe_destroy_all(void) {
@@ -508,12 +559,12 @@ static void mi_subprocs_unsafe_destroy_all(void) {
508
559
  while (subproc!=NULL) {
509
560
  mi_subproc_t* next = subproc->next;
510
561
  if (subproc!=&subproc_main) {
511
- mi_subproc_unsafe_destroy(subproc);
562
+ mi_subproc_unsafe_destroy(subproc, false /* take subprocs lock */);
512
563
  }
513
564
  subproc = next;
514
565
  }
515
- }
516
- mi_subproc_unsafe_destroy(&subproc_main);
566
+ }
567
+ mi_subproc_unsafe_destroy(&subproc_main, true /* take subprocs lock */);
517
568
  }
518
569
 
519
570
 
@@ -562,6 +613,7 @@ static mi_theap_t* _mi_thread_init_theap_default(void) {
562
613
  // note: we cannot access thread-locals yet as that can cause (recursive) allocation
563
614
  // (on macOS <= 14 for example where the loader allocates thread-local data on demand).
564
615
  mi_tld_t* tld = mi_tld_alloc();
616
+ if (tld==NULL) return NULL; // things are very wrong if this fails (out of memory)
565
617
  // allocate and initialize the theap for the main heap
566
618
  theap = _mi_theap_create(mi_heap_main(), tld);
567
619
  }
@@ -576,19 +628,50 @@ static mi_theap_t* _mi_thread_init_theap_default(void) {
576
628
  static void mi_thread_theaps_done(mi_tld_t* tld)
577
629
  {
578
630
  // reset the thread local theaps
579
- __mi_theap_main = NULL;
580
631
  _mi_theap_default_set((mi_theap_t*)&_mi_theap_empty);
581
632
  _mi_theap_cached_set((mi_theap_t*)&_mi_theap_empty);
582
-
583
- // delete all theaps in this thread
584
- mi_theap_t* curr = tld->theaps;
585
- while (curr != NULL) {
586
- mi_theap_t* next = curr->tnext; // save `tnext` as `curr` will be freed
587
- // never destroy theaps; if a dll is linked statically with mimalloc,
588
- // there may still be delete/free calls after the mi_fls_done is called. Issue #207
589
- _mi_theap_delete(curr);
590
- curr = next;
633
+ __mi_theap_main = NULL;
634
+
635
+ // abandon the pages of all theaps in this thread
636
+ mi_lock(&tld->theaps_lock) {
637
+ mi_theap_t* theap = tld->theaps;
638
+ while (theap != NULL) {
639
+ mi_theap_t* next = theap->tnext;
640
+ // never destroy theaps; if a dll is linked statically with mimalloc,
641
+ // there may still be delete/free calls after the mi_fls_done is called. Issue #207
642
+ _mi_theap_collect_abandon(theap);
643
+ mi_assert_internal(theap->page_count==0);
644
+ theap = next;
645
+ }
591
646
  }
647
+
648
+ // free the theaps of this thread.
649
+ // This can run concurrently with a `mi_heap_free_theaps` and we need to ensure we free theaps atomically.
650
+ // We do this in a loop where we release the theaps_lock at every potential re-iteration to unblock
651
+ // potential concurrent `mi_heap_free_theaps` which tries to remove the theap from our theaps list.
652
+ bool all_freed;
653
+ do {
654
+ all_freed = true;
655
+ mi_lock(&tld->theaps_lock) {
656
+ mi_theap_t* theap = tld->theaps;
657
+ while (theap != NULL) {
658
+ mi_theap_t* next = theap->tnext;
659
+ mi_assert_internal(theap->page_count==0);
660
+ if (!_mi_theap_free(theap, true /* acquire heap->theaps_lock */, false /* dont re-acquire the tld->theaps_lock*/ )) {
661
+ all_freed = false;
662
+ }
663
+ theap = next;
664
+ }
665
+ }
666
+ if (!all_freed) {
667
+ mi_subproc_stat_counter_increase(tld->subproc,heaps_delete_wait,1);
668
+ _mi_prim_thread_yield();
669
+ }
670
+ else {
671
+ mi_assert_internal(tld->theaps==NULL);
672
+ }
673
+ } while (!all_freed);
674
+
592
675
  mi_assert(_mi_theap_default()==(mi_theap_t*)&_mi_theap_empty); // careful to not re-initialize the default theap during theap_delete
593
676
  mi_assert(!mi_theap_is_initialized(_mi_theap_default()));
594
677
  }
@@ -613,11 +696,10 @@ static void mi_thread_theaps_done(mi_tld_t* tld)
613
696
 
614
697
  // Set up handlers so `mi_thread_done` is called automatically
615
698
  static void mi_process_setup_auto_thread_done(void) {
616
- static bool tls_initialized = false; // fine if it races
617
- if (tls_initialized) return;
618
- tls_initialized = true;
619
- _mi_prim_thread_init_auto_done();
620
- _mi_theap_default_set(&theap_main);
699
+ mi_atomic_do_once {
700
+ _mi_prim_thread_init_auto_done();
701
+ _mi_theap_default_set(&theap_main);
702
+ }
621
703
  }
622
704
 
623
705
 
@@ -635,7 +717,7 @@ void mi_thread_init(void) mi_attr_noexcept
635
717
  if (_mi_thread_is_initialized()) return;
636
718
 
637
719
  // initialize the default theap
638
- _mi_thread_init_theap_default();
720
+ if (_mi_thread_init_theap_default() == NULL) return; // out-of-memory on tld/theap allocation
639
721
 
640
722
  mi_heap_stat_increase(mi_heap_main(), threads, 1);
641
723
  // _mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id());
@@ -649,7 +731,11 @@ void _mi_thread_done(mi_theap_t* _theap_main)
649
731
  {
650
732
  // NULL can be passed on some platforms
651
733
  if (_theap_main==NULL) {
652
- _theap_main = __mi_theap_main;
734
+ _theap_main = __mi_theap_main; // don't call `mi_theap_main_safe` as that re-initializes the thread
735
+ if (_theap_main==NULL) { // can happen if `mi_theap_main_safe` is never called; but then the default is main
736
+ _theap_main = _mi_theap_default();
737
+ mi_assert_internal(_theap_main==NULL || _mi_is_theap_main(_theap_main));
738
+ }
653
739
  }
654
740
 
655
741
  // prevent re-entrancy through theap_done/theap_set_default_direct (issue #699)
@@ -699,8 +785,8 @@ mi_decl_cold mi_decl_noinline mi_theap_t* _mi_theap_empty_get(void) {
699
785
  #else
700
786
  // with only direct entries, use the "arbitrary user data" field
701
787
  // and assume it is NULL (see also <http://www.nynaeve.net/?p=98>)
702
- #define MI_TLS_INITIAL_EXPANSION_SLOT (0)
703
788
  #define MI_TLS_INITIAL_SLOT (5)
789
+ #define MI_TLS_INITIAL_EXPANSION_SLOT (0)
704
790
  #endif
705
791
 
706
792
  // we initially use the last of the expansion slots as the default NULL.
@@ -710,38 +796,62 @@ mi_decl_hidden size_t _mi_theap_default_expansion_slot = MI_TLS_INITIAL_EXPANSIO
710
796
  mi_decl_hidden size_t _mi_theap_cached_slot = MI_TLS_INITIAL_SLOT;
711
797
  mi_decl_hidden size_t _mi_theap_cached_expansion_slot = MI_TLS_INITIAL_EXPANSION_SLOT;
712
798
 
713
- static size_t mi_win_tls_slot_alloc(size_t* extended) {
714
- const DWORD slot = TlsAlloc();
715
- if (slot==TLS_OUT_OF_INDEXES || slot >= MI_TLS_DIRECT_SLOTS + MI_TLS_EXPANSION_SLOTS - 1) {
716
- // note: we also fail if the program already allocated the maximum number of expansion slots (as we use the last one as the default)
799
+ static DWORD mi_tls_raw_index_default = TLS_OUT_OF_INDEXES;
800
+ static DWORD mi_tls_raw_index_cached = TLS_OUT_OF_INDEXES;
801
+
802
+ static bool mi_win_tls_slot_alloc(size_t* slot, size_t* extended, DWORD* raw_index) {
803
+ const DWORD index = TlsAlloc();
804
+ *raw_index = index;
805
+ if (index==TLS_OUT_OF_INDEXES) {
717
806
  *extended = 0;
718
- return 0;
807
+ *slot = 0;
808
+ return false;
719
809
  }
720
- else if (slot<MI_TLS_DIRECT_SLOTS) {
810
+ else if (index<MI_TLS_DIRECT_SLOTS) {
721
811
  *extended = 0;
722
- return (slot + MI_TLS_DIRECT_FIRST);
812
+ *slot = index + MI_TLS_DIRECT_FIRST;
813
+ return true;
814
+ }
815
+ #if !MI_WIN_DIRECT_TLS
816
+ else if (index < MI_TLS_DIRECT_SLOTS + MI_TLS_EXPANSION_SLOTS - 1) { // check maximum number of expansion slots - 1 (as we use the last one as the default)
817
+ *extended = index - MI_TLS_DIRECT_SLOTS;
818
+ *slot = MI_TLS_EXPANSION_SLOT;
819
+ return true;
723
820
  }
821
+ #endif
724
822
  else {
725
- #if MI_WIN_DIRECT_TLS
823
+ // to high an index for us
824
+ _mi_error_message(EFAULT, "returned tls index was too high (%u)\n", index);
825
+ TlsFree(index);
826
+ *raw_index = TLS_OUT_OF_INDEXES;
726
827
  *extended = 0;
727
- return 0;
728
- #else
729
- *extended = (slot - MI_TLS_DIRECT_SLOTS);
730
- return MI_TLS_EXPANSION_SLOT;
731
- #endif
828
+ *slot = 0;
829
+ return false;
830
+ }
831
+ }
832
+
833
+ static void mi_win_tls_slot_free(DWORD* raw_index) {
834
+ if (*raw_index != TLS_OUT_OF_INDEXES) {
835
+ TlsFree(*raw_index);
836
+ *raw_index = TLS_OUT_OF_INDEXES;
732
837
  }
733
838
  }
734
839
 
735
- mi_decl_cold mi_theap_t* _mi_win_tls_slots_init(void) {
736
- static mi_atomic_once_t tls_slots_init;
737
- if (mi_atomic_once(&tls_slots_init)) {
738
- _mi_theap_default_slot = mi_win_tls_slot_alloc(&_mi_theap_default_expansion_slot);
739
- _mi_theap_cached_slot = mi_win_tls_slot_alloc(&_mi_theap_cached_expansion_slot);
740
- if (_mi_theap_cached_slot==0) {
840
+ static void mi_tls_slots_init(void) {
841
+ mi_atomic_do_once {
842
+ bool ok = mi_win_tls_slot_alloc(&_mi_theap_default_slot, &_mi_theap_default_expansion_slot, &mi_tls_raw_index_default);
843
+ if (ok) {
844
+ ok = mi_win_tls_slot_alloc(&_mi_theap_cached_slot, &_mi_theap_cached_expansion_slot, &mi_tls_raw_index_cached);
845
+ }
846
+ if (!ok) {
741
847
  _mi_error_message(EFAULT, "unable to allocate fast TLS user slot (0x%zx)\n", _mi_theap_cached_slot);
742
848
  }
743
849
  }
744
- return (mi_theap_t*)&_mi_theap_empty;
850
+ }
851
+
852
+ static void mi_tls_slots_done(void) {
853
+ mi_win_tls_slot_free(&mi_tls_raw_index_default);
854
+ mi_win_tls_slot_free(&mi_tls_raw_index_cached );
745
855
  }
746
856
 
747
857
  static void mi_win_tls_slot_set(size_t slot, size_t extended_slot, void* value) {
@@ -761,43 +871,90 @@ static void mi_win_tls_slot_set(size_t slot, size_t extended_slot, void* value)
761
871
  mi_decl_hidden pthread_key_t _mi_theap_default_key = 0;
762
872
  mi_decl_hidden pthread_key_t _mi_theap_cached_key = 0;
763
873
 
764
- mi_decl_cold mi_theap_t* _mi_tls_keys_init(void) {
765
- static mi_atomic_once_t tls_keys_init;
766
- if (mi_atomic_once(&tls_keys_init)) {
767
- pthread_key_create(&_mi_theap_default_key, NULL);
768
- pthread_key_create(&_mi_theap_cached_key, NULL);
874
+ // create a non-zero pthread key
875
+ static int mi_pthread_key_create( pthread_key_t* pkey ) {
876
+ pthread_key_t key;
877
+ int err = pthread_key_create(&key, NULL);
878
+ if (err!=0) return err;
879
+ if (key==0) {
880
+ // if we get a zero key, create another one as we use 0 for an invalid key
881
+ pthread_key_t key2;
882
+ err = pthread_key_create(&key2, NULL);
883
+ pthread_key_delete(key); // delete the old key
884
+ if (err!=0) return err;
885
+ key = key2;
886
+ }
887
+ mi_assert_internal(key!=0);
888
+ *pkey = key;
889
+ return 0;
890
+ }
891
+
892
+ static void mi_tls_slots_init(void) {
893
+ mi_atomic_do_once {
894
+ int err = mi_pthread_key_create(&_mi_theap_default_key);
895
+ if (err==0) {
896
+ err = mi_pthread_key_create(&_mi_theap_cached_key);
897
+ }
898
+ if (err!=0) {
899
+ _mi_error_message(EFAULT, "unable to allocate pthread keys (error %d)\n", err);
900
+ }
769
901
  }
770
- return (mi_theap_t*)&_mi_theap_empty;
902
+ }
903
+
904
+ static void mi_tls_slots_done(void) {
905
+ if (_mi_theap_default_key != 0) {
906
+ pthread_key_delete(_mi_theap_default_key);
907
+ _mi_theap_default_key = 0;
908
+ }
909
+ if (_mi_theap_cached_key != 0) {
910
+ pthread_key_delete(_mi_theap_cached_key);
911
+ _mi_theap_cached_key = 0;
912
+ }
913
+ }
914
+
915
+ #else
916
+
917
+ static void mi_tls_slots_init(void) {
918
+ // nothing
919
+ }
920
+
921
+ static void mi_tls_slots_done(void) {
922
+ // nothing
771
923
  }
772
924
 
773
925
  #endif
774
926
 
775
927
  void _mi_theap_cached_set(mi_theap_t* theap) {
928
+ mi_theap_t* prev = _mi_theap_cached();
929
+ if (prev==theap) return;
930
+ // set
931
+ mi_tls_slots_init();
776
932
  #if MI_TLS_MODEL_THREAD_LOCAL
777
933
  __mi_theap_cached = theap;
778
934
  #elif MI_TLS_MODEL_FIXED_SLOT
779
935
  mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_SLOT_CACHED, theap);
780
936
  #elif MI_TLS_MODEL_DYNAMIC_WIN32
781
- _mi_win_tls_slots_init();
782
937
  mi_win_tls_slot_set(_mi_theap_cached_slot, _mi_theap_cached_expansion_slot, theap);
783
938
  #elif MI_TLS_MODEL_DYNAMIC_PTHREADS
784
- _mi_tls_keys_init();
785
939
  if (_mi_theap_cached_key!=0) pthread_setspecific(_mi_theap_cached_key, theap);
786
940
  #endif
941
+ // update refcounts (so cached theap memory keeps available until no longer cached)
942
+ _mi_theap_incref(theap);
943
+ _mi_theap_decref(prev);
787
944
  }
788
945
 
789
946
  void _mi_theap_default_set(mi_theap_t* theap) {
947
+ mi_theap_t* const theap_old = _mi_theap_default();
790
948
  mi_assert_internal(theap != NULL);
791
949
  mi_assert_internal(theap->tld->thread_id==0 || theap->tld->thread_id==_mi_thread_id());
950
+ mi_tls_slots_init();
792
951
  #if MI_TLS_MODEL_THREAD_LOCAL
793
952
  __mi_theap_default = theap;
794
953
  #elif MI_TLS_MODEL_FIXED_SLOT
795
954
  mi_prim_tls_slot_set(MI_TLS_MODEL_FIXED_SLOT_DEFAULT, theap);
796
955
  #elif MI_TLS_MODEL_DYNAMIC_WIN32
797
- _mi_win_tls_slots_init();
798
956
  mi_win_tls_slot_set(_mi_theap_default_slot, _mi_theap_default_expansion_slot, theap);
799
957
  #elif MI_TLS_MODEL_DYNAMIC_PTHREADS
800
- _mi_tls_keys_init();
801
958
  if (_mi_theap_default_key!=0) pthread_setspecific(_mi_theap_default_key, theap);
802
959
  #endif
803
960
 
@@ -805,10 +962,15 @@ void _mi_theap_default_set(mi_theap_t* theap) {
805
962
  if (mi_theap_is_initialized(theap)) {
806
963
  // ensure the default theap is passed to `_mi_thread_done` as on some platforms we cannot access TLS at thread termination (as it would allocate again)
807
964
  _mi_prim_thread_associate_default_theap(theap);
808
- if (_mi_is_heap_main(theap->heap)) {
965
+ if (_mi_is_heap_main(_mi_theap_heap(theap))) {
809
966
  __mi_theap_main = theap;
810
967
  }
811
968
  }
969
+
970
+ // ensure either the default slot contains the main theap, or __mi_theap_main is initialized
971
+ if (mi_theap_is_initialized(theap_old) && _mi_is_heap_main(_mi_theap_heap(theap_old))) {
972
+ __mi_theap_main = theap_old;
973
+ }
812
974
  }
813
975
 
814
976
  void mi_thread_set_in_threadpool(void) mi_attr_noexcept {
@@ -932,13 +1094,8 @@ static void mi_detect_cpu_features(void) {
932
1094
 
933
1095
 
934
1096
  // Initialize the process; called by thread_init or the process loader
935
- void mi_process_init(void) mi_attr_noexcept {
936
- // ensure we are called once
937
- static mi_atomic_once_t process_init;
938
- // #if _MSC_VER < 1920
939
- // mi_heap_main_init(); // vs2017 can dynamically re-initialize theap_main
940
- // #endif
941
- if (!mi_atomic_once(&process_init)) return;
1097
+ static void mi_process_init_once(void) mi_attr_noexcept {
1098
+ _mi_process_is_initialized = true;
942
1099
  _mi_verbose_message("process init: 0x%zx\n", _mi_thread_id());
943
1100
 
944
1101
  mi_detect_cpu_features();
@@ -978,6 +1135,16 @@ void mi_process_init(void) mi_attr_noexcept {
978
1135
  }
979
1136
  }
980
1137
 
1138
+ // Initialize the process; called by thread_init or the process loader
1139
+ void mi_process_init(void) mi_attr_noexcept {
1140
+ // #if _MSC_VER < 1920
1141
+ // mi_heap_main_init(); // vs2017 can dynamically re-initialize _mi_heap_main
1142
+ // #endif
1143
+ mi_atomic_do_once {
1144
+ mi_process_init_once();
1145
+ }
1146
+ }
1147
+
981
1148
  // Called when the process is done (cdecl as it is used with `at_exit` on some platforms)
982
1149
  void mi_cdecl mi_process_done(void) mi_attr_noexcept {
983
1150
  // only shutdown if we were initialized
@@ -1002,21 +1169,25 @@ void mi_cdecl mi_process_done(void) mi_attr_noexcept {
1002
1169
  #endif
1003
1170
  #endif
1004
1171
 
1172
+ // done with tracking tools
1173
+ mi_track_done()
1174
+
1005
1175
  // Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free
1006
1176
  // since after process_done there might still be other code running that calls `free` (like at_exit routines,
1007
1177
  // or C-runtime termination code.
1008
1178
  if (mi_option_is_enabled(mi_option_destroy_on_exit)) {
1009
- mi_subprocs_unsafe_destroy_all();
1010
- _mi_page_map_unsafe_destroy(_mi_subproc_main());
1179
+ mi_subprocs_unsafe_destroy_all(); // destroys all subprocs, arenas, and the page_map!
1011
1180
  }
1012
1181
  else {
1013
1182
  mi_heap_stats_merge_to_subproc(mi_heap_main());
1014
1183
  }
1015
-
1184
+
1185
+ // careful now to no longer access any allocator functionality
1016
1186
  if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
1017
- mi_subproc_stats_print_out(NULL, NULL, NULL);
1187
+ mi_subproc_stats_print_out(mi_subproc_main(), NULL, NULL);
1018
1188
  }
1019
1189
  mi_lock_done(&subprocs_lock);
1190
+ mi_tls_slots_done();
1020
1191
  _mi_allocator_done();
1021
1192
  _mi_verbose_message("process done: 0x%zx\n", tld_main.thread_id);
1022
1193
  os_preloading = true; // don't call the C runtime anymore