@@ -526,18 +526,40 @@ void ShenandoahControlThread::service_concurrent_old_cycle(const ShenandoahHeap*
526
526
_allow_old_preemption.unset ();
527
527
528
528
if (heap->is_prepare_for_old_mark_in_progress ()) {
529
+ // Coalescing threads detected the cancellation request and aborted. Stay
530
+ // in this state so control thread may resume the coalescing work.
529
531
assert (old_generation->state () == ShenandoahOldGeneration::FILLING, " Prepare for mark should be in progress" );
530
532
return ;
531
533
}
532
534
533
- assert (old_generation->state () == ShenandoahOldGeneration::BOOTSTRAPPING, " Finished with filling, should be bootstrapping" );
535
+ // It is possible for a young generation request to preempt this nascent old
536
+ // collection cycle _after_ we've finished making the old regions parseable (filling),
537
+ // but _before_ we have unset the preemption flag. It is also possible for an
538
+ // allocation failure to occur after the threads have finished filling. We must
539
+ // check if we have been cancelled before we start a bootstrap cycle.
540
+ if (check_cancellation_or_degen (ShenandoahGC::_degenerated_outside_cycle)) {
541
+ if (heap->cancelled_gc ()) {
542
+ // If this was a preemption request, the cancellation would have been cleared
543
+ // so that we run a concurrent young cycle. If the cancellation is still set,
544
+ // then this is an allocation failure and we need to run a degenerated cycle.
545
+ // If this is a preemption request, we're just going to fall through and run
546
+ // the bootstrap cycle to start the old generation cycle (the bootstrap cycle is
547
+ // a concurrent young cycle - which is what we're being asked to do in that case).
548
+ // If the cycle is cancelled for any other reason, we return from here and let
549
+ // the control thread return to the top of its decision loop.
550
+ log_info (gc)(" Preparation for old generation cycle was cancelled" );
551
+ return ;
552
+ }
553
+ }
554
+ old_generation->transition_to (ShenandoahOldGeneration::BOOTSTRAPPING);
534
555
}
535
556
case ShenandoahOldGeneration::BOOTSTRAPPING: {
536
557
// Configure the young generation's concurrent mark to put objects in
537
558
// old regions into the concurrent mark queues associated with the old
538
559
// generation. The young cycle will run as normal except that rather than
539
560
// ignore old references it will mark and enqueue them in the old concurrent
540
561
// task queues but it will not traverse them.
562
+ set_gc_mode (bootstrapping_old);
541
563
young_generation->set_old_gen_task_queues (old_generation->task_queues ());
542
564
ShenandoahGCSession session (cause, young_generation);
543
565
service_concurrent_cycle (heap,young_generation, cause, true );
@@ -556,9 +578,7 @@ void ShenandoahControlThread::service_concurrent_old_cycle(const ShenandoahHeap*
556
578
557
579
// From here we will 'resume' the old concurrent mark. This will skip reset
558
580
// and init mark for the concurrent mark. All of that work will have been
559
- // done by the bootstrapping young cycle. In order to simplify the debugging
560
- // effort, the old cycle will ONLY complete the mark phase. No actual
561
- // collection of the old generation is happening here.
581
+ // done by the bootstrapping young cycle.
562
582
set_gc_mode (servicing_old);
563
583
old_generation->transition_to (ShenandoahOldGeneration::MARKING);
564
584
}
@@ -1016,6 +1036,7 @@ const char* ShenandoahControlThread::gc_mode_name(ShenandoahControlThread::GCMod
1016
1036
case stw_degenerated: return " degenerated" ;
1017
1037
case stw_full: return " full" ;
1018
1038
case servicing_old: return " old" ;
1039
+ case bootstrapping_old: return " bootstrap" ;
1019
1040
default : return " unknown" ;
1020
1041
}
1021
1042
}
0 commit comments