@@ -2717,6 +2717,7 @@ size_t gc_heap::interesting_data_per_gc[max_idp_count];
2717
2717
#endif //MULTIPLE_HEAPS
2718
2718
2719
2719
no_gc_region_info gc_heap::current_no_gc_region_info;
2720
+ FinalizerWorkItem* gc_heap::finalizer_work;
2720
2721
BOOL gc_heap::proceed_with_gc_p = FALSE;
2721
2722
GCSpinLock gc_heap::gc_lock;
2722
2723
@@ -11472,7 +11473,16 @@ heap_segment* gc_heap::get_free_region (int gen_number, size_t size)
11472
11473
region = free_regions[huge_free_region].unlink_smallest_region (size);
11473
11474
if (region == nullptr)
11474
11475
{
11475
- ASSERT_HOLDING_SPIN_LOCK(&gc_lock);
11476
+ if (settings.pause_mode == pause_no_gc)
11477
+ {
11478
+ // In case of no-gc-region, the gc lock is being held by the thread
11479
+ // triggering the GC.
11480
+ assert (gc_lock.holding_thread != (Thread*)-1);
11481
+ }
11482
+ else
11483
+ {
11484
+ ASSERT_HOLDING_SPIN_LOCK(&gc_lock);
11485
+ }
11476
11486
11477
11487
// get it from the global list of huge free regions
11478
11488
region = global_free_huge_regions.unlink_smallest_region (size);
@@ -21228,11 +21238,41 @@ BOOL gc_heap::should_proceed_with_gc()
21228
21238
{
21229
21239
if (current_no_gc_region_info.started)
21230
21240
{
21231
- // The no_gc mode was already in progress yet we triggered another GC,
21232
- // this effectively exits the no_gc mode.
21233
- restore_data_for_no_gc();
21241
+ if (current_no_gc_region_info.soh_withheld_budget != 0)
21242
+ {
21243
+ dprintf(1, ("[no_gc_callback] allocation budget exhausted with withheld, time to trigger callback\n"));
21244
+ #ifdef MULTIPLE_HEAPS
21245
+ for (int i = 0; i < gc_heap::n_heaps; i++)
21246
+ {
21247
+ gc_heap* hp = gc_heap::g_heaps [i];
21248
+ #else
21249
+ {
21250
+ gc_heap* hp = pGenGCHeap;
21251
+ #endif
21252
+ dd_new_allocation (hp->dynamic_data_of (soh_gen0)) += current_no_gc_region_info.soh_withheld_budget;
21253
+ dd_new_allocation (hp->dynamic_data_of (loh_generation)) += current_no_gc_region_info.loh_withheld_budget;
21254
+ }
21255
+ current_no_gc_region_info.soh_withheld_budget = 0;
21256
+ current_no_gc_region_info.loh_withheld_budget = 0;
21234
21257
21235
- memset (¤t_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
21258
+ // Trigger the callback
21259
+ schedule_no_gc_callback (false);
21260
+ current_no_gc_region_info.callback = nullptr;
21261
+ return FALSE;
21262
+ }
21263
+ else
21264
+ {
21265
+ dprintf(1, ("[no_gc_callback] GC triggered while in no_gc mode. Exiting no_gc mode.\n"));
21266
+ // The no_gc mode was already in progress yet we triggered another GC,
21267
+ // this effectively exits the no_gc mode.
21268
+ restore_data_for_no_gc();
21269
+ if (current_no_gc_region_info.callback != nullptr)
21270
+ {
21271
+ dprintf (1, ("[no_gc_callback] detaching callback on exit"));
21272
+ schedule_no_gc_callback (true);
21273
+ }
21274
+ memset (¤t_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
21275
+ }
21236
21276
}
21237
21277
else
21238
21278
return should_proceed_for_no_gc();
@@ -22345,14 +22385,50 @@ end_no_gc_region_status gc_heap::end_no_gc_region()
22345
22385
status = end_no_gc_alloc_exceeded;
22346
22386
22347
22387
if (settings.pause_mode == pause_no_gc)
22388
+ {
22348
22389
restore_data_for_no_gc();
22390
+ if (current_no_gc_region_info.callback != nullptr)
22391
+ {
22392
+ dprintf (1, ("[no_gc_callback] detaching callback on exit"));
22393
+ schedule_no_gc_callback (true);
22394
+ }
22395
+ }
22349
22396
22350
22397
// sets current_no_gc_region_info.started to FALSE here.
22351
22398
memset (¤t_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
22352
22399
22353
22400
return status;
22354
22401
}
22355
22402
22403
+ void gc_heap::schedule_no_gc_callback (bool abandoned)
22404
+ {
22405
+ // We still want to schedule the work even when the no-gc callback is abandoned
22406
+ // so that we can free the memory associated with it.
22407
+ current_no_gc_region_info.callback->abandoned = abandoned;
22408
+
22409
+ if (!current_no_gc_region_info.callback->scheduled)
22410
+ {
22411
+ current_no_gc_region_info.callback->scheduled = true;
22412
+ schedule_finalizer_work(current_no_gc_region_info.callback);
22413
+ }
22414
+ }
22415
+
22416
+ void gc_heap::schedule_finalizer_work (FinalizerWorkItem* callback)
22417
+ {
22418
+ FinalizerWorkItem* prev;
22419
+ do
22420
+ {
22421
+ prev = finalizer_work;
22422
+ callback->next = prev;
22423
+ }
22424
+ while (Interlocked::CompareExchangePointer (&finalizer_work, callback, prev) != prev);
22425
+
22426
+ if (prev == nullptr)
22427
+ {
22428
+ GCToEEInterface::EnableFinalization(true);
22429
+ }
22430
+ }
22431
+
22356
22432
//update counters
22357
22433
void gc_heap::update_collection_counts ()
22358
22434
{
@@ -44395,6 +44471,103 @@ class NoGCRegionLockHolder
44395
44471
}
44396
44472
};
44397
44473
44474
+ enable_no_gc_region_callback_status gc_heap::enable_no_gc_callback(NoGCRegionCallbackFinalizerWorkItem* callback, uint64_t callback_threshold)
44475
+ {
44476
+ dprintf(1, ("[no_gc_callback] calling enable_no_gc_callback with callback_threshold = %llu\n", callback_threshold));
44477
+ enable_no_gc_region_callback_status status = enable_no_gc_region_callback_status::succeed;
44478
+ suspend_EE();
44479
+ {
44480
+ if (!current_no_gc_region_info.started)
44481
+ {
44482
+ status = enable_no_gc_region_callback_status::not_started;
44483
+ }
44484
+ else if (current_no_gc_region_info.callback != nullptr)
44485
+ {
44486
+ status = enable_no_gc_region_callback_status::already_registered;
44487
+ }
44488
+ else
44489
+ {
44490
+ uint64_t total_original_soh_budget = 0;
44491
+ uint64_t total_original_loh_budget = 0;
44492
+ #ifdef MULTIPLE_HEAPS
44493
+ for (int i = 0; i < gc_heap::n_heaps; i++)
44494
+ {
44495
+ gc_heap* hp = gc_heap::g_heaps [i];
44496
+ #else
44497
+ {
44498
+ gc_heap* hp = pGenGCHeap;
44499
+ #endif
44500
+ total_original_soh_budget += hp->soh_allocation_no_gc;
44501
+ total_original_loh_budget += hp->loh_allocation_no_gc;
44502
+ }
44503
+ uint64_t total_original_budget = total_original_soh_budget + total_original_loh_budget;
44504
+ if (total_original_budget >= callback_threshold)
44505
+ {
44506
+ uint64_t total_withheld = total_original_budget - callback_threshold;
44507
+
44508
+ float soh_ratio = ((float)total_original_soh_budget)/total_original_budget;
44509
+ float loh_ratio = ((float)total_original_loh_budget)/total_original_budget;
44510
+
44511
+ size_t soh_withheld_budget = (size_t)(soh_ratio * total_withheld);
44512
+ size_t loh_withheld_budget = (size_t)(loh_ratio * total_withheld);
44513
+
44514
+ #ifdef MULTIPLE_HEAPS
44515
+ soh_withheld_budget = soh_withheld_budget / gc_heap::n_heaps;
44516
+ loh_withheld_budget = loh_withheld_budget / gc_heap::n_heaps;
44517
+ #endif
44518
+ soh_withheld_budget = max(soh_withheld_budget, 1);
44519
+ soh_withheld_budget = Align(soh_withheld_budget, get_alignment_constant (TRUE));
44520
+ loh_withheld_budget = Align(loh_withheld_budget, get_alignment_constant (FALSE));
44521
+ #ifdef MULTIPLE_HEAPS
44522
+ for (int i = 0; i < gc_heap::n_heaps; i++)
44523
+ {
44524
+ gc_heap* hp = gc_heap::g_heaps [i];
44525
+ #else
44526
+ {
44527
+ gc_heap* hp = pGenGCHeap;
44528
+ #endif
44529
+ if (dd_new_allocation (hp->dynamic_data_of (soh_gen0)) <= (ptrdiff_t)soh_withheld_budget)
44530
+ {
44531
+ dprintf(1, ("[no_gc_callback] failed because of running out of soh budget= %llu\n", soh_withheld_budget));
44532
+ status = insufficient_budget;
44533
+ }
44534
+ if (dd_new_allocation (hp->dynamic_data_of (loh_generation)) <= (ptrdiff_t)loh_withheld_budget)
44535
+ {
44536
+ dprintf(1, ("[no_gc_callback] failed because of running out of loh budget= %llu\n", loh_withheld_budget));
44537
+ status = insufficient_budget;
44538
+ }
44539
+ }
44540
+
44541
+ if (status == enable_no_gc_region_callback_status::succeed)
44542
+ {
44543
+ dprintf(1, ("[no_gc_callback] enabling succeed\n"));
44544
+ #ifdef MULTIPLE_HEAPS
44545
+ for (int i = 0; i < gc_heap::n_heaps; i++)
44546
+ {
44547
+ gc_heap* hp = gc_heap::g_heaps [i];
44548
+ #else
44549
+ {
44550
+ gc_heap* hp = pGenGCHeap;
44551
+ #endif
44552
+ dd_new_allocation (hp->dynamic_data_of (soh_gen0)) -= soh_withheld_budget;
44553
+ dd_new_allocation (hp->dynamic_data_of (loh_generation)) -= loh_withheld_budget;
44554
+ }
44555
+ current_no_gc_region_info.soh_withheld_budget = soh_withheld_budget;
44556
+ current_no_gc_region_info.loh_withheld_budget = loh_withheld_budget;
44557
+ current_no_gc_region_info.callback = callback;
44558
+ }
44559
+ }
44560
+ else
44561
+ {
44562
+ status = enable_no_gc_region_callback_status::insufficient_budget;
44563
+ }
44564
+ }
44565
+ }
44566
+ restart_EE();
44567
+
44568
+ return status;
44569
+ }
44570
+
44398
44571
// An explanation of locking for finalization:
44399
44572
//
44400
44573
// Multiple threads allocate objects. During the allocation, they are serialized by
@@ -46135,6 +46308,16 @@ unsigned int GCHeap::WhichGeneration (Object* object)
46135
46308
return g;
46136
46309
}
46137
46310
46311
+ enable_no_gc_region_callback_status GCHeap::EnableNoGCRegionCallback(NoGCRegionCallbackFinalizerWorkItem* callback, uint64_t callback_threshold)
46312
+ {
46313
+ return gc_heap::enable_no_gc_callback(callback, callback_threshold);
46314
+ }
46315
+
46316
+ FinalizerWorkItem* GCHeap::GetExtraWorkForFinalization()
46317
+ {
46318
+ return Interlocked::ExchangePointer(&gc_heap::finalizer_work, nullptr);
46319
+ }
46320
+
46138
46321
unsigned int GCHeap::GetGenerationWithRange (Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved)
46139
46322
{
46140
46323
int generation = -1;
0 commit comments