Skip to content

Commit 3a8a432

Browse files
author
William Kemper
committed
8349094: GenShen: Race between control and regulator threads may violate assertions
Reviewed-by: ysr, kdnilsen
1 parent 99fb350 commit 3a8a432

18 files changed

+669
-636
lines changed

src/hotspot/share/gc/shared/gcCause.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class GCCause : public AllStatic {
7474

7575
_shenandoah_stop_vm,
7676
_shenandoah_allocation_failure_evac,
77+
_shenandoah_humongous_allocation_failure,
7778
_shenandoah_concurrent_gc,
7879
_shenandoah_upgrade_to_full_gc,
7980

src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ int ShenandoahOldHeuristics::compare_by_index(RegionData a, RegionData b) {
6060
ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap) :
6161
ShenandoahHeuristics(generation),
6262
_heap(gen_heap),
63-
_old_gen(generation),
6463
_first_pinned_candidate(NOT_FOUND),
6564
_last_old_collection_candidate(0),
6665
_next_old_collection_candidate(0),
@@ -567,9 +566,9 @@ void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_
567566
// allocation request will require a STW full GC.
568567
size_t allowed_old_gen_span = num_regions - (ShenandoahGenerationalHumongousReserve * num_regions) / 100;
569568

570-
size_t old_available = _old_gen->available() / HeapWordSize;
569+
size_t old_available = _old_generation->available() / HeapWordSize;
571570
size_t region_size_words = ShenandoahHeapRegion::region_size_words();
572-
size_t old_unaffiliated_available = _old_gen->free_unaffiliated_regions() * region_size_words;
571+
size_t old_unaffiliated_available = _old_generation->free_unaffiliated_regions() * region_size_words;
573572
assert(old_available >= old_unaffiliated_available, "sanity");
574573
size_t old_fragmented_available = old_available - old_unaffiliated_available;
575574

@@ -603,12 +602,12 @@ void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_
603602
}
604603

605604
void ShenandoahOldHeuristics::set_trigger_if_old_is_overgrown() {
606-
size_t old_used = _old_gen->used() + _old_gen->get_humongous_waste();
607-
size_t trigger_threshold = _old_gen->usage_trigger_threshold();
605+
size_t old_used = _old_generation->used() + _old_generation->get_humongous_waste();
606+
size_t trigger_threshold = _old_generation->usage_trigger_threshold();
608607
// Detects unsigned arithmetic underflow
609608
assert(old_used <= _heap->capacity(),
610609
"Old used (%zu, %zu) must not be more than heap capacity (%zu)",
611-
_old_gen->used(), _old_gen->get_humongous_waste(), _heap->capacity());
610+
_old_generation->used(), _old_generation->get_humongous_waste(), _heap->capacity());
612611
if (old_used > trigger_threshold) {
613612
_growth_trigger = true;
614613
}
@@ -620,13 +619,32 @@ void ShenandoahOldHeuristics::evaluate_triggers(size_t first_old_region, size_t
620619
set_trigger_if_old_is_overgrown();
621620
}
622621

