@@ -2890,10 +2890,14 @@ bool gc_heap::trigger_initial_gen2_p = false;
2890
2890
2891
2891
#ifdef BACKGROUND_GC
2892
2892
bool gc_heap::trigger_bgc_for_rethreading_p = false;
2893
+ int gc_heap::total_bgc_threads = 0;
2894
+ int gc_heap::last_bgc_n_heaps = 0;
2895
+ int gc_heap::last_total_bgc_threads = 0;
2893
2896
#endif //BACKGROUND_GC
2894
2897
2895
2898
#ifdef STRESS_DYNAMIC_HEAP_COUNT
2896
2899
int gc_heap::heaps_in_this_gc = 0;
2900
+ int gc_heap::bgc_to_ngc2_ratio = 0;
2897
2901
#endif //STRESS_DYNAMIC_HEAP_COUNT
2898
2902
#endif // DYNAMIC_HEAP_COUNT
2899
2903
@@ -14190,6 +14194,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
14190
14194
14191
14195
if ((dynamic_adaptation_mode == dynamic_adaptation_to_application_sizes) && (conserve_mem_setting == 0))
14192
14196
conserve_mem_setting = 5;
14197
+
14198
+ #ifdef STRESS_DYNAMIC_HEAP_COUNT
14199
+ bgc_to_ngc2_ratio = (int)GCConfig::GetGCDBGCRatio();
14200
+ dprintf (1, ("bgc_to_ngc2_ratio is %d", bgc_to_ngc2_ratio));
14201
+ #endif
14193
14202
#endif //DYNAMIC_HEAP_COUNT
14194
14203
14195
14204
if (conserve_mem_setting < 0)
@@ -21079,6 +21088,18 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
21079
21088
if (!((n == max_generation) && *blocking_collection_p))
21080
21089
{
21081
21090
n = max_generation;
21091
+
21092
+ #ifdef STRESS_DYNAMIC_HEAP_COUNT
21093
+ if (bgc_to_ngc2_ratio)
21094
+ {
21095
+ int r = (int)gc_rand::get_rand ((bgc_to_ngc2_ratio + 1) * 10);
21096
+ dprintf (6666, ("%d - making this full GC %s", r, ((r < 10) ? "NGC2" : "BGC")));
21097
+ if (r < 10)
21098
+ {
21099
+ *blocking_collection_p = TRUE;
21100
+ }
21101
+ }
21102
+ #endif //STRESS_DYNAMIC_HEAP_COUNT
21082
21103
}
21083
21104
}
21084
21105
}
@@ -24340,12 +24361,37 @@ void gc_heap::garbage_collect (int n)
24340
24361
size_t saved_bgc_th_count_creation_failed = bgc_th_count_creation_failed;
24341
24362
#endif //DYNAMIC_HEAP_COUNT
24342
24363
24364
+ // This is the count of threads that GCToEEInterface::CreateThread reported successful for.
24365
+ int total_bgc_threads_running = 0;
24343
24366
for (int i = 0; i < n_heaps; i++)
24344
24367
{
24345
- prepare_bgc_thread (g_heaps[i]);
24368
+ gc_heap* hp = g_heaps[i];
24369
+ if (prepare_bgc_thread (hp))
24370
+ {
24371
+ assert (hp->bgc_thread_running);
24372
+ if (!hp->bgc_thread_running)
24373
+ {
24374
+ dprintf (6666, ("h%d prepare succeeded but running is still false!", i));
24375
+ GCToOSInterface::DebugBreak();
24376
+ }
24377
+ total_bgc_threads_running++;
24378
+ }
24379
+ else
24380
+ {
24381
+ break;
24382
+ }
24346
24383
}
24347
24384
24348
24385
#ifdef DYNAMIC_HEAP_COUNT
24386
+ // Even if we don't do a BGC, we need to record how many threads were successfully created because those will
24387
+ // be running.
24388
+ total_bgc_threads = max (total_bgc_threads, total_bgc_threads_running);
24389
+
24390
+ if (total_bgc_threads_running != n_heaps)
24391
+ {
24392
+ dprintf (6666, ("wanted to have %d BGC threads but only have %d", n_heaps, total_bgc_threads_running));
24393
+ }
24394
+
24349
24395
add_to_bgc_th_creation_history (current_gc_index,
24350
24396
(bgc_th_count_created - saved_bgc_th_count_created),
24351
24397
(bgc_th_count_created_th_existed - saved_bgc_th_count_created_th_existed),
@@ -24377,7 +24423,15 @@ void gc_heap::garbage_collect (int n)
24377
24423
for (int i = 0; i < n_heaps; i++)
24378
24424
{
24379
24425
gc_heap* hp = g_heaps[i];
24380
- if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init())
24426
+
24427
+ if (!(hp->bgc_thread_running))
24428
+ {
24429
+ assert (!(hp->bgc_thread));
24430
+ }
24431
+
24432
+ // In theory we could be in a situation where bgc_thread_running is false but bgc_thread is non NULL. We don't
24433
+ // support this scenario so don't do a BGC.
24434
+ if (!(hp->bgc_thread_running && hp->bgc_thread && hp->commit_mark_array_bgc_init()))
24381
24435
{
24382
24436
do_concurrent_p = FALSE;
24383
24437
break;
@@ -24397,8 +24451,37 @@ void gc_heap::garbage_collect (int n)
24397
24451
}
24398
24452
#endif //MULTIPLE_HEAPS
24399
24453
24454
+ #ifdef DYNAMIC_HEAP_COUNT
24455
+ dprintf (6666, ("last BGC saw %d heaps and %d total threads, currently %d heaps and %d total threads, %s BGC",
24456
+ last_bgc_n_heaps, last_total_bgc_threads, n_heaps, total_bgc_threads, (do_concurrent_p ? "doing" : "not doing")));
24457
+ #endif //DYNAMIC_HEAP_COUNT
24458
+
24400
24459
if (do_concurrent_p)
24401
24460
{
24461
+ #ifdef DYNAMIC_HEAP_COUNT
24462
+ int diff = n_heaps - last_bgc_n_heaps;
24463
+ if (diff > 0)
24464
+ {
24465
+ int saved_idle_bgc_thread_count = dynamic_heap_count_data.idle_bgc_thread_count;
24466
+ int max_idle_event_count = min (n_heaps, last_total_bgc_threads);
24467
+ int idle_events_to_set = max_idle_event_count - last_bgc_n_heaps;
24468
+ if (idle_events_to_set > 0)
24469
+ {
24470
+ Interlocked::ExchangeAdd (&dynamic_heap_count_data.idle_bgc_thread_count, -idle_events_to_set);
24471
+ dprintf (6666, ("%d BGC threads exist, setting %d idle events for h%d-h%d, total idle %d -> %d",
24472
+ total_bgc_threads, idle_events_to_set, last_bgc_n_heaps, (last_bgc_n_heaps + idle_events_to_set - 1),
24473
+ saved_idle_bgc_thread_count, VolatileLoadWithoutBarrier (&dynamic_heap_count_data.idle_bgc_thread_count)));
24474
+ for (int heap_idx = last_bgc_n_heaps; heap_idx < max_idle_event_count; heap_idx++)
24475
+ {
24476
+ g_heaps[heap_idx]->bgc_idle_thread_event.Set();
24477
+ }
24478
+ }
24479
+ }
24480
+
24481
+ last_bgc_n_heaps = n_heaps;
24482
+ last_total_bgc_threads = total_bgc_threads;
24483
+ #endif //DYNAMIC_HEAP_COUNT
24484
+
24402
24485
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24403
24486
SoftwareWriteWatch::EnableForGCHeap();
24404
24487
#endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
@@ -25926,9 +26009,6 @@ void gc_heap::check_heap_count ()
25926
26009
for (int heap_idx = n_heaps; heap_idx < new_n_heaps; heap_idx++)
25927
26010
{
25928
26011
g_heaps[heap_idx]->gc_idle_thread_event.Set();
25929
- #ifdef BACKGROUND_GC
25930
- g_heaps[heap_idx]->bgc_idle_thread_event.Set();
25931
- #endif //BACKGROUND_GC
25932
26012
}
25933
26013
}
25934
26014
@@ -37645,6 +37725,19 @@ void gc_heap::gc_thread_stub (void* arg)
37645
37725
void gc_heap::bgc_thread_stub (void* arg)
37646
37726
{
37647
37727
gc_heap* heap = (gc_heap*)arg;
37728
+
37729
+ #ifdef STRESS_DYNAMIC_HEAP_COUNT
37730
+ // We should only do this every so often; otherwise we'll never be able to do a BGC
37731
+ int r = (int)gc_rand::get_rand (30);
37732
+ bool wait_p = (r < 10);
37733
+
37734
+ if (wait_p)
37735
+ {
37736
+ GCToOSInterface::Sleep (100);
37737
+ }
37738
+ dprintf (6666, ("h%d %s", heap->heap_number, (wait_p ? "waited" : "did not wait")));
37739
+ #endif
37740
+
37648
37741
heap->bgc_thread = GCToEEInterface::GetThread();
37649
37742
assert(heap->bgc_thread != nullptr);
37650
37743
heap->bgc_thread_function();
@@ -39437,6 +39530,8 @@ void gc_heap::add_to_bgc_th_creation_history (size_t gc_index, size_t count_crea
39437
39530
}
39438
39531
#endif //DYNAMIC_HEAP_COUNT
39439
39532
39533
+ // If this returns TRUE, we are saying we expect that thread to be there. However, when that thread is available to work is indeterministic.
39534
+ // But when we actually start a BGC, naturally we'll need to wait till it gets to the point it can work.
39440
39535
BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
39441
39536
{
39442
39537
BOOL success = FALSE;
@@ -39448,7 +39543,19 @@ BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
39448
39543
dprintf (2, ("GC thread not running"));
39449
39544
if (gh->bgc_thread == 0)
39450
39545
{
39546
+ #ifdef STRESS_DYNAMIC_HEAP_COUNT
39547
+ // to stress, we just don't actually try to create the thread to simulate a failure
39548
+ int r = (int)gc_rand::get_rand (100);
39549
+ bool try_to_create_p = (r > 10);
39550
+ BOOL thread_created_p = (try_to_create_p ? create_bgc_thread (gh) : FALSE);
39551
+ if (!thread_created_p)
39552
+ {
39553
+ dprintf (6666, ("h%d we failed to create the thread, %s", gh->heap_number, (try_to_create_p ? "tried" : "didn't try")));
39554
+ }
39555
+ if (thread_created_p)
39556
+ #else //STRESS_DYNAMIC_HEAP_COUNT
39451
39557
if (create_bgc_thread(gh))
39558
+ #endif //STRESS_DYNAMIC_HEAP_COUNT
39452
39559
{
39453
39560
success = TRUE;
39454
39561
thread_created = TRUE;
@@ -39466,8 +39573,11 @@ BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
39466
39573
else
39467
39574
{
39468
39575
#ifdef DYNAMIC_HEAP_COUNT
39576
+ // This would be a very unusual scenario where GCToEEInterface::CreateThread told us it failed yet the thread was created.
39469
39577
bgc_th_count_created_th_existed++;
39578
+ dprintf (6666, ("h%d we cannot have a thread that runs yet CreateThread reported it failed to create it", gh->heap_number));
39470
39579
#endif //DYNAMIC_HEAP_COUNT
39580
+ assert (!"GCToEEInterface::CreateThread returned FALSE yet the thread was created!");
39471
39581
}
39472
39582
}
39473
39583
else
@@ -39665,7 +39775,7 @@ void gc_heap::bgc_thread_function()
39665
39775
while (1)
39666
39776
{
39667
39777
// Wait for work to do...
39668
- dprintf (3 , ("bgc thread: waiting..."));
39778
+ dprintf (6666 , ("h%d bgc thread: waiting...", heap_number ));
39669
39779
39670
39780
cooperative_mode = enable_preemptive ();
39671
39781
//current_thread->m_fPreemptiveGCDisabled = 0;
@@ -39714,36 +39824,71 @@ void gc_heap::bgc_thread_function()
39714
39824
continue;
39715
39825
}
39716
39826
}
39827
+
39828
+ #ifdef STRESS_DYNAMIC_HEAP_COUNT
39829
+ if (n_heaps <= heap_number)
39830
+ {
39831
+ uint32_t delay_ms = (uint32_t)gc_rand::get_rand (200);
39832
+ GCToOSInterface::Sleep (delay_ms);
39833
+ }
39834
+ #endif //STRESS_DYNAMIC_HEAP_COUNT
39835
+
39717
39836
// if we signal the thread with no concurrent work to do -> exit
39718
39837
if (!settings.concurrent)
39719
39838
{
39720
- dprintf (3, ("no concurrent GC needed, exiting"));
39839
+ dprintf (6666, ("h%d no concurrent GC needed, exiting", heap_number));
39840
+
39841
+ #ifdef STRESS_DYNAMIC_HEAP_COUNT
39842
+ flush_gc_log (true);
39843
+ GCToOSInterface::DebugBreak();
39844
+ #endif
39721
39845
break;
39722
39846
}
39723
- gc_background_running = TRUE;
39724
- dprintf (2, (ThreadStressLog::gcStartBgcThread(), heap_number,
39725
- generation_free_list_space (generation_of (max_generation)),
39726
- generation_free_obj_space (generation_of (max_generation)),
39727
- dd_fragmentation (dynamic_data_of (max_generation))));
39728
39847
39729
39848
#ifdef DYNAMIC_HEAP_COUNT
39730
39849
if (n_heaps <= heap_number)
39731
39850
{
39851
+ Interlocked::Increment (&dynamic_heap_count_data.idle_bgc_thread_count);
39732
39852
add_to_bgc_hc_history (hc_record_bgc_inactive);
39733
39853
39734
39854
// this is the case where we have more background GC threads than heaps
39735
39855
// - wait until we're told to continue...
39736
- dprintf (9999, ("BGC thread %d idle (%d heaps) (gc%Id)", heap_number, n_heaps, VolatileLoadWithoutBarrier (&settings.gc_index)));
39856
+ dprintf (6666, ("BGC%Id h%d going idle (%d heaps), idle count is now %d",
39857
+ VolatileLoadWithoutBarrier (&settings.gc_index), heap_number, n_heaps, VolatileLoadWithoutBarrier (&dynamic_heap_count_data.idle_bgc_thread_count)));
39737
39858
bgc_idle_thread_event.Wait(INFINITE, FALSE);
39738
- dprintf (9999, ("BGC thread %d waking from idle (%d heaps) (gc%Id)", heap_number, n_heaps, VolatileLoadWithoutBarrier (&settings.gc_index)));
39859
+ dprintf (6666, ("BGC%Id h%d woke from idle (%d heaps), idle count is now %d",
39860
+ VolatileLoadWithoutBarrier (&settings.gc_index), heap_number, n_heaps, VolatileLoadWithoutBarrier (&dynamic_heap_count_data.idle_bgc_thread_count)));
39739
39861
continue;
39740
39862
}
39741
39863
else
39742
39864
{
39865
+ if (heap_number == 0)
39866
+ {
39867
+ const int spin_count = 1024;
39868
+ int idle_bgc_thread_count = total_bgc_threads - n_heaps;
39869
+ dprintf (6666, ("n_heaps %d, total %d bgc threads, bgc idle should be %d and is %d",
39870
+ n_heaps, total_bgc_threads, idle_bgc_thread_count, VolatileLoadWithoutBarrier (&dynamic_heap_count_data.idle_bgc_thread_count)));
39871
+ if (idle_bgc_thread_count != dynamic_heap_count_data.idle_bgc_thread_count)
39872
+ {
39873
+ dprintf (6666, ("current idle is %d, trying to get to %d",
39874
+ VolatileLoadWithoutBarrier (&dynamic_heap_count_data.idle_bgc_thread_count), idle_bgc_thread_count));
39875
+ spin_and_wait (spin_count, (idle_bgc_thread_count == dynamic_heap_count_data.idle_bgc_thread_count));
39876
+ }
39877
+ }
39878
+
39743
39879
add_to_bgc_hc_history (hc_record_bgc_active);
39744
39880
}
39745
39881
#endif //DYNAMIC_HEAP_COUNT
39746
39882
39883
+ if (heap_number == 0)
39884
+ {
39885
+ gc_background_running = TRUE;
39886
+ dprintf (6666, (ThreadStressLog::gcStartBgcThread(), heap_number,
39887
+ generation_free_list_space (generation_of (max_generation)),
39888
+ generation_free_obj_space (generation_of (max_generation)),
39889
+ dd_fragmentation (dynamic_data_of (max_generation))));
39890
+ }
39891
+
39747
39892
gc1();
39748
39893
39749
39894
#ifndef DOUBLY_LINKED_FL
0 commit comments