Skip to content

Commit 1877a48

Browse files
committed
8308745: ObjArrayKlass::allocate_objArray_klass may call into java while holding a lock
Reviewed-by: dlong, dholmes, fparain
1 parent 997e615 commit 1877a48

File tree

11 files changed

+122
-78
lines changed

11 files changed

+122
-78
lines changed

src/hotspot/share/classfile/classLoaderData.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2024, 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
@@ -407,9 +407,6 @@ void ClassLoaderData::methods_do(void f(Method*)) {
407407
}
408408

409409
void ClassLoaderData::loaded_classes_do(KlassClosure* klass_closure) {
410-
// To call this, one must have the MultiArray_lock held, but the _klasses list still has lock free reads.
411-
assert_locked_or_safepoint(MultiArray_lock);
412-
413410
// Lock-free access requires load_acquire
414411
for (Klass* k = Atomic::load_acquire(&_klasses); k != nullptr; k = k->next_link()) {
415412
// Filter out InstanceKlasses (or their ObjArrayKlasses) that have not entered the

src/hotspot/share/memory/metaspace.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2017, 2021 SAP SE. All rights reserved.
44
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
55
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -858,6 +858,7 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
858858
assert(false, "Should not allocate with exception pending");
859859
return nullptr; // caller does a CHECK_NULL too
860860
}
861+
assert(!THREAD->owns_locks(), "allocating metaspace while holding mutex");
861862

862863
MetaWord* result = allocate(loader_data, word_size, type);
863864

src/hotspot/share/oops/arrayKlass.cpp

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024, 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
@@ -130,26 +130,21 @@ ArrayKlass* ArrayKlass::array_klass(int n, TRAPS) {
130130
// lock-free read needs acquire semantics
131131
if (higher_dimension_acquire() == nullptr) {
132132

133-
ResourceMark rm(THREAD);
134-
{
135-
// Ensure atomic creation of higher dimensions
136-
MutexLocker mu(THREAD, MultiArray_lock);
133+
// Ensure atomic creation of higher dimensions
134+
RecursiveLocker rl(MultiArray_lock, THREAD);
137135

138-
// Check if another thread beat us
139-
if (higher_dimension() == nullptr) {
140-
141-
// Create multi-dim klass object and link them together
142-
ObjArrayKlass* ak =
136+
if (higher_dimension() == nullptr) {
137+
// Create multi-dim klass object and link them together
138+
ObjArrayKlass* ak =
143139
ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, CHECK_NULL);
144-
ak->set_lower_dimension(this);
145-
// use 'release' to pair with lock-free load
146-
release_set_higher_dimension(ak);
147-
assert(ak->is_objArray_klass(), "incorrect initialization of ObjArrayKlass");
148-
}
140+
// use 'release' to pair with lock-free load
141+
release_set_higher_dimension(ak);
142+
assert(ak->lower_dimension() == this, "lower dimension mismatch");
149143
}
150144
}
151145

152-
ObjArrayKlass *ak = higher_dimension();
146+
ObjArrayKlass* ak = higher_dimension();
147+
assert(ak != nullptr, "should be set");
153148
THREAD->check_possible_safepoint();
154149
return ak->array_klass(n, THREAD);
155150
}

src/hotspot/share/oops/instanceKlass.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,23 +1545,22 @@ void InstanceKlass::check_valid_for_instantiation(bool throwError, TRAPS) {
15451545
ArrayKlass* InstanceKlass::array_klass(int n, TRAPS) {
15461546
// Need load-acquire for lock-free read
15471547
if (array_klasses_acquire() == nullptr) {
1548-
ResourceMark rm(THREAD);
1549-
JavaThread *jt = THREAD;
1550-
{
1551-
// Atomic creation of array_klasses
1552-
MutexLocker ma(THREAD, MultiArray_lock);
1553-
1554-
// Check if update has already taken place
1555-
if (array_klasses() == nullptr) {
1556-
ObjArrayKlass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this, CHECK_NULL);
1557-
// use 'release' to pair with lock-free load
1558-
release_set_array_klasses(k);
1559-
}
1548+
1549+
// Recursively lock array allocation
1550+
RecursiveLocker rl(MultiArray_lock, THREAD);
1551+
1552+
// Check if another thread created the array klass while we were waiting for the lock.
1553+
if (array_klasses() == nullptr) {
1554+
ObjArrayKlass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this, CHECK_NULL);
1555+
// use 'release' to pair with lock-free load
1556+
release_set_array_klasses(k);
15601557
}
15611558
}
1559+
15621560
// array_klasses() will always be set at this point
1563-
ObjArrayKlass* oak = array_klasses();
1564-
return oak->array_klass(n, THREAD);
1561+
ObjArrayKlass* ak = array_klasses();
1562+
assert(ak != nullptr, "should be set");
1563+
return ak->array_klass(n, THREAD);
15651564
}
15661565

15671566
ArrayKlass* InstanceKlass::array_klass_or_null(int n) {
@@ -2762,7 +2761,7 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
27622761
if (array_klasses() != nullptr) {
27632762
// To get a consistent list of classes we need MultiArray_lock to ensure
27642763
// array classes aren't observed while they are being restored.
2765-
MutexLocker ml(MultiArray_lock);
2764+
RecursiveLocker rl(MultiArray_lock, THREAD);
27662765
assert(this == array_klasses()->bottom_klass(), "sanity");
27672766
// Array classes have null protection domain.
27682767
// --> see ArrayKlass::complete_create_array_klass()

src/hotspot/share/oops/objArrayKlass.cpp

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024, 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
@@ -60,36 +60,19 @@ ObjArrayKlass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_da
6060
// Eagerly allocate the direct array supertype.
6161
Klass* super_klass = nullptr;
6262
if (!Universe::is_bootstrapping() || vmClasses::Object_klass_loaded()) {
63+
assert(MultiArray_lock->holds_lock(THREAD), "must hold lock after bootstrapping");
6364
Klass* element_super = element_klass->super();
6465
if (element_super != nullptr) {
6566
// The element type has a direct super. E.g., String[] has direct super of Object[].
66-
super_klass = element_super->array_klass_or_null();
67-
bool supers_exist = super_klass != nullptr;
6867
// Also, see if the element has secondary supertypes.
69-
// We need an array type for each.
68+
// We need an array type for each before creating this array type.
69+
super_klass = element_super->array_klass(CHECK_NULL);
7070
const Array<Klass*>* element_supers = element_klass->secondary_supers();
71-
for( int i = element_supers->length()-1; i >= 0; i-- ) {
71+
for (int i = element_supers->length() - 1; i >= 0; i--) {
7272
Klass* elem_super = element_supers->at(i);
73-
if (elem_super->array_klass_or_null() == nullptr) {
74-
supers_exist = false;
75-
break;
76-
}
77-
}
78-
if (!supers_exist) {
79-
// Oops. Not allocated yet. Back out, allocate it, and retry.
80-
Klass* ek = nullptr;
81-
{
82-
MutexUnlocker mu(MultiArray_lock);
83-
super_klass = element_super->array_klass(CHECK_NULL);
84-
for( int i = element_supers->length()-1; i >= 0; i-- ) {
85-
Klass* elem_super = element_supers->at(i);
86-
elem_super->array_klass(CHECK_NULL);
87-
}
88-
// Now retry from the beginning
89-
ek = element_klass->array_klass(n, CHECK_NULL);
90-
} // re-lock
91-
return ObjArrayKlass::cast(ek);
73+
elem_super->array_klass(CHECK_NULL);
9274
}
75+
// Fall through because inheritance is acyclic and we hold the global recursive lock to allocate all the arrays.
9376
} else {
9477
// The element type is already Object. Object[] has direct super of Object.
9578
super_klass = vmClasses::Object_klass();
@@ -150,6 +133,10 @@ ObjArrayKlass::ObjArrayKlass(int n, Klass* element_klass, Symbol* name) : ArrayK
150133
set_bottom_klass(bk);
151134
set_class_loader_data(bk->class_loader_data());
152135

136+
if (element_klass->is_array_klass()) {
137+
set_lower_dimension(ArrayKlass::cast(element_klass));
138+
}
139+
153140
set_layout_helper(array_layout_helper(T_OBJECT));
154141
assert(is_array_klass(), "sanity");
155142
assert(is_objArray_klass(), "sanity");

src/hotspot/share/prims/jvmtiExport.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3146,9 +3146,6 @@ bool JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() {
31463146
return false;
31473147
}
31483148

3149-
if (MultiArray_lock->owner() == thread) {
3150-
return false;
3151-
}
31523149
return true;
31533150
}
31543151

src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2024, 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
@@ -102,10 +102,6 @@ JvmtiGetLoadedClasses::getLoadedClasses(JvmtiEnv *env, jint* classCountPtr, jcla
102102

103103
LoadedClassesClosure closure(env, false);
104104
{
105-
// To get a consistent list of classes we need MultiArray_lock to ensure
106-
// array classes aren't created.
107-
MutexLocker ma(MultiArray_lock);
108-
109105
// Iterate through all classes in ClassLoaderDataGraph
110106
// and collect them using the LoadedClassesClosure
111107
MutexLocker mcld(ClassLoaderDataGraph_lock);
@@ -122,8 +118,9 @@ JvmtiGetLoadedClasses::getClassLoaderClasses(JvmtiEnv *env, jobject initiatingLo
122118
LoadedClassesClosure closure(env, true);
123119
{
124120
// To get a consistent list of classes we need MultiArray_lock to ensure
125-
// array classes aren't created during this walk.
126-
MutexLocker ma(MultiArray_lock);
121+
// array classes aren't created by another thread during this walk. This walks through the
122+
// InstanceKlass::_array_klasses links.
123+
RecursiveLocker ma(MultiArray_lock, Thread::current());
127124
MutexLocker sd(SystemDictionary_lock);
128125
oop loader = JNIHandles::resolve(initiatingLoader);
129126
// All classes loaded from this loader as initiating loader are

src/hotspot/share/runtime/mutex.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2024, 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
@@ -31,6 +31,7 @@
3131
#include "runtime/os.inline.hpp"
3232
#include "runtime/osThread.hpp"
3333
#include "runtime/safepointMechanism.inline.hpp"
34+
#include "runtime/semaphore.inline.hpp"
3435
#include "runtime/threadCrashProtection.hpp"
3536
#include "utilities/events.hpp"
3637
#include "utilities/macros.hpp"
@@ -522,3 +523,33 @@ void Mutex::set_owner_implementation(Thread *new_owner) {
522523
}
523524
}
524525
#endif // ASSERT
526+
527+
528+
RecursiveMutex::RecursiveMutex() : _sem(1), _owner(nullptr), _recursions(0) {}
529+
530+
void RecursiveMutex::lock(Thread* current) {
531+
assert(current == Thread::current(), "must be current thread");
532+
if (current == _owner) {
533+
_recursions++;
534+
} else {
535+
// can be called by jvmti by VMThread.
536+
if (current->is_Java_thread()) {
537+
_sem.wait_with_safepoint_check(JavaThread::cast(current));
538+
} else {
539+
_sem.wait();
540+
}
541+
_recursions++;
542+
assert(_recursions == 1, "should be");
543+
_owner = current;
544+
}
545+
}
546+
547+
void RecursiveMutex::unlock(Thread* current) {
548+
assert(current == Thread::current(), "must be current thread");
549+
assert(current == _owner, "must be owner");
550+
_recursions--;
551+
if (_recursions == 0) {
552+
_owner = nullptr;
553+
_sem.signal();
554+
}
555+
}

src/hotspot/share/runtime/mutex.hpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2024, 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
@@ -27,6 +27,7 @@
2727

2828
#include "memory/allocation.hpp"
2929
#include "runtime/atomic.hpp"
30+
#include "runtime/semaphore.hpp"
3031

3132
#if defined(LINUX) || defined(AIX) || defined(BSD)
3233
# include "mutex_posix.hpp"
@@ -241,4 +242,24 @@ class PaddedMonitor : public Monitor {
241242
PaddedMonitor(Rank rank, const char *name) : Monitor(rank, name) {};
242243
};
243244

245+
// RecursiveMutex is a minimal implementation, and has no safety and rank checks that Mutex has.
246+
// There are also no checks that the recursive lock is not held when going to Java or to JNI, like
247+
// other JVM mutexes have. This should be used only for cases where the alternatives with all the
248+
// nice safety features don't work.
249+
// Waiting on the RecursiveMutex partipates in the safepoint protocol if the current thread is a Java thread,
250+
// (ie. waiting sets JavaThread to blocked)
251+
class RecursiveMutex : public CHeapObj<mtThread> {
252+
Semaphore _sem;
253+
Thread* _owner;
254+
int _recursions;
255+
256+
NONCOPYABLE(RecursiveMutex);
257+
public:
258+
RecursiveMutex();
259+
void lock(Thread* current);
260+
void unlock(Thread* current);
261+
// For use in asserts
262+
bool holds_lock(Thread* current) { return _owner == current; }
263+
};
264+
244265
#endif // SHARE_RUNTIME_MUTEX_HPP

src/hotspot/share/runtime/mutexLocker.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024, 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
@@ -86,7 +86,6 @@ Monitor* Compilation_lock = nullptr;
8686
Mutex* CompileTaskAlloc_lock = nullptr;
8787
Mutex* CompileStatistics_lock = nullptr;
8888
Mutex* DirectivesStack_lock = nullptr;
89-
Mutex* MultiArray_lock = nullptr;
9089
Monitor* Terminator_lock = nullptr;
9190
Monitor* InitCompleted_lock = nullptr;
9291
Monitor* BeforeExit_lock = nullptr;
@@ -156,6 +155,8 @@ Monitor* JVMCI_lock = nullptr;
156155
Monitor* JVMCIRuntime_lock = nullptr;
157156
#endif
158157

158+
// Only one RecursiveMutex
159+
RecursiveMutex* MultiArray_lock = nullptr;
159160

160161
#define MAX_NUM_MUTEX 128
161162
static Mutex* _mutex_array[MAX_NUM_MUTEX];
@@ -269,7 +270,6 @@ void mutex_init() {
269270
MUTEX_DEFN(MethodCompileQueue_lock , PaddedMonitor, safepoint);
270271
MUTEX_DEFN(CompileStatistics_lock , PaddedMutex , safepoint);
271272
MUTEX_DEFN(DirectivesStack_lock , PaddedMutex , nosafepoint);
272-
MUTEX_DEFN(MultiArray_lock , PaddedMutex , safepoint);
273273

274274
MUTEX_DEFN(JvmtiThreadState_lock , PaddedMutex , safepoint); // Used by JvmtiThreadState/JvmtiEventController
275275
MUTEX_DEFN(EscapeBarrier_lock , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI
@@ -283,6 +283,7 @@ void mutex_init() {
283283
MUTEX_DEFN(PeriodicTask_lock , PaddedMonitor, safepoint, true);
284284
MUTEX_DEFN(RedefineClasses_lock , PaddedMonitor, safepoint);
285285
MUTEX_DEFN(Verify_lock , PaddedMutex , safepoint);
286+
MUTEX_DEFN(ClassLoaderDataGraph_lock , PaddedMutex , safepoint);
286287

287288
if (WhiteBoxAPI) {
288289
MUTEX_DEFN(Compilation_lock , PaddedMonitor, nosafepoint);
@@ -334,7 +335,6 @@ void mutex_init() {
334335

335336
MUTEX_DEFL(PerfDataMemAlloc_lock , PaddedMutex , Heap_lock);
336337
MUTEX_DEFL(PerfDataManager_lock , PaddedMutex , Heap_lock);
337-
MUTEX_DEFL(ClassLoaderDataGraph_lock , PaddedMutex , MultiArray_lock);
338338
MUTEX_DEFL(VMOperation_lock , PaddedMonitor, Heap_lock, true);
339339
MUTEX_DEFL(ClassInitError_lock , PaddedMonitor, Threads_lock);
340340

@@ -357,6 +357,9 @@ void mutex_init() {
357357
// JVMCIRuntime_lock must be acquired before JVMCI_lock to avoid deadlock
358358
MUTEX_DEFL(JVMCI_lock , PaddedMonitor, JVMCIRuntime_lock);
359359
#endif
360+
361+
// Allocate RecursiveMutex
362+
MultiArray_lock = new RecursiveMutex();
360363
}
361364

362365
#undef MUTEX_DEFL

0 commit comments

Comments
 (0)