RFR: 8306334: Handle preemption of old cycle between filling and bootstrap phases [v2]

William Kemper wkemper at openjdk.org
Thu Apr 20 22:57:20 UTC 2023


On Thu, 20 Apr 2023 21:38:59 GMT, Y. Srinivas Ramakrishna <ysr at openjdk.org> wrote:

>> William Kemper has updated the pull request incrementally with one additional commit since the last revision:
>> 
>>   Re-enable TestArrayCopyStress
>
> src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp line 541:
> 
>> 539:       // have been preempted before we start a bootstrap cycle.
>> 540:       if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) {
>> 541:         if (heap->cancelled_gc()) {
> 
> In most of the other states where cancellation is checked, we only check `heap->cancelled_gc()`, but here we first `check_cancellation_or_degen()` before checking `cancelled_gc()`.
> 
> Now that method is below, where I have annotated some comments in between lines of code (github doesn't allow me to comment on lines not touched in a  PR).
> 
> 
> bool ShenandoahControlThread::check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point) {
>   ShenandoahHeap* heap = ShenandoahHeap::heap();
>   if (!heap->cancelled_gc()) {
>     return false;
>   }
> 
> 
> In other words, if `check_cancellation_..` answers true, it must be be that `cancelled_gc()` is true already so the test at line 541 ~~is~~ seemed redundant at first blush. (Which was corrected upon reading further below.)
> 
> However, there are more circumstances in which `check_cancellation_..` will answer true below.
> 
> 
> 
>   if (in_graceful_shutdown()) {
>     return true;
>   }
> 
>   assert(_degen_point == ShenandoahGC::_degenerated_outside_cycle,
>          "Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point));
> 
>   if (is_alloc_failure_gc()) {
>     _degen_point = point;
>     return true;
>   }
> 
> The assignment of `_degen_point = point` seems to be equivalent to assigning `_degen_point = ShenandoahGC::_degenerated_outside_cycle`, because of the assertion further above. So the comment in the assertion and the assignment further below look a bit fishy to me; as if code changed and these code paths may nt have been updated? Anyway, it seemed a bit confusing; such an assignment also happens further down in this method (code below).
> 
> 
> 
>   if (_preemption_requested.is_set()) {
>     assert(_requested_generation == YOUNG, "Only young GCs may preempt old.");
>     _preemption_requested.unset();
> 
>     // Old generation marking is only cancellable during concurrent marking.
>     // Once final mark is complete, the code does not check again for cancellation.
>     // If old generation was cancelled for an allocation failure, we wouldn't
>     // make it to this case. The calling code is responsible for forcing a
>     // cancellation due to allocation failure into a degenerated cycle.
>     _degen_point = point;
>     heap->clear_cancelled_gc(false /* clear oom handler */);
>     return true;
>   }
> 
> I see that above we clear the cancellation, so I can see the case where `check_cancellation_` would answer true, but a subsequent `cancelled_gc` would not. So I see the case where we are doing the extra subsequent check after returning from this method. Still, this seems a bit subtle and I wonder if these could not be somehow made less subtle (and possibly cause confusion or maintenance issues). Anyway, this would be for some future clean-up.
> 
> 
>   fatal("Cancel GC either for alloc failure GC, or gracefully exiting, or to pause old generation marking");
>   return false;
> }
> 
> 
> Anyway, I thought I'd point this out, although I don't understand all of the various interactions here well enough to do a real review.

Yes, the name `check_cancellation_or_degen` does not suggest at the complexity or side effects of the method.

> The assignment of _degen_point = point seems to be equivalent to assigning _degen_point = ShenandoahGC::_degenerated_outside_cycle, because of the assertion further above.

The assertion is checking the value of `_degen_point` _before_ the assignment, asserting that we do not overwrite a `_degen_point` that has already been set.

-------------

PR Review Comment: https://git.openjdk.org/shenandoah/pull/262#discussion_r1173158329


More information about the shenandoah-dev mailing list