Skip to content

Commit fe806ca

Browse files
author
William Kemper
committed
8350605: assert(!heap->is_uncommit_in_progress()) failed: Cannot uncommit bitmaps while resetting them
Reviewed-by: kdnilsen, ysr
1 parent 5b8d349 commit fe806ca

File tree

2 files changed

+55
-38
lines changed

2 files changed

+55
-38
lines changed

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

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131

3232
ShenandoahUncommitThread::ShenandoahUncommitThread(ShenandoahHeap* heap)
3333
: _heap(heap),
34-
_stop_lock(Mutex::safepoint - 2, "ShenandoahUncommitStop_lock", true),
35-
_uncommit_lock(Mutex::safepoint - 2, "ShenandoahUncommitCancel_lock", true) {
34+
_uncommit_lock(Mutex::safepoint - 2, "ShenandoahUncommit_lock", true) {
3635
set_name("Shenandoah Uncommit Thread");
3736
create_and_start();
3837

@@ -68,11 +67,10 @@ void ShenandoahUncommitThread::run_service() {
6867
uncommit(shrink_before, shrink_until);
6968
}
7069
}
71-
{
72-
MonitorLocker locker(&_stop_lock, Mutex::_no_safepoint_check_flag);
73-
if (!_stop_requested.is_set()) {
74-
timed_out = locker.wait(poll_interval);
75-
}
70+
71+
if (!should_terminate()) {
72+
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
73+
timed_out = locker.wait(poll_interval);
7674
}
7775
}
7876
}
@@ -104,15 +102,15 @@ bool ShenandoahUncommitThread::has_work(double shrink_before, size_t shrink_unti
104102
void ShenandoahUncommitThread::notify_soft_max_changed() {
105103
assert(is_uncommit_allowed(), "Only notify if uncommit is allowed");
106104
if (_soft_max_changed.try_set()) {
107-
MonitorLocker locker(&_stop_lock, Mutex::_no_safepoint_check_flag);
105+
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
108106
locker.notify_all();
109107
}
110108
}
111109

112110
void ShenandoahUncommitThread::notify_explicit_gc_requested() {
113111
assert(is_uncommit_allowed(), "Only notify if uncommit is allowed");
114112
if (_explicit_gc_requested.try_set()) {
115-
MonitorLocker locker(&_stop_lock, Mutex::_no_safepoint_check_flag);
113+
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
116114
locker.notify_all();
117115
}
118116
}
@@ -125,33 +123,64 @@ void ShenandoahUncommitThread::uncommit(double shrink_before, size_t shrink_unti
125123
assert(ShenandoahUncommit, "should be enabled");
126124
assert(_uncommit_in_progress.is_unset(), "Uncommit should not be in progress");
127125

128-
if (!is_uncommit_allowed()) {
126+
{
127+
// Final check, under the lock, if uncommit is allowed.
128+
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
129+
if (is_uncommit_allowed()) {
130+
_uncommit_in_progress.set();
131+
}
132+
}
133+
134+
// If not allowed to start, do nothing.
135+
if (!_uncommit_in_progress.is_set()) {
129136
return;
130137
}
131138

139+
// From here on, uncommit is in progress. Attempts to stop the uncommit must wait
140+
// until the cancellation request is acknowledged and uncommit is no longer in progress.
132141
const char* msg = "Concurrent uncommit";
142+
const double start = os::elapsedTime();
133143
EventMark em("%s", msg);
134-
double start = os::elapsedTime();
135144
log_info(gc, start)("%s", msg);
136145

137-
_uncommit_in_progress.set();
146+
// This is the number of regions uncommitted during this increment of uncommit work.
147+
const size_t uncommitted_region_count = do_uncommit_work(shrink_before, shrink_until);
148+
149+
{
150+
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
151+
_uncommit_in_progress.unset();
152+
locker.notify_all();
153+
}
138154

155+
if (uncommitted_region_count > 0) {
156+
_heap->notify_heap_changed();
157+
}
158+
159+
const double elapsed = os::elapsedTime() - start;
160+
log_info(gc)("%s " PROPERFMT " (" PROPERFMT ") %.3fms",
161+
msg, PROPERFMTARGS(uncommitted_region_count * ShenandoahHeapRegion::region_size_bytes()), PROPERFMTARGS(_heap->capacity()),
162+
elapsed * MILLIUNITS);
163+
}
164+
165+
size_t ShenandoahUncommitThread::do_uncommit_work(double shrink_before, size_t shrink_until) const {
166+
size_t count = 0;
139167
// Application allocates from the beginning of the heap, and GC allocates at
140168
// the end of it. It is more efficient to uncommit from the end, so that applications
141169
// could enjoy the near committed regions. GC allocations are much less frequent,
142170
// and therefore can accept the committing costs.
143-
size_t count = 0;
144171
for (size_t i = _heap->num_regions(); i > 0; i--) {
145172
if (!is_uncommit_allowed()) {
173+
// GC wants to start, so the uncommit operation must stop
146174
break;
147175
}
148176

149177
ShenandoahHeapRegion* r = _heap->get_region(i - 1);
150178
if (r->is_empty_committed() && (r->empty_time() < shrink_before)) {
151179
SuspendibleThreadSetJoiner sts_joiner;
152-
ShenandoahHeapLocker locker(_heap->lock());
180+
ShenandoahHeapLocker heap_locker(_heap->lock());
153181
if (r->is_empty_committed()) {
154182
if (_heap->committed() < shrink_until + ShenandoahHeapRegion::region_size_bytes()) {
183+
// We have uncommitted enough regions to hit the target heap committed size
155184
break;
156185
}
157186

@@ -161,26 +190,13 @@ void ShenandoahUncommitThread::uncommit(double shrink_before, size_t shrink_unti
161190
}
162191
SpinPause(); // allow allocators to take the lock
163192
}
164-
165-
{
166-
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
167-
_uncommit_in_progress.unset();
168-
locker.notify_all();
169-
}
170-
171-
if (count > 0) {
172-
_heap->notify_heap_changed();
173-
}
174-
175-
double elapsed = os::elapsedTime() - start;
176-
log_info(gc)("%s " PROPERFMT " (" PROPERFMT ") %.3fms",
177-
msg, PROPERFMTARGS(count * ShenandoahHeapRegion::region_size_bytes()), PROPERFMTARGS(_heap->capacity()),
178-
elapsed * MILLIUNITS);
193+
return count;
179194
}
180195

196+
181197
void ShenandoahUncommitThread::stop_service() {
182-
MonitorLocker locker(&_stop_lock, Mutex::_safepoint_check_flag);
183-
_stop_requested.set();
198+
MonitorLocker locker(&_uncommit_lock, Mutex::_safepoint_check_flag);
199+
_uncommit_allowed.unset();
184200
locker.notify_all();
185201
}
186202

@@ -193,5 +209,6 @@ void ShenandoahUncommitThread::forbid_uncommit() {
193209
}
194210

195211
void ShenandoahUncommitThread::allow_uncommit() {
212+
MonitorLocker locker(&_uncommit_lock, Mutex::_no_safepoint_check_flag);
196213
_uncommit_allowed.set();
197214
}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,12 @@ class ShenandoahUncommitThread : public ConcurrentGCThread {
3838
// Indicates that an explicit gc has been requested
3939
ShenandoahSharedFlag _explicit_gc_requested;
4040

41-
// Indicates that the thread should stop and terminate
42-
ShenandoahSharedFlag _stop_requested;
43-
4441
// Indicates whether it is safe to uncommit regions
4542
ShenandoahSharedFlag _uncommit_allowed;
4643

4744
// Indicates that regions are being actively uncommitted
4845
ShenandoahSharedFlag _uncommit_in_progress;
4946

50-
// This lock is used to coordinate stopping and terminating this thread
51-
Monitor _stop_lock;
52-
5347
// This lock is used to coordinate allowing or forbidding regions to be uncommitted
5448
Monitor _uncommit_lock;
5549

@@ -66,6 +60,12 @@ class ShenandoahUncommitThread : public ConcurrentGCThread {
6660
// True if the control thread has allowed this thread to uncommit regions
6761
bool is_uncommit_allowed() const;
6862

63+
// Iterate over and uncommit eligible regions until committed heap falls below
64+
// `shrink_until` bytes. A region is eligible for uncommit if the timestamp at which
65+
// it was last made empty is before `shrink_before` seconds since jvm start.
66+
// Returns the number of regions uncommitted. May be interrupted by `forbid_uncommit`.
67+
size_t do_uncommit_work(double shrink_before, size_t shrink_until) const;
68+
6969
public:
7070
explicit ShenandoahUncommitThread(ShenandoahHeap* heap);
7171

@@ -85,7 +85,7 @@ class ShenandoahUncommitThread : public ConcurrentGCThread {
8585
void allow_uncommit();
8686

8787
// True if uncommit is in progress
88-
bool is_uncommit_in_progress() {
88+
bool is_uncommit_in_progress() const {
8989
return _uncommit_in_progress.is_set();
9090
}
9191
protected:

0 commit comments

Comments
 (0)