622+
bool ShenandoahOldHeuristics::should_resume_old_cycle() {
623+
// If we are preparing to mark old, or if we are already marking old, then try to continue that work.
624+
if (_old_generation->is_concurrent_mark_in_progress()) {
625+
assert(_old_generation->state() == ShenandoahOldGeneration::MARKING, "Unexpected old gen state: %s", _old_generation->state_name());
626+
log_trigger("Resume marking old");
627+
return true;
628+
}
629+
630+
if (_old_generation->is_preparing_for_mark()) {
631+
assert(_old_generation->state() == ShenandoahOldGeneration::FILLING, "Unexpected old gen state: %s", _old_generation->state_name());
632+
log_trigger("Resume preparing to mark old");
633+
return true;
634+
}
635+
636+
return false;
637+
}
638+
623639
bool ShenandoahOldHeuristics::should_start_gc() {
624-
// Cannot start a new old-gen GC until previous one has finished.
625-
//
626-
// Future refinement: under certain circumstances, we might be more sophisticated about this choice.
627-
// For example, we could choose to abandon the previous old collection before it has completed evacuations.
628-
ShenandoahHeap* heap = ShenandoahHeap::heap();
629-
if (!_old_generation->can_start_gc() || heap->collection_set()->has_old_regions()) {
640+
641+
const ShenandoahHeap* heap = ShenandoahHeap::heap();
642+
if (_old_generation->is_doing_mixed_evacuations()) {
643+
// Do not try to start an old cycle if we are waiting for old regions to be evacuated (we need
644+
// a young cycle for this). Note that the young heuristic has a feature to expedite old evacuations.
645+
// Future refinement: under certain circumstances, we might be more sophisticated about this choice.
646+
// For example, we could choose to abandon the previous old collection before it has completed evacuations.
647+
log_debug(gc)("Not starting an old cycle because we are waiting for mixed evacuations");
630648
return false;
631649
}
632650

src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ class ShenandoahOldHeuristics : public ShenandoahHeuristics {
5353
static uint NOT_FOUND;
5454

5555
ShenandoahGenerationalHeap* _heap;
56-
ShenandoahOldGeneration* _old_gen;
5756

5857
// After final marking of the old generation, this heuristic will select
5958
// a set of candidate regions to be included in subsequent mixed collections.
@@ -186,6 +185,9 @@ class ShenandoahOldHeuristics : public ShenandoahHeuristics {
186185

187186
bool should_start_gc() override;
188187

188+
// Returns true if the old generation needs to prepare for marking, or continue marking.
189+
bool should_resume_old_cycle();
190+
189191
void record_success_concurrent() override;
190192

191193
void record_success_degenerated() override;

src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, Basi
9090
void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) {
9191
#if COMPILER2_OR_JVMCI
9292
assert(!ReduceInitialCardMarks || !ShenandoahCardBarrier || ShenandoahGenerationalHeap::heap()->is_in_young(new_obj),
93-
"Error: losing card mark on initialzing store to old gen");
93+
"Allocating new object outside of young generation: " INTPTR_FORMAT, p2i(new_obj));
9494
#endif // COMPILER2_OR_JVMCI
9595
assert(thread->deferred_card_mark().is_empty(), "We don't use this");
9696
}

src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,25 +123,28 @@ void ShenandoahCollectorPolicy::record_shutdown() {
123123
_in_shutdown.set();
124124
}
125125

126-
bool ShenandoahCollectorPolicy::is_at_shutdown() {
126+
bool ShenandoahCollectorPolicy::is_at_shutdown() const {
127127
return _in_shutdown.is_set();
128128
}
129129

130-
bool is_explicit_gc(GCCause::Cause cause) {
130+
bool ShenandoahCollectorPolicy::is_explicit_gc(GCCause::Cause cause) {
131131
return GCCause::is_user_requested_gc(cause)
132-
|| GCCause::is_serviceability_requested_gc(cause);
132+
|| GCCause::is_serviceability_requested_gc(cause)
133+
|| cause == GCCause::_wb_full_gc
134+
|| cause == GCCause::_wb_young_gc;
133135
}
134136

135137
bool is_implicit_gc(GCCause::Cause cause) {
136138
return cause != GCCause::_no_gc
137139
&& cause != GCCause::_shenandoah_concurrent_gc
138140
&& cause != GCCause::_allocation_failure
139-
&& !is_explicit_gc(cause);
141+
&& !ShenandoahCollectorPolicy::is_explicit_gc(cause);
140142
}
141143

142144
#ifdef ASSERT
143145
bool is_valid_request(GCCause::Cause cause) {
144-
return is_explicit_gc(cause)
146+
return ShenandoahCollectorPolicy::is_explicit_gc(cause)
147+
|| ShenandoahCollectorPolicy::is_shenandoah_gc(cause)
145148
|| cause == GCCause::_metadata_GC_clear_soft_refs
146149
|| cause == GCCause::_codecache_GC_aggressive
147150
|| cause == GCCause::_codecache_GC_threshold
@@ -153,6 +156,22 @@ bool is_valid_request(GCCause::Cause cause) {
153156
}
154157
#endif
155158

159+
bool ShenandoahCollectorPolicy::is_shenandoah_gc(GCCause::Cause cause) {
160+
return cause == GCCause::_allocation_failure
161+
|| cause == GCCause::_shenandoah_stop_vm
162+
|| cause == GCCause::_shenandoah_allocation_failure_evac
163+
|| cause == GCCause::_shenandoah_humongous_allocation_failure
164+
|| cause == GCCause::_shenandoah_concurrent_gc
165+
|| cause == GCCause::_shenandoah_upgrade_to_full_gc;
166+
}
167+
168+
169+
bool ShenandoahCollectorPolicy::is_allocation_failure(GCCause::Cause cause) {
170+
return cause == GCCause::_allocation_failure
171+
|| cause == GCCause::_shenandoah_allocation_failure_evac
172+
|| cause == GCCause::_shenandoah_humongous_allocation_failure;
173+
}
174+
156175
bool ShenandoahCollectorPolicy::is_requested_gc(GCCause::Cause cause) {
157176
return is_explicit_gc(cause) || is_implicit_gc(cause);
158177
}

src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
7777
void record_collection_cause(GCCause::Cause cause);
7878

7979
void record_shutdown();
80-
bool is_at_shutdown();
80+
bool is_at_shutdown() const;
8181

82-
ShenandoahTracer* tracer() {return _tracer;}
82+
ShenandoahTracer* tracer() const {return _tracer;}
8383

8484
void print_gc_stats(outputStream* out) const;
8585

@@ -90,15 +90,18 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
9090
// If the heuristics find that the number of consecutive degenerated cycles is above
9191
// ShenandoahFullGCThreshold, then they will initiate a Full GC upon an allocation
9292
// failure.
93-
inline size_t consecutive_degenerated_gc_count() const {
93+
size_t consecutive_degenerated_gc_count() const {
9494
return _consecutive_degenerated_gcs;
9595
}
9696

97+
static bool is_allocation_failure(GCCause::Cause cause);
98+
static bool is_shenandoah_gc(GCCause::Cause cause);
9799
static bool is_requested_gc(GCCause::Cause cause);
100+
static bool is_explicit_gc(GCCause::Cause cause);
98101
static bool should_run_full_gc(GCCause::Cause cause);
99102
static bool should_handle_requested_gc(GCCause::Cause cause);
100103

101-
inline size_t consecutive_young_gc_count() const {
104+
size_t consecutive_young_gc_count() const {
102105
return _consecutive_young_gcs;
103106
}
104107

src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ ShenandoahControlThread::ShenandoahControlThread() :
5050

5151
void ShenandoahControlThread::run_service() {
5252
ShenandoahHeap* const heap = ShenandoahHeap::heap();
53-
5453
const GCMode default_mode = concurrent_normal;
5554
const GCCause::Cause default_cause = GCCause::_shenandoah_concurrent_gc;
5655
int sleep = ShenandoahControlIntervalMin;
@@ -59,9 +58,14 @@ void ShenandoahControlThread::run_service() {
5958

6059
ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy();
6160
ShenandoahHeuristics* const heuristics = heap->heuristics();
62-
while (!in_graceful_shutdown() && !should_terminate()) {
61+
while (!should_terminate()) {
62+
const GCCause::Cause cancelled_cause = heap->cancelled_cause();
63+
if (cancelled_cause == GCCause::_shenandoah_stop_vm) {
64+
break;
65+
}
66+
6367
// Figure out if we have pending requests.
64-
const bool alloc_failure_pending = _alloc_failure_gc.is_set();
68+
const bool alloc_failure_pending = ShenandoahCollectorPolicy::is_allocation_failure(cancelled_cause);
6569
const bool is_gc_requested = _gc_requested.is_set();
6670
const GCCause::Cause requested_gc_cause = _requested_gc_cause;
6771

@@ -254,11 +258,6 @@ void ShenandoahControlThread::run_service() {
254258
}
255259
os::naked_short_sleep(sleep);
256260
}
257-
258-
// Wait for the actual stop(), can't leave run_service() earlier.
259-
while (!should_terminate()) {
260-
os::naked_short_sleep(ShenandoahControlIntervalMin);
261-
}
262261
}
263262

264263
void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cause) {
@@ -322,19 +321,24 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau
322321
bool ShenandoahControlThread::check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point) {
323322
ShenandoahHeap* heap = ShenandoahHeap::heap();
324323
if (heap->cancelled_gc()) {
325-
assert (is_alloc_failure_gc() || in_graceful_shutdown(), "Cancel GC either for alloc failure GC, or gracefully exiting");
326-
if (!in_graceful_shutdown()) {
324+
if (heap->cancelled_cause() == GCCause::_shenandoah_stop_vm) {
325+
return true;
326+
}
327+
328+
if (ShenandoahCollectorPolicy::is_allocation_failure(heap->cancelled_cause())) {
327329
assert (_degen_point == ShenandoahGC::_degenerated_outside_cycle,
328330
"Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point));
329331
_degen_point = point;
332+
return true;
330333
}
331-
return true;
334+
335+
fatal("Unexpected reason for cancellation: %s", GCCause::to_string(heap->cancelled_cause()));
332336
}
333337
return false;
334338
}
335339

336340
void ShenandoahControlThread::stop_service() {
337-
// Nothing to do here.
341+
ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_stop_vm);
338342
}
339343

340344
void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) {
@@ -363,6 +367,11 @@ void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
363367
}
364368

365369
void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
370+
if (should_terminate()) {
371+
log_info(gc)("Control thread is terminating, no more GCs");
372+
return;
373+
}
374+
366375
// For normal requested GCs (System.gc) we want to block the caller. However,
367376
// for whitebox requested GC, we want to initiate the GC and return immediately.
368377
// The whitebox caller thread will arrange for itself to wait until the GC notifies
@@ -385,7 +394,7 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
385394
MonitorLocker ml(&_gc_waiters_lock);
386395
size_t current_gc_id = get_gc_id();
387396
size_t required_gc_id = current_gc_id + 1;
388-
while (current_gc_id < required_gc_id) {
397+
while (current_gc_id < required_gc_id && !should_terminate()) {
389398
// Although setting gc request is under _gc_waiters_lock, but read side (run_service())
390399
// does not take the lock. We need to enforce following order, so that read side sees
391400
// latest requested gc cause when the flag is set.

src/hotspot/share/gc/shenandoah/shenandoahController.cpp

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#include "gc/shared/gc_globals.hpp"
2727
#include "gc/shenandoah/shenandoahController.hpp"
28+
29+
#include "shenandoahCollectorPolicy.hpp"
2830
#include "gc/shenandoah/shenandoahHeap.hpp"
2931
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
3032

@@ -37,14 +39,6 @@ size_t ShenandoahController::reset_allocs_seen() {
3739
return Atomic::xchg(&_allocs_seen, (size_t)0, memory_order_relaxed);
3840
}
3941

40-
void ShenandoahController::prepare_for_graceful_shutdown() {
41-
_graceful_shutdown.set();
42-
}
43-
44-
bool ShenandoahController::in_graceful_shutdown() {
45-
return _graceful_shutdown.is_set();
46-
}
47-
4842
void ShenandoahController::update_gc_id() {
4943
Atomic::inc(&_gc_id);
5044
}
@@ -53,59 +47,38 @@ size_t ShenandoahController::get_gc_id() {
5347
return Atomic::load(&_gc_id);
5448
}
5549

56-
void ShenandoahController::handle_alloc_failure(ShenandoahAllocRequest& req, bool block) {
57-
ShenandoahHeap* heap = ShenandoahHeap::heap();
58-
50+
void ShenandoahController::handle_alloc_failure(const ShenandoahAllocRequest& req, bool block) {
5951
assert(current()->is_Java_thread(), "expect Java thread here");
60-
bool is_humongous = ShenandoahHeapRegion::requires_humongous(req.size());
6152

62-
if (try_set_alloc_failure_gc(is_humongous)) {
63-
// Only report the first allocation failure
64-
log_info(gc)("Failed to allocate %s, %zu%s",
65-
req.type_string(),
66-
byte_size_in_proper_unit(req.size() * HeapWordSize), proper_unit_for_byte_size(req.size() * HeapWordSize));
53+
const bool is_humongous = ShenandoahHeapRegion::requires_humongous(req.size());
54+
const GCCause::Cause cause = is_humongous ? GCCause::_shenandoah_humongous_allocation_failure : GCCause::_allocation_failure;
6755

68-
// Now that alloc failure GC is scheduled, we can abort everything else
69-
heap->cancel_gc(GCCause::_allocation_failure);
56+
ShenandoahHeap* const heap = ShenandoahHeap::heap();
57+
if (heap->cancel_gc(cause)) {
58+
log_info(gc)("Failed to allocate %s, " PROPERFMT, req.type_string(), PROPERFMTARGS(req.size() * HeapWordSize));
59+
request_gc(cause);
7060
}
7161

72-
7362
if (block) {
7463
MonitorLocker ml(&_alloc_failure_waiters_lock);
75-
while (is_alloc_failure_gc()) {
64+
while (!should_terminate() && ShenandoahCollectorPolicy::is_allocation_failure(heap->cancelled_cause())) {
7665
ml.wait();
7766
}
7867
}
7968
}
8069

8170
void ShenandoahController::handle_alloc_failure_evac(size_t words) {
82-
ShenandoahHeap* heap = ShenandoahHeap::heap();
83-
bool is_humongous = ShenandoahHeapRegion::requires_humongous(words);
8471

85-
if (try_set_alloc_failure_gc(is_humongous)) {
86-
// Only report the first allocation failure
87-
log_info(gc)("Failed to allocate %zu%s for evacuation",
88-
byte_size_in_proper_unit(words * HeapWordSize), proper_unit_for_byte_size(words * HeapWordSize));
89-
}
72+
ShenandoahHeap* const heap = ShenandoahHeap::heap();
73+
const bool is_humongous = ShenandoahHeapRegion::requires_humongous(words);
74+
const GCCause::Cause cause = is_humongous ? GCCause::_shenandoah_humongous_allocation_failure : GCCause::_shenandoah_allocation_failure_evac;
9075

91-
// Forcefully report allocation failure
92-
heap->cancel_gc(GCCause::_shenandoah_allocation_failure_evac);
76+
if (heap->cancel_gc(cause)) {
77+
log_info(gc)("Failed to allocate " PROPERFMT " for evacuation", PROPERFMTARGS(words * HeapWordSize));
78+
}
9379
}
9480

9581
void ShenandoahController::notify_alloc_failure_waiters() {
96-
_alloc_failure_gc.unset();
97-
_humongous_alloc_failure_gc.unset();
9882
MonitorLocker ml(&_alloc_failure_waiters_lock);
9983
ml.notify_all();
10084
}
101-
102-
bool ShenandoahController::try_set_alloc_failure_gc(bool is_humongous) {
103-
if (is_humongous) {
104-
_humongous_alloc_failure_gc.try_set();
105-
}
106-
return _alloc_failure_gc.try_set();
107-
}
108-
109-
bool ShenandoahController::is_alloc_failure_gc() {
110-
return _alloc_failure_gc.is_set();
111-
}

0 commit comments

Comments
 (0)