Skip to content

Commit 9a001c4

Browse files
committed
8348402: PerfDataManager stalls shutdown for 1ms
Reviewed-by: shade Backport-of: 305bbdae7fe40e33cf2baa100c134bd85ecaa553
1 parent cdeacd5 commit 9a001c4

File tree

5 files changed

+52
-22
lines changed

5 files changed

+52
-22
lines changed

src/hotspot/share/runtime/objectMonitor.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "runtime/sharedRuntime.hpp"
5454
#include "services/threadService.hpp"
5555
#include "utilities/dtrace.hpp"
56+
#include "utilities/globalCounter.inline.hpp"
5657
#include "utilities/macros.hpp"
5758
#include "utilities/preserveException.hpp"
5859
#if INCLUDE_JFR
@@ -828,9 +829,9 @@ void ObjectMonitor::EnterI(JavaThread* current) {
828829
// That is by design - we trade "lossy" counters which are exposed to
829830
// races during updates for a lower probe effect.
830831

831-
// This PerfData object can be used in parallel with a safepoint.
832-
// See the work around in PerfDataManager::destroy().
833-
OM_PERFDATA_OP(FutileWakeups, inc());
832+
// We are in safepoint safe state, so shutdown can remove the counter
833+
// under our feet. Make sure we make this access safely.
834+
OM_PERFDATA_SAFE_OP(FutileWakeups, inc());
834835
++nWakeups;
835836

836837
// Assuming this is not a spurious wakeup we'll normally find _succ == current.
@@ -971,8 +972,6 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
971972
// *must* retry _owner before parking.
972973
OrderAccess::fence();
973974

974-
// This PerfData object can be used in parallel with a safepoint.
975-
// See the work around in PerfDataManager::destroy().
976975
OM_PERFDATA_OP(FutileWakeups, inc());
977976
}
978977

src/hotspot/share/runtime/objectMonitor.hpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,31 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
197197

198198
// Only perform a PerfData operation if the PerfData object has been
199199
// allocated and if the PerfDataManager has not freed the PerfData
200-
// objects which can happen at normal VM shutdown.
201-
//
200+
// objects which can happen at normal VM shutdown. This operation is
201+
// only safe when thread is not in safepoint-safe code, i.e. PerfDataManager
202+
// could not reach the safepoint and free the counter while we are using it.
203+
// If this is not guaranteed, use OM_PERFDATA_SAFE_OP instead.
202204
#define OM_PERFDATA_OP(f, op_str) \
203205
do { \
204-
if (ObjectMonitor::_sync_ ## f != nullptr && \
205-
PerfDataManager::has_PerfData()) { \
206-
ObjectMonitor::_sync_ ## f->op_str; \
206+
if (ObjectMonitor::_sync_ ## f != nullptr) { \
207+
if (PerfDataManager::has_PerfData()) { \
208+
ObjectMonitor::_sync_ ## f->op_str; \
209+
} \
210+
} \
211+
} while (0)
212+
213+
// Only perform a PerfData operation if the PerfData object has been
214+
// allocated and if the PerfDataManager has not freed the PerfData
215+
// objects which can happen at normal VM shutdown. Additionally, we
216+
// enter the critical section to resolve the race against PerfDataManager
217+
// entering the safepoint and deleting the counter during shutdown.
218+
#define OM_PERFDATA_SAFE_OP(f, op_str) \
219+
do { \
220+
if (ObjectMonitor::_sync_ ## f != nullptr) { \
221+
GlobalCounter::CriticalSection cs(Thread::current()); \
222+
if (PerfDataManager::has_PerfData()) { \
223+
ObjectMonitor::_sync_ ## f->op_str; \
224+
} \
207225
} \
208226
} while (0)
209227

src/hotspot/share/runtime/perfData.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "runtime/os.hpp"
3636
#include "runtime/perfData.inline.hpp"
3737
#include "utilities/exceptions.hpp"
38+
#include "utilities/globalCounter.inline.hpp"
3839
#include "utilities/globalDefinitions.hpp"
3940

4041
PerfDataList* PerfDataManager::_all = nullptr;
@@ -251,15 +252,13 @@ void PerfDataManager::destroy() {
251252
// destroy already called, or initialization never happened
252253
return;
253254

254-
// Clear the flag before we free the PerfData counters. Thus begins
255-
// the race between this thread and another thread that has just
256-
// queried PerfDataManager::has_PerfData() and gotten back 'true'.
257-
// The hope is that the other thread will finish its PerfData
258-
// manipulation before we free the memory. The two alternatives are
259-
// 1) leak the PerfData memory or 2) do some form of synchronized
260-
// access or check before every PerfData operation.
261-
_has_PerfData = false;
262-
os::naked_short_sleep(1); // 1ms sleep to let other thread(s) run
255+
// About to delete the counters than might still be accessed by other threads.
256+
// The shutdown is performed in two stages: a) clear the flag to notify future
257+
// counter users that we are at shutdown; b) sync up with current users, waiting
258+
// for them to finish with counters.
259+
//
260+
Atomic::store(&_has_PerfData, false);
261+
GlobalCounter::write_synchronize();
263262

264263
log_debug(perf, datacreation)("Total = %d, Sampled = %d, Constants = %d",
265264
_all->length(), _sampled == nullptr ? 0 : _sampled->length(),
@@ -286,7 +285,7 @@ void PerfDataManager::add_item(PerfData* p, bool sampled) {
286285
// Default sizes determined using -Xlog:perf+datacreation=debug
287286
if (_all == nullptr) {
288287
_all = new PerfDataList(191);
289-
_has_PerfData = true;
288+
Atomic::release_store(&_has_PerfData, true);
290289
}
291290

292291
assert(!_all->contains(p->name()), "duplicate name added");

src/hotspot/share/runtime/perfData.hpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
2626
#define SHARE_RUNTIME_PERFDATA_HPP
2727

2828
#include "memory/allocation.hpp"
29+
#include "runtime/atomic.hpp"
2930
#include "runtime/perfDataTypes.hpp"
3031
#include "runtime/perfMemory.hpp"
3132
#include "runtime/timer.hpp"
@@ -241,6 +242,18 @@ enum CounterNS {
241242
* UsePerfData, a product flag set to true by default. This flag may
242243
* be removed from the product in the future.
243244
*
245+
* There are possible shutdown races between counter uses and counter
246+
* destruction code. Normal shutdown happens with taking VM_Exit safepoint
247+
* operation, so in the vast majority of uses this is not an issue. On the
248+
* paths where a concurrent access can still happen when VM is at safepoint,
249+
* use the following pattern to coordinate with shutdown:
250+
*
251+
* {
252+
* GlobalCounter::CriticalSection cs(Thread::current());
253+
* if (PerfDataManager::has_PerfData()) {
254+
* <update-counter>
255+
* }
256+
* }
244257
*/
245258
class PerfData : public CHeapObj<mtInternal> {
246259

@@ -785,7 +798,7 @@ class PerfDataManager : AllStatic {
785798
}
786799

787800
static void destroy();
788-
static bool has_PerfData() { return _has_PerfData; }
801+
static bool has_PerfData() { return Atomic::load_acquire(&_has_PerfData); }
789802
};
790803

791804
// Useful macros to create the performance counters

src/hotspot/share/runtime/synchronizer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "utilities/align.hpp"
6161
#include "utilities/dtrace.hpp"
6262
#include "utilities/events.hpp"
63+
#include "utilities/globalCounter.inline.hpp"
6364
#include "utilities/linkedlist.hpp"
6465
#include "utilities/preserveException.hpp"
6566

0 commit comments

Comments
 (0)