From 78b80150e009745b8f28d36c3836f18ad0ca921f Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Tue, 12 Nov 2024 15:23:48 +0000 Subject: [PATCH 001/112] 8338383: Implement JEP 491: Synchronize Virtual Threads without Pinning Co-authored-by: Patricio Chilano Mateo Co-authored-by: Alan Bateman Co-authored-by: Andrew Haley Co-authored-by: Fei Yang Co-authored-by: Coleen Phillimore Co-authored-by: Richard Reingruber Co-authored-by: Martin Doerr Reviewed-by: aboldtch, dholmes, coleenp, fbredberg, dlong, sspitsyn --- src/hotspot/cpu/aarch64/aarch64.ad | 7 +- .../cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 4 +- .../cpu/aarch64/c1_Runtime1_aarch64.cpp | 37 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 31 +- .../continuationFreezeThaw_aarch64.inline.hpp | 42 +- .../continuationHelper_aarch64.inline.hpp | 20 +- src/hotspot/cpu/aarch64/frame_aarch64.cpp | 30 + src/hotspot/cpu/aarch64/frame_aarch64.hpp | 3 +- .../cpu/aarch64/interp_masm_aarch64.cpp | 83 +- .../cpu/aarch64/interp_masm_aarch64.hpp | 5 + .../cpu/aarch64/macroAssembler_aarch64.cpp | 44 +- .../cpu/aarch64/macroAssembler_aarch64.hpp | 7 +- .../cpu/aarch64/sharedRuntime_aarch64.cpp | 60 +- .../stackChunkFrameStream_aarch64.inline.hpp | 1 + .../cpu/aarch64/stubGenerator_aarch64.cpp | 32 + .../templateInterpreterGenerator_aarch64.cpp | 67 +- src/hotspot/cpu/arm/c1_Runtime1_arm.cpp | 4 + .../arm/continuationFreezeThaw_arm.inline.hpp | 13 + .../cpu/arm/continuationHelper_arm.inline.hpp | 6 +- src/hotspot/cpu/arm/frame_arm.cpp | 5 + src/hotspot/cpu/arm/sharedRuntime_arm.cpp | 5 + .../arm/templateInterpreterGenerator_arm.cpp | 4 + src/hotspot/cpu/ppc/assembler_ppc.hpp | 1 + src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 6 + src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 8 +- src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp | 8 +- .../ppc/continuationFreezeThaw_ppc.inline.hpp | 30 +- .../cpu/ppc/continuationHelper_ppc.inline.hpp | 18 +- src/hotspot/cpu/ppc/frame_ppc.cpp | 5 + src/hotspot/cpu/ppc/frame_ppc.inline.hpp | 5 +- src/hotspot/cpu/ppc/interp_masm_ppc.hpp | 10 +- src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 96 ++- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 148 ++-- src/hotspot/cpu/ppc/macroAssembler_ppc.hpp | 24 +- .../cpu/ppc/macroAssembler_ppc.inline.hpp | 14 +- src/hotspot/cpu/ppc/nativeInst_ppc.cpp | 8 +- src/hotspot/cpu/ppc/ppc.ad | 24 +- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 72 +- .../ppc/stackChunkFrameStream_ppc.inline.hpp | 5 +- src/hotspot/cpu/ppc/stubGenerator_ppc.cpp | 40 + .../ppc/templateInterpreterGenerator_ppc.cpp | 85 +- .../cpu/riscv/c1_MacroAssembler_riscv.cpp | 9 +- src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp | 40 +- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 43 +- .../cpu/riscv/c2_MacroAssembler_riscv.hpp | 10 +- .../continuationFreezeThaw_riscv.inline.hpp | 53 +- .../riscv/continuationHelper_riscv.inline.hpp | 18 +- src/hotspot/cpu/riscv/frame_riscv.cpp | 30 + src/hotspot/cpu/riscv/frame_riscv.hpp | 3 +- src/hotspot/cpu/riscv/interp_masm_riscv.cpp | 84 +- src/hotspot/cpu/riscv/interp_masm_riscv.hpp | 5 + .../cpu/riscv/macroAssembler_riscv.cpp | 42 +- .../cpu/riscv/macroAssembler_riscv.hpp | 7 +- src/hotspot/cpu/riscv/riscv.ad | 45 +- src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 61 +- .../stackChunkFrameStream_riscv.inline.hpp | 1 + src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 31 + .../templateInterpreterGenerator_riscv.cpp | 60 +- src/hotspot/cpu/s390/c1_Runtime1_s390.cpp | 5 + .../continuationFreezeThaw_s390.inline.hpp | 13 + .../s390/continuationHelper_s390.inline.hpp | 6 +- src/hotspot/cpu/s390/frame_s390.cpp | 5 + src/hotspot/cpu/s390/macroAssembler_s390.cpp | 33 +- src/hotspot/cpu/s390/sharedRuntime_s390.cpp | 5 + .../templateInterpreterGenerator_s390.cpp | 5 + src/hotspot/cpu/x86/assembler_x86.cpp | 20 + src/hotspot/cpu/x86/assembler_x86.hpp | 4 + src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 7 +- src/hotspot/cpu/x86/c1_Runtime1_x86.cpp | 27 +- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 124 ++- .../x86/continuationFreezeThaw_x86.inline.hpp | 41 +- .../cpu/x86/continuationHelper_x86.inline.hpp | 18 +- src/hotspot/cpu/x86/frame_x86.cpp | 30 + src/hotspot/cpu/x86/globalDefinitions_x86.hpp | 2 + src/hotspot/cpu/x86/interp_masm_x86.cpp | 68 +- src/hotspot/cpu/x86/interp_masm_x86.hpp | 5 + src/hotspot/cpu/x86/macroAssembler_x86.cpp | 29 +- src/hotspot/cpu/x86/macroAssembler_x86.hpp | 14 +- src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp | 5 + src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 55 +- .../x86/stackChunkFrameStream_x86.inline.hpp | 1 + src/hotspot/cpu/x86/stubGenerator_x86_64.cpp | 32 + src/hotspot/cpu/x86/stubGenerator_x86_64.hpp | 2 + src/hotspot/cpu/x86/stubRoutines_x86.hpp | 2 +- .../x86/templateInterpreterGenerator_x86.cpp | 51 +- .../continuationFreezeThaw_zero.inline.hpp | 13 + .../zero/continuationHelper_zero.inline.hpp | 6 +- src/hotspot/cpu/zero/frame_zero.cpp | 5 + .../cpu/zero/globalDefinitions_zero.hpp | 2 - src/hotspot/cpu/zero/sharedRuntime_zero.cpp | 5 + src/hotspot/cpu/zero/zeroInterpreter_zero.cpp | 6 - src/hotspot/share/c1/c1_MacroAssembler.hpp | 4 +- src/hotspot/share/c1/c1_Runtime1.hpp | 3 + src/hotspot/share/classfile/javaClasses.cpp | 81 +- src/hotspot/share/classfile/javaClasses.hpp | 21 + src/hotspot/share/classfile/vmSymbols.hpp | 3 + src/hotspot/share/code/nmethod.cpp | 5 +- src/hotspot/share/include/jvm.h | 6 + .../share/interpreter/interpreterRuntime.cpp | 2 +- src/hotspot/share/interpreter/oopMapCache.cpp | 6 + .../share/interpreter/templateInterpreter.cpp | 3 +- .../share/interpreter/templateInterpreter.hpp | 6 +- .../templateInterpreterGenerator.cpp | 4 +- .../templateInterpreterGenerator.hpp | 1 + .../interpreter/zero/bytecodeInterpreter.cpp | 18 - .../interpreter/zero/zeroInterpreter.hpp | 1 + src/hotspot/share/jfr/metadata/metadata.xml | 6 + .../share/jfr/support/jfrThreadLocal.cpp | 2 +- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 9 +- src/hotspot/share/oops/instanceKlass.cpp | 1 + .../share/oops/instanceStackChunkKlass.cpp | 19 +- .../share/oops/instanceStackChunkKlass.hpp | 5 +- .../oops/instanceStackChunkKlass.inline.hpp | 5 +- src/hotspot/share/oops/method.cpp | 5 + src/hotspot/share/oops/method.hpp | 3 + src/hotspot/share/oops/oopHandle.hpp | 1 + src/hotspot/share/oops/oopHandle.inline.hpp | 4 + src/hotspot/share/oops/stackChunkOop.cpp | 71 +- src/hotspot/share/oops/stackChunkOop.hpp | 26 +- .../share/oops/stackChunkOop.inline.hpp | 53 +- src/hotspot/share/opto/library_call.cpp | 6 + src/hotspot/share/prims/jvm.cpp | 37 + src/hotspot/share/prims/jvmtiEnv.cpp | 68 +- src/hotspot/share/prims/jvmtiEnvBase.cpp | 90 ++- src/hotspot/share/prims/jvmtiEnvBase.hpp | 8 +- src/hotspot/share/prims/jvmtiExport.cpp | 13 + src/hotspot/share/prims/jvmtiExport.hpp | 1 + src/hotspot/share/prims/jvmtiTagMap.cpp | 9 +- src/hotspot/share/runtime/basicLock.hpp | 1 + src/hotspot/share/runtime/continuation.cpp | 109 +++ src/hotspot/share/runtime/continuation.hpp | 22 +- .../share/runtime/continuationEntry.cpp | 7 + .../share/runtime/continuationEntry.hpp | 8 + .../share/runtime/continuationFreezeThaw.cpp | 740 ++++++++++++----- .../share/runtime/continuationHelper.hpp | 18 +- .../runtime/continuationHelper.inline.hpp | 24 +- .../share/runtime/continuationJavaClasses.cpp | 4 +- .../share/runtime/continuationJavaClasses.hpp | 20 +- .../continuationJavaClasses.inline.hpp | 19 +- src/hotspot/share/runtime/deoptimization.cpp | 6 +- src/hotspot/share/runtime/frame.cpp | 38 +- src/hotspot/share/runtime/frame.hpp | 8 +- src/hotspot/share/runtime/javaThread.cpp | 81 +- src/hotspot/share/runtime/javaThread.hpp | 111 ++- .../share/runtime/javaThread.inline.hpp | 12 +- .../share/runtime/lightweightSynchronizer.cpp | 53 +- .../share/runtime/lightweightSynchronizer.hpp | 2 +- src/hotspot/share/runtime/lockStack.hpp | 4 + .../share/runtime/lockStack.inline.hpp | 23 + src/hotspot/share/runtime/objectMonitor.cpp | 741 +++++++++++++----- src/hotspot/share/runtime/objectMonitor.hpp | 144 ++-- .../share/runtime/objectMonitor.inline.hpp | 134 ++-- src/hotspot/share/runtime/sharedRuntime.cpp | 2 +- src/hotspot/share/runtime/sharedRuntime.hpp | 4 + .../share/runtime/stackChunkFrameStream.hpp | 4 +- .../runtime/stackChunkFrameStream.inline.hpp | 23 +- src/hotspot/share/runtime/stackValue.cpp | 6 +- src/hotspot/share/runtime/stackValue.hpp | 4 +- src/hotspot/share/runtime/stubRoutines.cpp | 1 + src/hotspot/share/runtime/stubRoutines.hpp | 2 + src/hotspot/share/runtime/synchronizer.cpp | 66 +- src/hotspot/share/runtime/synchronizer.hpp | 13 +- .../share/runtime/threadIdentifier.cpp | 14 +- .../share/runtime/threadIdentifier.hpp | 4 +- src/hotspot/share/runtime/threads.cpp | 71 +- src/hotspot/share/runtime/threads.hpp | 5 +- src/hotspot/share/runtime/vframe.cpp | 13 +- src/hotspot/share/runtime/vframe.inline.hpp | 7 + src/hotspot/share/runtime/vframe_hp.cpp | 3 +- src/hotspot/share/runtime/vmOperations.cpp | 21 +- src/hotspot/share/runtime/vmStructs.cpp | 6 +- src/hotspot/share/services/heapDumper.cpp | 9 +- src/hotspot/share/services/threadService.cpp | 29 +- .../classes/sun/nio/ch/EPollSelectorImpl.java | 80 +- .../sun/nio/ch/KQueueSelectorImpl.java | 80 +- .../java/io/ByteArrayOutputStream.java | 12 +- .../share/classes/java/lang/Object.java | 24 +- .../java/lang/PinnedThreadPrinter.java | 151 ---- .../share/classes/java/lang/Thread.java | 16 +- .../classes/java/lang/VirtualThread.java | 295 +++++-- .../classes/java/lang/invoke/MethodType.java | 4 +- .../java/lang/invoke/StringConcatFactory.java | 2 +- .../classes/java/lang/ref/Finalizer.java | 4 +- .../java/lang/ref/NativeReferenceQueue.java | 92 --- .../classes/java/lang/ref/Reference.java | 5 - .../classes/java/lang/ref/ReferenceQueue.java | 93 +-- .../internal/access/JavaLangRefAccess.java | 9 +- .../jdk/internal/misc/InternalLock.java | 8 +- .../jdk/internal/util/ReferencedKeyMap.java | 24 +- .../jdk/internal/util/ReferencedKeySet.java | 22 +- .../classes/jdk/internal/vm/Continuation.java | 7 +- .../share/classes/sun/nio/ch/Poller.java | 52 +- .../security/ssl/X509TrustManagerImpl.java | 10 + src/java.base/share/native/libjava/Thread.c | 2 +- .../share/native/libjava/VirtualThread.c | 4 +- .../sun/jvm/hotspot/runtime/JavaThread.java | 8 +- .../jvm/hotspot/runtime/ObjectMonitor.java | 6 +- .../sun/jvm/hotspot/runtime/Threads.java | 31 +- .../classes/jdk/jfr/internal/JDKEvents.java | 1 - .../jdk/jfr/internal/MirrorEvents.java | 2 - test/hotspot/gtest/nmt/test_vmatree.cpp | 14 +- test/hotspot/jtreg/ProblemList-Virtual.txt | 14 + .../stress/OverloadCompileQueueTest.java | 4 +- .../vthread/JNIMonitor/JNIMonitor.java | 11 +- .../UnmountedVThreadNativeMethodAtTop.java | 162 ++++ .../libUnmountedVThreadNativeMethodAtTop.c} | 10 +- .../mcontenter01/libmcontenter01.cpp | 8 +- .../mcontentered01/libmcontentered01.cpp | 2 - .../monitorwaited01/libmonitorwaited01.cpp | 45 +- .../monitorwaited01/monitorwaited01.java | 3 +- .../GetThreadStateMountedTest.java | 15 +- .../libGetThreadStateMountedTest.cpp | 10 + .../StopThreadTest/StopThreadTest.java | 40 +- .../SuspendResume1/SuspendResume1.java | 16 +- .../SuspendResume2/SuspendResume2.java | 18 +- .../SuspendWithInterruptLock.java | 13 +- .../VThreadEventTest/libVThreadEventTest.cpp | 61 ++ .../VThreadMonitorTest.java | 15 + .../libVThreadMonitorTest.cpp | 19 +- .../serviceability/sa/ClhsdbInspect.java | 6 +- .../java.base/java/lang/Object.java | 673 ++++++++-------- test/jdk/ProblemList-Xcomp.txt | 1 + .../Thread/virtual/ActivateSpareCarrier.java | 147 ---- .../virtual/CancelTimerWithContention.java | 103 +++ .../java/lang/Thread/virtual/JfrEvents.java | 224 +++++- .../java/lang/Thread/virtual/LockingMode.java | 28 +- .../lang/Thread/virtual/MiscMonitorTests.java | 509 ++++++++++++ .../lang/Thread/virtual/MonitorEnterExit.java | 218 +++++- .../Thread/virtual/MonitorWaitNotify.java | 298 +++++++ .../jdk/java/lang/Thread/virtual/Parking.java | 142 +++- .../virtual/RetryMonitorEnterWhenPinned.java | 142 ++++ .../Thread/virtual/SynchronizedNative.java | 511 ++++++++++++ .../java/lang/Thread/virtual/ThreadAPI.java | 76 +- .../Thread/virtual/TracePinnedThreads.java | 177 ----- .../VirtualThreadPinnedEventThrows.java | 125 --- .../event/VirtualThreadPinnedEvent.java | 65 -- .../Thread/virtual/libSynchronizedNative.c | 32 +- .../GetStackTraceALotWithTimedWait.java | 91 +++ .../stress/LotsOfContendedMonitorEnter.java | 111 +++ .../stress/LotsOfUncontendedMonitorEnter.java | 93 +++ .../stress/Skynet100kWithMonitors.java | 121 +++ .../Thread/virtual/stress/TEST.properties | 24 - .../ReflectionCallerCacheTest.java | 2 +- .../nio/channels/vthread/SelectorOps.java | 8 + .../jdk/internal/vm/Continuation/Basic.java | 16 +- .../jdk/internal/vm/Continuation/Fuzz.java | 12 +- 246 files changed, 8283 insertions(+), 2743 deletions(-) delete mode 100644 src/java.base/share/classes/java/lang/PinnedThreadPrinter.java delete mode 100644 src/java.base/share/classes/java/lang/ref/NativeReferenceQueue.java create mode 100644 test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java rename test/{jdk/java/lang/Thread/virtual/libTracePinnedThreads.c => hotspot/jtreg/serviceability/HeapDump/libUnmountedVThreadNativeMethodAtTop.c} (74%) delete mode 100644 test/jdk/java/lang/Thread/virtual/ActivateSpareCarrier.java create mode 100644 test/jdk/java/lang/Thread/virtual/CancelTimerWithContention.java rename src/java.base/share/classes/jdk/internal/event/VirtualThreadPinnedEvent.java => test/jdk/java/lang/Thread/virtual/LockingMode.java (62%) create mode 100644 test/jdk/java/lang/Thread/virtual/MiscMonitorTests.java create mode 100644 test/jdk/java/lang/Thread/virtual/RetryMonitorEnterWhenPinned.java create mode 100644 test/jdk/java/lang/Thread/virtual/SynchronizedNative.java delete mode 100644 test/jdk/java/lang/Thread/virtual/TracePinnedThreads.java delete mode 100644 test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java delete mode 100644 test/jdk/java/lang/Thread/virtual/java.base/jdk/internal/event/VirtualThreadPinnedEvent.java rename src/jdk.jfr/share/classes/jdk/jfr/events/VirtualThreadPinnedEvent.java => test/jdk/java/lang/Thread/virtual/libSynchronizedNative.c (50%) create mode 100644 test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWithTimedWait.java create mode 100644 test/jdk/java/lang/Thread/virtual/stress/LotsOfContendedMonitorEnter.java create mode 100644 test/jdk/java/lang/Thread/virtual/stress/LotsOfUncontendedMonitorEnter.java create mode 100644 test/jdk/java/lang/Thread/virtual/stress/Skynet100kWithMonitors.java delete mode 100644 test/jdk/java/lang/Thread/virtual/stress/TEST.properties diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 26ccd217fd0f5..a97d01b7683f5 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1648,8 +1648,8 @@ int MachCallRuntimeNode::ret_addr_offset() { // for real runtime callouts it will be six instructions // see aarch64_enc_java_to_runtime // adr(rscratch2, retaddr) + // str(rscratch2, Address(rthread, JavaThread::last_Java_pc_offset())); // lea(rscratch1, RuntimeAddress(addr) - // stp(zr, rscratch2, Address(__ pre(sp, -2 * wordSize))) // blr(rscratch1) CodeBlob *cb = CodeCache::find_blob(_entry_point); if (cb) { @@ -3696,14 +3696,13 @@ encode %{ __ post_call_nop(); } else { Label retaddr; + // Make the anchor frame walkable __ adr(rscratch2, retaddr); + __ str(rscratch2, Address(rthread, JavaThread::last_Java_pc_offset())); __ lea(rscratch1, RuntimeAddress(entry)); - // Leave a breadcrumb for JavaFrameAnchor::capture_last_Java_pc() - __ stp(zr, rscratch2, Address(__ pre(sp, -2 * wordSize))); __ blr(rscratch1); __ bind(retaddr); __ post_call_nop(); - __ add(sp, sp, 2 * wordSize); } if (Compile::current()->max_vector_size() > 0) { __ reinitialize_ptrue(); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index 946471b51fd5b..fc6cadc8450cb 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -119,8 +119,8 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr cbnz(hdr, slow_case); // done bind(done); + inc_held_monitor_count(rscratch1); } - increment(Address(rthread, JavaThread::held_monitor_count_offset())); return null_check_offset; } @@ -159,8 +159,8 @@ void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_ } // done bind(done); + dec_held_monitor_count(rscratch1); } - decrement(Address(rthread, JavaThread::held_monitor_count_offset())); } diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp index 00b678d540565..19665d1b89404 100644 --- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp @@ -160,16 +160,15 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre } enum return_state_t { - does_not_return, requires_return + does_not_return, requires_return, requires_pop_epilogue_return }; - // Implementation of StubFrame class StubFrame: public StackObj { private: StubAssembler* _sasm; - bool _return_state; + return_state_t _return_state; public: StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, return_state_t return_state=requires_return); @@ -183,8 +182,17 @@ void StubAssembler::prologue(const char* name, bool must_gc_arguments) { enter(); } -void StubAssembler::epilogue() { - leave(); +void StubAssembler::epilogue(bool use_pop) { + // Avoid using a leave instruction when this frame may + // have been frozen, since the current value of rfp + // restored from the stub would be invalid. We still + // must restore the rfp value saved on enter though. + if (use_pop) { + ldp(rfp, lr, Address(post(sp, 2 * wordSize))); + authenticate_return_address(); + } else { + leave(); + } ret(lr); } @@ -203,10 +211,10 @@ void StubFrame::load_argument(int offset_in_words, Register reg) { } StubFrame::~StubFrame() { - if (_return_state == requires_return) { - __ epilogue(); - } else { + if (_return_state == does_not_return) { __ should_not_reach_here(); + } else { + __ epilogue(_return_state == requires_pop_epilogue_return); } } @@ -252,7 +260,7 @@ static OopMap* generate_oop_map(StubAssembler* sasm, bool save_fpu_registers) { for (int i = 0; i < FrameMap::nof_cpu_regs; i++) { Register r = as_Register(i); - if (i <= 18 && i != rscratch1->encoding() && i != rscratch2->encoding()) { + if (r == rthread || (i <= 18 && i != rscratch1->encoding() && i != rscratch2->encoding())) { int sp_offset = cpu_reg_save_offsets[i]; oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset), r->as_VMReg()); @@ -337,6 +345,15 @@ void Runtime1::initialize_pd() { } } +// return: offset in 64-bit words. +uint Runtime1::runtime_blob_current_thread_offset(frame f) { + CodeBlob* cb = f.cb(); + assert(cb == Runtime1::blob_for(C1StubId::monitorenter_id) || + cb == Runtime1::blob_for(C1StubId::monitorenter_nofpu_id), "must be"); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + int offset = cpu_reg_save_offsets[rthread->encoding()]; + return offset / 2; // SP offsets are in halfwords +} // target: the entry point of the method that creates and posts the exception oop // has_argument: true if the exception needs arguments (passed in rscratch1 and rscratch2) @@ -868,7 +885,7 @@ OopMapSet* Runtime1::generate_code_for(C1StubId id, StubAssembler* sasm) { // fall through case C1StubId::monitorenter_id: { - StubFrame f(sasm, "monitorenter", dont_gc_arguments); + StubFrame f(sasm, "monitorenter", dont_gc_arguments, requires_pop_epilogue_return); OopMap* map = save_live_registers(sasm, save_fpu_registers); // Called with store_parameter and not C abi diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index a7bbb6ebe0f10..4c22133c0566e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -153,7 +153,7 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register Label count, no_count; assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr); + assert_different_registers(oop, box, tmp, disp_hdr, rscratch2); // Load markWord from object into displaced_header. ldr(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes())); @@ -206,12 +206,10 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register // Handle existing monitor. bind(object_has_monitor); - // The object's monitor m is unlocked iff m->owner == nullptr, - // otherwise m->owner may contain a thread or a stack address. - // - // Try to CAS m->owner from null to current thread. + // Try to CAS owner (no owner => current thread's _lock_id). + ldr(rscratch2, Address(rthread, JavaThread::lock_id_offset())); add(tmp, disp_hdr, (in_bytes(ObjectMonitor::owner_offset())-markWord::monitor_value)); - cmpxchg(tmp, zr, rthread, Assembler::xword, /*acquire*/ true, + cmpxchg(tmp, zr, rscratch2, Assembler::xword, /*acquire*/ true, /*release*/ true, /*weak*/ false, tmp3Reg); // Sets flags for result // Store a non-null value into the box to avoid looking like a re-entrant @@ -223,7 +221,7 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register br(Assembler::EQ, cont); // CAS success means locking succeeded - cmp(tmp3Reg, rthread); + cmp(tmp3Reg, rscratch2); br(Assembler::NE, cont); // Check for recursive locking // Recursive lock case @@ -236,7 +234,9 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register br(Assembler::NE, no_count); bind(count); - increment(Address(rthread, JavaThread::held_monitor_count_offset())); + if (LockingMode == LM_LEGACY) { + inc_held_monitor_count(rscratch1); + } bind(no_count); } @@ -343,7 +343,9 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe br(Assembler::NE, no_count); bind(count); - decrement(Address(rthread, JavaThread::held_monitor_count_offset())); + if (LockingMode == LM_LEGACY) { + dec_held_monitor_count(rscratch1); + } bind(no_count); } @@ -351,7 +353,7 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register t1, Register t2, Register t3) { assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - assert_different_registers(obj, box, t1, t2, t3); + assert_different_registers(obj, box, t1, t2, t3, rscratch2); // Handle inflated monitor. Label inflated; @@ -467,13 +469,14 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist // Compute owner address. lea(t2_owner_addr, owner_address); - // CAS owner (null => current thread). - cmpxchg(t2_owner_addr, zr, rthread, Assembler::xword, /*acquire*/ true, + // Try to CAS owner (no owner => current thread's _lock_id). + ldr(rscratch2, Address(rthread, JavaThread::lock_id_offset())); + cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, /*acquire*/ true, /*release*/ false, /*weak*/ false, t3_owner); br(Assembler::EQ, monitor_locked); // Check if recursive. - cmp(t3_owner, rthread); + cmp(t3_owner, rscratch2); br(Assembler::NE, slow_path); // Recursive. @@ -486,7 +489,6 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist } bind(locked); - increment(Address(rthread, JavaThread::held_monitor_count_offset())); #ifdef ASSERT // Check that locked label is reached with Flags == EQ. @@ -655,7 +657,6 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Regi } bind(unlocked); - decrement(Address(rthread, JavaThread::held_monitor_count_offset())); cmp(zr, zr); // Set Flags to EQ => fast path #ifdef ASSERT diff --git a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp index d3b9e89773cb1..e82162df4ab26 100644 --- a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,6 +129,11 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { } } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + assert(f.interpreter_frame_last_sp() == nullptr, "should be null for top frame"); + f.interpreter_frame_set_last_sp(f.unextended_sp()); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { assert(hf.fp() == hf.unextended_sp() + (f.fp() - f.unextended_sp()), ""); assert((f.at(frame::interpreter_frame_last_sp_offset) != 0) @@ -149,10 +154,16 @@ inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, co // extended_sp is already relativized by TemplateInterpreterGenerator::generate_normal_entry or // AbstractInterpreter::layout_activation + // The interpreter native wrapper code adds space in the stack equal to size_of_parameters() + // after the fixed part of the frame. For wait0 this is equal to 3 words (this + long parameter). + // We adjust by this size since otherwise the saved last sp will be less than the extended_sp. + DEBUG_ONLY(Method* m = hf.interpreter_frame_method();) + DEBUG_ONLY(int extra_space = m->is_object_wait0() ? m->size_of_parameters() : 0;) + assert((hf.fp() - hf.unextended_sp()) == (f.fp() - f.unextended_sp()), ""); assert(hf.unextended_sp() == (intptr_t*)hf.at(frame::interpreter_frame_last_sp_offset), ""); assert(hf.unextended_sp() <= (intptr_t*)hf.at(frame::interpreter_frame_initial_sp_offset), ""); - assert(hf.unextended_sp() > (intptr_t*)hf.at(frame::interpreter_frame_extended_sp_offset), ""); + assert(hf.unextended_sp() + extra_space > (intptr_t*)hf.at(frame::interpreter_frame_extended_sp_offset), ""); assert(hf.fp() > (intptr_t*)hf.at(frame::interpreter_frame_initial_sp_offset), ""); assert(hf.fp() <= (intptr_t*)hf.at(frame::interpreter_frame_locals_offset), ""); } @@ -213,7 +224,6 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& // If caller is interpreted it already made room for the callee arguments int overlap = caller.is_interpreted_frame() ? ContinuationHelper::InterpretedFrame::stack_argsize(hf) : 0; const int fsize = (int)(ContinuationHelper::InterpretedFrame::frame_bottom(hf) - hf.unextended_sp() - overlap); - const int locals = hf.interpreter_frame_method()->max_locals(); intptr_t* frame_sp = caller.unextended_sp() - fsize; intptr_t* fp = frame_sp + (hf.fp() - heap_sp); if ((intptr_t)fp % frame::frame_alignment != 0) { @@ -235,7 +245,7 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& int fsize = FKind::size(hf); intptr_t* frame_sp = caller.unextended_sp() - fsize; if (bottom || caller.is_interpreted_frame()) { - int argsize = hf.compiled_frame_stack_argsize(); + int argsize = FKind::stack_argsize(hf); fsize += argsize; frame_sp -= argsize; @@ -252,8 +262,8 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& // we need to recreate a "real" frame pointer, pointing into the stack fp = frame_sp + FKind::size(hf) - frame::sender_sp_offset; } else { - fp = FKind::stub - ? frame_sp + fsize - frame::sender_sp_offset // on AArch64, this value is used for the safepoint stub + fp = FKind::stub || FKind::native + ? frame_sp + fsize - frame::sender_sp_offset // fp always points to the address below the pushed return pc. We need correct address. : *(intptr_t**)(hf.sp() - frame::sender_sp_offset); // we need to re-read fp because it may be an oop and we might have fixed the frame. } return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); // TODO PERF : this computes deopt state; is it necessary? @@ -277,6 +287,22 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) { patch_callee_link(caller, caller.fp()); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + intptr_t* fp = caller_sp - frame::sender_sp_offset; + patch_callee_link(f, fp); +} + +inline intptr_t* ThawBase::push_cleanup_continuation() { + frame enterSpecial = new_entry_frame(); + intptr_t* sp = enterSpecial.sp(); + + sp[-1] = (intptr_t)ContinuationEntry::cleanup_pc(); + sp[-2] = (intptr_t)enterSpecial.fp(); + + log_develop_trace(continuations, preempt)("push_cleanup_continuation initial sp: " INTPTR_FORMAT " final sp: " INTPTR_FORMAT, p2i(sp + 2 * frame::metadata_words), p2i(sp)); + return sp; +} + inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { // Make sure that last_sp is kept relativized. assert((intptr_t*)f.at_relative(frame::interpreter_frame_last_sp_offset) == f.unextended_sp(), ""); @@ -285,7 +311,9 @@ inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, c assert(f.at_absolute(frame::interpreter_frame_monitor_block_top_offset) <= frame::interpreter_frame_initial_sp_offset, ""); // Make sure that extended_sp is kept relativized. - assert((intptr_t*)f.at_relative(frame::interpreter_frame_extended_sp_offset) < f.unextended_sp(), ""); + DEBUG_ONLY(Method* m = hf.interpreter_frame_method();) + DEBUG_ONLY(int extra_space = m->is_object_wait0() ? m->size_of_parameters() : 0;) // see comment in relativize_interpreted_frame_metadata() + assert((intptr_t*)f.at_relative(frame::interpreter_frame_extended_sp_offset) < f.unextended_sp() + extra_space, ""); } #endif // CPU_AARCH64_CONTINUATIONFREEZETHAW_AARCH64_INLINE_HPP diff --git a/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp index 2715ebef8f0d5..e39580369db49 100644 --- a/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,22 @@ static inline intptr_t** link_address(const frame& f) { : (intptr_t**)(f.unextended_sp() + f.cb()->frame_size() - frame::sender_sp_offset); } +static inline void patch_return_pc_with_preempt_stub(frame& f) { + if (f.is_runtime_frame()) { + // Unlike x86 we don't know where in the callee frame the return pc is + // saved so we can't patch the return from the VM call back to Java. + // Instead, we will patch the return from the runtime stub back to the + // compiled method so that the target returns to the preempt cleanup stub. + intptr_t* caller_sp = f.sp() + f.cb()->frame_size(); + caller_sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub(); + } else { + // The target will check for preemption once it returns to the interpreter + // or the native wrapper code and will manually jump to the preempt stub. + JavaThread *thread = JavaThread::current(); + thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub()); + } +} + inline int ContinuationHelper::frame_align_words(int size) { #ifdef _LP64 return size & 1; @@ -83,12 +99,12 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, anchor->set_last_Java_fp(entry->entry_fp()); } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { intptr_t* fp = *(intptr_t**)(sp - frame::sender_sp_offset); anchor->set_last_Java_fp(fp); } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { intptr_t* sp = f.sp(); address pc = ContinuationHelper::return_address_at( diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index c2b127f31bb6d..361b913fd2ea2 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -420,6 +420,36 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { return fr; } +#if defined(ASSERT) +static address get_register_address_in_stub(const frame& stub_fr, VMReg reg) { + RegisterMap map(nullptr, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + stub_fr.oop_map()->update_register_map(&stub_fr, &map); + return map.location(reg, stub_fr.sp()); +} +#endif + +JavaThread** frame::saved_thread_address(const frame& f) { + CodeBlob* cb = f.cb(); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + JavaThread** thread_addr; +#ifdef COMPILER1 + if (cb == Runtime1::blob_for(C1StubId::monitorenter_id) || + cb == Runtime1::blob_for(C1StubId::monitorenter_nofpu_id)) { + thread_addr = (JavaThread**)(f.sp() + Runtime1::runtime_blob_current_thread_offset(f)); + } else +#endif + { + // c2 only saves rbp in the stub frame so nothing to do. + thread_addr = nullptr; + } + assert(get_register_address_in_stub(f, SharedRuntime::thread_register()) == (address)thread_addr, "wrong thread address"); + return thread_addr; +} + //------------------------------------------------------------------------------ // frame::verify_deopt_original_pc // diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 401e2c6ae97ee..da020b4234d10 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -73,7 +73,8 @@ sender_sp_offset = 2, // Interpreter frames - interpreter_frame_oop_temp_offset = 3, // for native calls only + interpreter_frame_result_handler_offset = 3, // for native calls only + interpreter_frame_oop_temp_offset = 2, // for native calls only interpreter_frame_sender_sp_offset = -1, // outgoing sp before a call to an invoked method diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index c6af74c3dcdce..b892489af706f 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -666,7 +666,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); if (LockingMode == LM_MONITOR) { - call_VM(noreg, + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); } else { @@ -697,7 +697,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) if (LockingMode == LM_LIGHTWEIGHT) { lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); - b(count); + b(done); } else if (LockingMode == LM_LEGACY) { // Load (object->mark() | 1) into swap_reg ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes())); @@ -747,18 +747,18 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) // Save the test result, for recursive case, the result is zero str(swap_reg, Address(lock_reg, mark_offset)); - br(Assembler::EQ, count); + br(Assembler::NE, slow_case); + + bind(count); + inc_held_monitor_count(rscratch1); + b(done); } bind(slow_case); // Call the runtime routine for slow case - call_VM(noreg, + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); - b(done); - - bind(count); - increment(Address(rthread, JavaThread::held_monitor_count_offset())); bind(done); } @@ -804,11 +804,10 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) // Free entry str(zr, Address(lock_reg, BasicObjectLock::obj_offset())); + Label slow_case; if (LockingMode == LM_LIGHTWEIGHT) { - Label slow_case; lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); - b(count); - bind(slow_case); + b(done); } else if (LockingMode == LM_LEGACY) { // Load the old header from BasicLock structure ldr(header_reg, Address(swap_reg, @@ -818,16 +817,17 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) cbz(header_reg, count); // Atomic swap back the old header - cmpxchg_obj_header(swap_reg, header_reg, obj_reg, rscratch1, count, /*fallthrough*/nullptr); + cmpxchg_obj_header(swap_reg, header_reg, obj_reg, rscratch1, count, &slow_case); + + bind(count); + dec_held_monitor_count(rscratch1); + b(done); } + + bind(slow_case); // Call the runtime routine for slow case. str(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - b(done); - - bind(count); - decrement(Address(rthread, JavaThread::held_monitor_count_offset())); - bind(done); restore_bcp(); } @@ -1531,6 +1531,55 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, restore_locals(); } +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1) { + assert(arg_1 == c_rarg1, ""); + Label resume_pc, not_preempted; + +#ifdef ASSERT + { + Label L; + ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); + cbz(rscratch1, L); + stop("Should not have alternate return address set"); + bind(L); + } +#endif /* ASSERT */ + + // Force freeze slow path. + push_cont_fastpath(); + + // Make VM call. In case of preemption set last_pc to the one we want to resume to. + adr(rscratch1, resume_pc); + str(rscratch1, Address(rthread, JavaThread::last_Java_pc_offset())); + call_VM_base(oop_result, noreg, noreg, entry_point, 1, false /*check_exceptions*/); + + pop_cont_fastpath(); + + // Check if preempted. + ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); + cbz(rscratch1, not_preempted); + str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); + br(rscratch1); + + // In case of preemption, this is where we will resume once we finally acquire the monitor. + bind(resume_pc); + restore_after_resume(false /* is_native */); + + bind(not_preempted); +} + +void InterpreterMacroAssembler::restore_after_resume(bool is_native) { + lea(rscratch1, ExternalAddress(Interpreter::cont_resume_interpreter_adapter())); + blr(rscratch1); + if (is_native) { + // On resume we need to set up stack as expected + push(dtos); + push(ltos); + } +} + void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { assert_different_registers(obj, rscratch1, mdo_addr.base(), mdo_addr.index()); Label update, next, none; diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 34dca56eaf746..bc2070d283d7d 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -58,6 +58,11 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_earlyret_value(TosState state); + void call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1); + void restore_after_resume(bool is_native); + void jump_to_entry(address entry); virtual void check_and_handle_popframe(Register java_thread); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 99ae1f1d90194..ce71d3a22621d 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -39,6 +39,7 @@ #include "gc/shared/tlab_globals.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "jvm.h" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -775,6 +776,10 @@ static void pass_arg3(MacroAssembler* masm, Register arg) { } } +static bool is_preemptable(address entry_point) { + return entry_point == CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter); +} + void MacroAssembler::call_VM_base(Register oop_result, Register java_thread, Register last_java_sp, @@ -810,7 +815,12 @@ void MacroAssembler::call_VM_base(Register oop_result, assert(last_java_sp != rfp, "can't use rfp"); Label l; - set_last_Java_frame(last_java_sp, rfp, l, rscratch1); + if (is_preemptable(entry_point)) { + // skip setting last_pc since we already set it to desired value. + set_last_Java_frame(last_java_sp, rfp, noreg, rscratch1); + } else { + set_last_Java_frame(last_java_sp, rfp, l, rscratch1); + } // do the call, remove parameters MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments, &l); @@ -5536,6 +5546,38 @@ void MacroAssembler::tlab_allocate(Register obj, bs->tlab_allocate(this, obj, var_size_in_bytes, con_size_in_bytes, t1, t2, slow_case); } +void MacroAssembler::inc_held_monitor_count(Register tmp) { + Address dst(rthread, JavaThread::held_monitor_count_offset()); +#ifdef ASSERT + ldr(tmp, dst); + increment(tmp); + str(tmp, dst); + Label ok; + tbz(tmp, 63, ok); + STOP("assert(held monitor count underflow)"); + should_not_reach_here(); + bind(ok); +#else + increment(dst); +#endif +} + +void MacroAssembler::dec_held_monitor_count(Register tmp) { + Address dst(rthread, JavaThread::held_monitor_count_offset()); +#ifdef ASSERT + ldr(tmp, dst); + decrement(tmp); + str(tmp, dst); + Label ok; + tbz(tmp, 63, ok); + STOP("assert(held monitor count underflow)"); + should_not_reach_here(); + bind(ok); +#else + decrement(dst); +#endif +} + void MacroAssembler::verify_tlab() { #ifdef ASSERT if (UseTLAB && VerifyOops) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 2a1edb01b209a..d9686aec3a96b 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -940,8 +940,11 @@ class MacroAssembler: public Assembler { void pop_CPU_state(bool restore_vectors = false, bool use_sve = false, int sve_vector_size_in_bytes = 0, int total_predicate_in_bytes = 0); - void push_cont_fastpath(Register java_thread); - void pop_cont_fastpath(Register java_thread); + void push_cont_fastpath(Register java_thread = rthread); + void pop_cont_fastpath(Register java_thread = rthread); + + void inc_held_monitor_count(Register tmp); + void dec_held_monitor_count(Register tmp); // Round up to a power of two void round_to(Register reg, int modulus); diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index fb8ae80cb4935..94d73b6bc2720 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -1179,12 +1179,14 @@ static void gen_continuation_enter(MacroAssembler* masm, __ bind(call_thaw); + ContinuationEntry::_thaw_call_pc_offset = __ pc() - start; __ rt_call(CAST_FROM_FN_PTR(address, StubRoutines::cont_thaw())); oop_maps->add_gc_map(__ pc() - start, map->deep_copy()); ContinuationEntry::_return_pc_offset = __ pc() - start; __ post_call_nop(); __ bind(exit); + ContinuationEntry::_cleanup_offset = __ pc() - start; continuation_enter_cleanup(masm); __ leave(); __ ret(lr); @@ -1281,6 +1283,10 @@ static void gen_continuation_yield(MacroAssembler* masm, oop_maps->add_gc_map(the_pc - start, map); } +void SharedRuntime::continuation_enter_cleanup(MacroAssembler* masm) { + ::continuation_enter_cleanup(masm); +} + static void gen_special_dispatch(MacroAssembler* masm, const methodHandle& method, const BasicType* sig_bt, @@ -1747,11 +1753,20 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, } // Change state to native (we save the return address in the thread, since it might not - // be pushed on the stack when we do a stack traversal). - // We use the same pc/oopMap repeatedly when we call out + // be pushed on the stack when we do a stack traversal). It is enough that the pc() + // points into the right code segment. It does not have to be the correct return pc. + // We use the same pc/oopMap repeatedly when we call out. Label native_return; - __ set_last_Java_frame(sp, noreg, native_return, rscratch1); + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + // For convenience we use the pc we want to resume to in case of preemption on Object.wait. + __ set_last_Java_frame(sp, noreg, native_return, rscratch1); + } else { + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + + __ set_last_Java_frame(sp, noreg, __ pc(), rscratch1); + } Label dtrace_method_entry, dtrace_method_entry_done; if (DTraceMethodProbes) { @@ -1829,12 +1844,13 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Save the test result, for recursive case, the result is zero __ str(swap_reg, Address(lock_reg, mark_word_offset)); __ br(Assembler::NE, slow_path_lock); + + __ bind(count); + __ inc_held_monitor_count(rscratch1); } else { assert(LockingMode == LM_LIGHTWEIGHT, "must be"); __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); } - __ bind(count); - __ increment(Address(rthread, JavaThread::held_monitor_count_offset())); // Slow path will re-enter here __ bind(lock_done); @@ -1853,11 +1869,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ rt_call(native_func); - __ bind(native_return); - - intptr_t return_pc = (intptr_t) __ pc(); - oop_maps->add_gc_map(return_pc - start, map); - // Verify or restore cpu control state after JNI call __ restore_cpu_control_state_after_jni(rscratch1, rscratch2); @@ -1916,6 +1927,18 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ stlrw(rscratch1, rscratch2); __ bind(after_transition); + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + // Check preemption for Object.wait() + __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ cbz(rscratch1, native_return); + __ str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ br(rscratch1); + __ bind(native_return); + + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + } + Label reguard; Label reguard_done; __ ldrb(rscratch1, Address(rthread, JavaThread::stack_guard_state_offset())); @@ -1939,7 +1962,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Simple recursive lock? __ ldr(rscratch1, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); __ cbnz(rscratch1, not_recursive); - __ decrement(Address(rthread, JavaThread::held_monitor_count_offset())); + __ dec_held_monitor_count(rscratch1); __ b(done); } @@ -1962,11 +1985,10 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label count; __ cmpxchg_obj_header(r0, old_hdr, obj_reg, rscratch1, count, &slow_path_unlock); __ bind(count); - __ decrement(Address(rthread, JavaThread::held_monitor_count_offset())); + __ dec_held_monitor_count(rscratch1); } else { assert(LockingMode == LM_LIGHTWEIGHT, ""); __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); - __ decrement(Address(rthread, JavaThread::held_monitor_count_offset())); } // slow path re-enters here @@ -2033,8 +2055,14 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ mov(c_rarg1, lock_reg); __ mov(c_rarg2, rthread); - // Not a leaf but we have last_Java_frame setup as we want + // Not a leaf but we have last_Java_frame setup as we want. + // We don't want to unmount in case of contention since that would complicate preserving + // the arguments that had already been marshalled into the native convention. So we force + // the freeze slow path to find this native wrapper frame (see recurse_freeze_native_frame()) + // and pin the vthread. Otherwise the fast path won't find it since we don't walk the stack. + __ push_cont_fastpath(); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), 3); + __ pop_cont_fastpath(); restore_args(masm, total_c_args, c_arg, out_regs); #ifdef ASSERT @@ -2575,6 +2603,10 @@ uint SharedRuntime::out_preserve_stack_slots() { } +VMReg SharedRuntime::thread_register() { + return rthread->as_VMReg(); +} + //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, diff --git a/src/hotspot/cpu/aarch64/stackChunkFrameStream_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/stackChunkFrameStream_aarch64.inline.hpp index 7c5cf63e382c8..8a221f1377268 100644 --- a/src/hotspot/cpu/aarch64/stackChunkFrameStream_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/stackChunkFrameStream_aarch64.inline.hpp @@ -116,6 +116,7 @@ inline int StackChunkFrameStream::interpreter_frame_num_oops() const f.interpreted_frame_oop_map(&mask); return mask.num_oops() + 1 // for the mirror oop + + (f.interpreter_frame_method()->is_native() ? 1 : 0) // temp oop slot + pointer_delta_as_int((intptr_t*)f.interpreter_frame_monitor_begin(), (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); } diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 26462eed7ded5..de5df5c1af156 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -7466,6 +7466,37 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_cont_preempt_stub() { + if (!Continuations::enabled()) return nullptr; + StubCodeMark mark(this, "StubRoutines","Continuation preempt stub"); + address start = __ pc(); + + __ reset_last_Java_frame(true); + + // Set sp to enterSpecial frame, i.e. remove all frames copied into the heap. + __ ldr(rscratch2, Address(rthread, JavaThread::cont_entry_offset())); + __ mov(sp, rscratch2); + + Label preemption_cancelled; + __ ldrb(rscratch1, Address(rthread, JavaThread::preemption_cancelled_offset())); + __ cbnz(rscratch1, preemption_cancelled); + + // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. + SharedRuntime::continuation_enter_cleanup(_masm); + __ leave(); + __ ret(lr); + + // We acquired the monitor after freezing the frames so call thaw to continue execution. + __ bind(preemption_cancelled); + __ strb(zr, Address(rthread, JavaThread::preemption_cancelled_offset())); + __ lea(rfp, Address(sp, checked_cast(ContinuationEntry::size()))); + __ lea(rscratch1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); + __ ldr(rscratch1, Address(rscratch1)); + __ br(rscratch1); + + return start; + } + // In sun.security.util.math.intpoly.IntegerPolynomial1305, integers // are represented as long[5], with BITS_PER_LIMB = 26. // Pack five 26-bit limbs into three 64-bit registers. @@ -8620,6 +8651,7 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_thaw = generate_cont_thaw(); StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); + StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); } void generate_final_stubs() { diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 9894841e933d8..8eefcba7ba220 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -607,6 +607,40 @@ address TemplateInterpreterGenerator::generate_safept_entry_for( return entry; } +address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { + if (!Continuations::enabled()) return nullptr; + address start = __ pc(); + + __ restore_bcp(); + __ restore_locals(); + + // Restore constant pool cache + __ ldr(rcpool, Address(rfp, frame::interpreter_frame_cache_offset * wordSize)); + + // Restore Java expression stack pointer + __ ldr(rscratch1, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ lea(esp, Address(rfp, rscratch1, Address::lsl(Interpreter::logStackElementSize))); + // and NULL it as marker that esp is now tos until next java call + __ str(zr, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize)); + + // Restore machine SP + __ ldr(rscratch1, Address(rfp, frame::interpreter_frame_extended_sp_offset * wordSize)); + __ lea(sp, Address(rfp, rscratch1, Address::lsl(LogBytesPerWord))); + + // Restore method + __ ldr(rmethod, Address(rfp, frame::interpreter_frame_method_offset * wordSize)); + + // Restore dispatch + uint64_t offset; + __ adrp(rdispatch, ExternalAddress((address)Interpreter::dispatch_table()), offset); + __ add(rdispatch, rdispatch, offset); + + __ ret(lr); + + return start; +} + + // Helpers for commoning out cases in the various type of method entries. // @@ -1314,6 +1348,9 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // result handler is in r0 // set result handler __ mov(result_handler, r0); + // Save it in the frame in case of preemption; we cannot rely on callee saved registers. + __ str(r0, Address(rfp, frame::interpreter_frame_result_handler_offset * wordSize)); + // pass mirror handle if static call { Label L; @@ -1349,9 +1386,10 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // pass JNIEnv __ add(c_rarg0, rthread, in_bytes(JavaThread::jni_environment_offset())); - // Set the last Java PC in the frame anchor to be the return address from - // the call to the native method: this will allow the debugger to - // generate an accurate stack trace. + // It is enough that the pc() points into the right code + // segment. It does not have to be the correct return pc. + // For convenience we use the pc we want to resume to in + // case of preemption on Object.wait. Label native_return; __ set_last_Java_frame(esp, rfp, native_return, rscratch1); @@ -1372,9 +1410,13 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); + __ push_cont_fastpath(); + // Call the native method. __ blr(r10); - __ bind(native_return); + + __ pop_cont_fastpath(); + __ get_method(rmethod); // result potentially in r0 or v0 @@ -1432,6 +1474,23 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); + if (LockingMode != LM_LEGACY) { + // Check preemption for Object.wait() + Label not_preempted; + __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ cbz(rscratch1, not_preempted); + __ str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ br(rscratch1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + // reload result_handler + __ ldr(result_handler, Address(rfp, frame::interpreter_frame_result_handler_offset*wordSize)); + __ bind(not_preempted); + } else { + // any pc will do so just use this one for LM_LEGACY to keep code together. + __ bind(native_return); + } + // reset_last_Java_frame __ reset_last_Java_frame(true); diff --git a/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp b/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp index b5117dedc424e..bae882ea93d93 100644 --- a/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp +++ b/src/hotspot/cpu/arm/c1_Runtime1_arm.cpp @@ -251,6 +251,10 @@ void StubAssembler::restore_live_registers_without_return() { void Runtime1::initialize_pd() { } +uint Runtime1::runtime_blob_current_thread_offset(frame f) { + Unimplemented(); + return 0; +} OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { OopMap* oop_map = save_live_registers(sasm); diff --git a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp index 29af5681a6785..fb5883181700b 100644 --- a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp @@ -48,6 +48,10 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { Unimplemented(); } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + Unimplemented(); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { Unimplemented(); } @@ -83,6 +87,15 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) { Unimplemented(); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + Unimplemented(); +} + +inline intptr_t* ThawBase::push_cleanup_continuation() { + Unimplemented(); + return nullptr; +} + template inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { Unimplemented(); diff --git a/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp b/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp index 2f96ccaf322df..445a013b3e5c8 100644 --- a/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp @@ -35,6 +35,10 @@ static inline intptr_t** link_address(const frame& f) { return nullptr; } +static inline void patch_return_pc_with_preempt_stub(frame& f) { + Unimplemented(); +} + inline int ContinuationHelper::frame_align_words(int size) { Unimplemented(); return 0; @@ -62,11 +66,11 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, Unimplemented(); } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { Unimplemented(); } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { Unimplemented(); return false; diff --git a/src/hotspot/cpu/arm/frame_arm.cpp b/src/hotspot/cpu/arm/frame_arm.cpp index 6221191e92634..13a5c471c6fea 100644 --- a/src/hotspot/cpu/arm/frame_arm.cpp +++ b/src/hotspot/cpu/arm/frame_arm.cpp @@ -325,6 +325,11 @@ bool frame::upcall_stub_frame_is_first() const { return false; } +JavaThread** frame::saved_thread_address(const frame& f) { + Unimplemented(); + return nullptr; +} + //------------------------------------------------------------------------------ // frame::verify_deopt_original_pc // diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 7c1f3aafe7d52..a0cc2f4419923 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -1358,6 +1358,11 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } +VMReg SharedRuntime::thread_register() { + Unimplemented(); + return nullptr; +} + //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { ResourceMark rm; diff --git a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp index ec9d237e50da0..61f6026dbe373 100644 --- a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp @@ -459,6 +459,10 @@ address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, return entry; } +address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { + return nullptr; +} + // Helpers for commoning out cases in the various type of method entries. // diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index b2711ac43b0b0..d2584cc1af31d 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1816,6 +1816,7 @@ class Assembler : public AbstractAssembler { relocInfo::relocType rt = relocInfo::none); // helper function for b, bcxx + inline bool is_branch(address a); inline bool is_within_range_of_b(address a, address pc); inline bool is_within_range_of_bcxx(address a, address pc); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index b0eaaccf0d097..07de57babfa86 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -484,6 +484,12 @@ inline void Assembler::bclrl( int boint, int biint, int bhint, relocInfo::relocT inline void Assembler::bcctr( int boint, int biint, int bhint, relocInfo::relocType rt) { emit_data(BCCTR_OPCODE| bo(boint) | bi(biint) | bh(bhint) | aa(0) | lk(0), rt); } inline void Assembler::bcctrl(int boint, int biint, int bhint, relocInfo::relocType rt) { emit_data(BCCTR_OPCODE| bo(boint) | bi(biint) | bh(bhint) | aa(0) | lk(1), rt); } +inline bool Assembler::is_branch(address a) { + int32_t instr = *(int32_t*) a; + int op = inv_op_ppc(instr); + return op == b_op || op == bc_op; +} + // helper function for b inline bool Assembler::is_within_range_of_b(address a, address pc) { // Guard against illegal branch targets, e.g. -1 (see CompiledDirectCall and ad-file). diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index 0eddec09b634d..8cd21478d41a4 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -133,7 +133,9 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox } bind(done); - inc_held_monitor_count(Rmark /*tmp*/); + if (LockingMode == LM_LEGACY) { + inc_held_monitor_count(Rmark /*tmp*/); + } } @@ -179,7 +181,9 @@ void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rb // Done bind(done); - dec_held_monitor_count(Rmark /*tmp*/); + if (LockingMode == LM_LEGACY) { + dec_held_monitor_count(Rmark /*tmp*/); + } } diff --git a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp index 654626d66d812..47cafc45f33ea 100644 --- a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp @@ -64,7 +64,8 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, address return_pc = call_c(entry_point); - reset_last_Java_frame(); + // Last java sp can be null when the RT call was preempted + reset_last_Java_frame(false /* check_last_java_sp */); // Check for pending exceptions. { @@ -257,6 +258,11 @@ void Runtime1::initialize_pd() { frame_size_in_bytes = align_up(sp_offset, frame::alignment_in_bytes); } +uint Runtime1::runtime_blob_current_thread_offset(frame f) { + // On PPC virtual threads don't save the JavaThread* in their context (e.g. C1 stub frames). + ShouldNotCallThis(); + return 0; +} OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { // Make a frame and preserve the caller's caller-save registers. diff --git a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp index c1a98b734dab7..e49bc548ad453 100644 --- a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp @@ -71,6 +71,12 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { // nothing to do } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + // nothing to do + DEBUG_ONLY( intptr_t* lspp = (intptr_t*) &(f.get_ijava_state()->top_frame_sp); ) + assert(*lspp == f.unextended_sp() - f.fp(), "should be " INTPTR_FORMAT " usp:" INTPTR_FORMAT " fp:" INTPTR_FORMAT, *lspp, p2i(f.unextended_sp()), p2i(f.fp())); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { intptr_t* vfp = f.fp(); intptr_t* hfp = hf.fp(); @@ -350,6 +356,7 @@ inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { if (is_entry_frame) { callers_sp = _cont.entryFP(); } else { + assert(!Interpreter::contains(pc), "sp:" PTR_FORMAT " pc:" PTR_FORMAT, p2i(sp), p2i(pc)); CodeBlob* cb = CodeCache::find_blob_fast(pc); callers_sp = sp + cb->frame_size(); } @@ -480,8 +487,8 @@ inline frame ThawBase::new_entry_frame() { template frame ThawBase::new_stack_frame(const frame& hf, frame& caller, bool bottom) { assert(FKind::is_instance(hf), ""); - assert(is_aligned(caller.fp(), frame::frame_alignment), ""); - assert(is_aligned(caller.sp(), frame::frame_alignment), ""); + assert(is_aligned(caller.fp(), frame::frame_alignment), PTR_FORMAT, p2i(caller.fp())); + // caller.sp() can be unaligned. This is fixed below. if (FKind::interpreted) { // Note: we have to overlap with the caller, at least if it is interpreted, to match the // max_thawing_size calculation during freeze. See also comment above. @@ -510,7 +517,7 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& return f; } else { int fsize = FKind::size(hf); - int argsize = hf.compiled_frame_stack_argsize(); + int argsize = FKind::stack_argsize(hf); intptr_t* frame_sp = caller.sp() - fsize; if ((bottom && argsize > 0) || caller.is_interpreted_frame()) { @@ -543,12 +550,29 @@ inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, c // Keep top_frame_sp relativized. } +inline intptr_t* ThawBase::push_cleanup_continuation() { + frame enterSpecial = new_entry_frame(); + frame::common_abi* enterSpecial_abi = (frame::common_abi*)enterSpecial.sp(); + + enterSpecial_abi->lr = (intptr_t)ContinuationEntry::cleanup_pc(); + + log_develop_trace(continuations, preempt)("push_cleanup_continuation enterSpecial sp: " INTPTR_FORMAT " cleanup pc: " INTPTR_FORMAT, + p2i(enterSpecial_abi), + p2i(ContinuationEntry::cleanup_pc())); + + return enterSpecial.sp(); +} + inline void ThawBase::patch_pd(frame& f, const frame& caller) { patch_callee_link(caller, caller.fp()); // Prevent assertion if f gets deoptimized right away before it's fully initialized f.mark_not_fully_initialized(); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + assert(f.own_abi()->callers_sp == (uint64_t)caller_sp, "should have been fixed by patch_caller_links"); +} + // // Interpreter Calling Procedure on PPC // diff --git a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp index 528f1f8ad962a..ae5a02451e220 100644 --- a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp @@ -27,10 +27,18 @@ #include "runtime/continuationHelper.hpp" -template -static inline intptr_t** link_address(const frame& f) { - Unimplemented(); - return nullptr; +static inline void patch_return_pc_with_preempt_stub(frame& f) { + if (f.is_runtime_frame()) { + // Patch the pc of the now old last Java frame (we already set the anchor to enterSpecial) + // so that when target goes back to Java it will actually return to the preempt cleanup stub. + frame::common_abi* abi = (frame::common_abi*)f.sp(); + abi->lr = (uint64_t)StubRoutines::cont_preempt_stub(); + } else { + // The target will check for preemption once it returns to the interpreter + // or the native wrapper code and will manually jump to the preempt stub. + JavaThread *thread = JavaThread::current(); + thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub()); + } } inline int ContinuationHelper::frame_align_words(int size) { @@ -59,11 +67,11 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, // nothing to do } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { // nothing to do } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { intptr_t* sp = f.sp(); address pc = *(address*)(sp - frame::sender_sp_ret_address_offset()); diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index eb16af5e9db1b..935def11e1a98 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -243,6 +243,11 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { return fr; } +JavaThread** frame::saved_thread_address(const frame& f) { + // The current thread (JavaThread*) is never stored on the stack + return nullptr; +} + frame frame::sender_for_interpreter_frame(RegisterMap *map) const { // This is the sp before any possible extension (adapter/locals). intptr_t* unextended_sp = interpreter_frame_sender_sp(); diff --git a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp index 2beb7a84d341e..c390449dc33ff 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp @@ -53,7 +53,10 @@ inline void frame::setup(kind knd) { // The back link for compiled frames on the heap is not valid if (is_heap_frame()) { // fp for interpreted frames should have been derelativized and passed to the constructor - assert(is_compiled_frame(), ""); + assert(is_compiled_frame() + || is_native_frame() // native wrapper (nmethod) for j.l.Object::wait0 + || is_runtime_frame(), // e.g. Runtime1::monitorenter, SharedRuntime::complete_monitor_locking_C + "sp:" PTR_FORMAT " fp:" PTR_FORMAT " name:%s", p2i(_sp), p2i(_unextended_sp + _cb->frame_size()), _cb->name()); // The back link for compiled frames on the heap is invalid. _fp = _unextended_sp + _cb->frame_size(); } else { diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index dd0a597ec8ddd..4c39f88ce5176 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -49,6 +49,14 @@ class InterpreterMacroAssembler: public MacroAssembler { virtual void check_and_handle_popframe(Register scratch_reg); virtual void check_and_handle_earlyret(Register scratch_reg); + void call_VM_preemptable(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void restore_after_resume(Register fp); + // R22 and R31 are preserved when a vthread gets preempted in the interpreter. + // The interpreter already assumes that these registers are nonvolatile across native calls. + bool nonvolatile_accross_vthread_preemtion(Register r) const { + return r->is_nonvolatile() && ((r == R22) || (r == R31)); + } + // Base routine for all dispatches. void dispatch_base(TosState state, address* table); @@ -182,7 +190,7 @@ class InterpreterMacroAssembler: public MacroAssembler { // Special call VM versions that check for exceptions and forward exception // via short cut (not via expensive forward exception stub). void check_and_forward_exception(Register Rscratch1, Register Rscratch2); - void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true, Label* last_java_pc = nullptr); void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index aa77f0169ea1a..632eb97e85206 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -932,7 +932,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, // void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { if (LockingMode == LM_MONITOR) { - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); } else { // template code (for LM_LEGACY): // @@ -953,8 +953,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { const Register current_header = R9_ARG7; const Register tmp = R10_ARG8; - Label count_locking, done; - Label cas_failed, slow_case; + Label count_locking, done, slow_case, cas_failed; assert_different_registers(header, object_mark_addr, current_header, tmp); @@ -969,7 +968,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { if (LockingMode == LM_LIGHTWEIGHT) { lightweight_lock(monitor, object, header, tmp, slow_case); - b(count_locking); + b(done); } else if (LockingMode == LM_LEGACY) { // Load markWord from object into header. ld(header, oopDesc::mark_offset_in_bytes(), object); @@ -1035,12 +1034,15 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // None of the above fast optimizations worked so we have to get into the // slow case of monitor enter. bind(slow_case); - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); - b(done); + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); // } - align(32, 12); - bind(count_locking); - inc_held_monitor_count(current_header /*tmp*/); + + if (LockingMode == LM_LEGACY) { + b(done); + align(32, 12); + bind(count_locking); + inc_held_monitor_count(current_header /*tmp*/); + } bind(done); } } @@ -1137,7 +1139,9 @@ void InterpreterMacroAssembler::unlock_object(Register monitor) { bind(free_slot); li(R0, 0); std(R0, in_bytes(BasicObjectLock::obj_offset()), monitor); - dec_held_monitor_count(current_header /*tmp*/); + if (LockingMode == LM_LEGACY) { + dec_held_monitor_count(current_header /*tmp*/); + } bind(done); } } @@ -2133,10 +2137,10 @@ void InterpreterMacroAssembler::check_and_forward_exception(Register Rscratch1, bind(Ldone); } -void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { +void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions, Label* last_java_pc) { save_interpreter_state(R11_scratch1); - MacroAssembler::call_VM(oop_result, entry_point, false); + MacroAssembler::call_VM(oop_result, entry_point, false /*check_exceptions*/, last_java_pc); restore_interpreter_state(R11_scratch1, /*bcp_and_mdx_only*/ true); @@ -2155,6 +2159,74 @@ void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point call_VM(oop_result, entry_point, check_exceptions); } +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, address entry_point, + Register arg_1, bool check_exceptions) { + if (!Continuations::enabled()) { + call_VM(oop_result, entry_point, arg_1, check_exceptions); + return; + } + + Label resume_pc, not_preempted; + + DEBUG_ONLY(ld(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread)); + DEBUG_ONLY(cmpdi(CCR0, R0, 0)); + asm_assert_eq("Should not have alternate return address set"); + + // Preserve 2 registers + assert(nonvolatile_accross_vthread_preemtion(R31) && nonvolatile_accross_vthread_preemtion(R22), ""); + ld(R3_ARG1, _abi0(callers_sp), R1_SP); // load FP + std(R31, _ijava_state_neg(lresult), R3_ARG1); + std(R22, _ijava_state_neg(fresult), R3_ARG1); + + // We set resume_pc as last java pc. It will be saved if the vthread gets preempted. + // Later execution will continue right there. + mr_if_needed(R4_ARG2, arg_1); + push_cont_fastpath(); + call_VM(oop_result, entry_point, false /*check_exceptions*/, &resume_pc /* last_java_pc */); + pop_cont_fastpath(); + + // Jump to handler if the call was preempted + ld(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread); + cmpdi(CCR0, R0, 0); + beq(CCR0, not_preempted); + mtlr(R0); + li(R0, 0); + std(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread); + blr(); + + bind(resume_pc); // Location to resume execution + restore_after_resume(noreg /* fp */); + bind(not_preempted); +} + +void InterpreterMacroAssembler::restore_after_resume(Register fp) { + if (!Continuations::enabled()) return; + + const address resume_adapter = TemplateInterpreter::cont_resume_interpreter_adapter(); + add_const_optimized(R31, R29_TOC, MacroAssembler::offset_to_global_toc(resume_adapter)); + mtctr(R31); + bctrl(); + // Restore registers that are preserved across vthread preemption + assert(nonvolatile_accross_vthread_preemtion(R31) && nonvolatile_accross_vthread_preemtion(R22), ""); + ld(R3_ARG1, _abi0(callers_sp), R1_SP); // load FP + ld(R31, _ijava_state_neg(lresult), R3_ARG1); + ld(R22, _ijava_state_neg(fresult), R3_ARG1); +#ifdef ASSERT + // Assert FP is in R11_scratch1 (see generate_cont_resume_interpreter_adapter()) + { + Label ok; + ld(R12_scratch2, 0, R1_SP); // load fp + cmpd(CCR0, R12_scratch2, R11_scratch1); + beq(CCR0, ok); + stop(FILE_AND_LINE ": FP is expected in R11_scratch1"); + bind(ok); + } +#endif + if (fp != noreg && fp != R11_scratch1) { + mr(fp, R11_scratch1); + } +} + void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index c585e03fdf5f0..994521ba48fca 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -31,6 +31,7 @@ #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "memory/resourceArea.hpp" #include "nativeInst_ppc.hpp" #include "oops/compressedKlass.inline.hpp" @@ -115,7 +116,8 @@ void MacroAssembler::align_prefix() { // Issue instructions that calculate given TOC from global TOC. void MacroAssembler::calculate_address_from_global_toc(Register dst, address addr, bool hi16, bool lo16, - bool add_relocation, bool emit_dummy_addr) { + bool add_relocation, bool emit_dummy_addr, + bool add_addr_to_reloc) { int offset = -1; if (emit_dummy_addr) { offset = -128; // dummy address @@ -129,7 +131,10 @@ void MacroAssembler::calculate_address_from_global_toc(Register dst, address add if (lo16) { if (add_relocation) { // Relocate at the addi to avoid confusion with a load from the method's TOC. - relocate(internal_word_Relocation::spec(addr)); + RelocationHolder rh = add_addr_to_reloc ? + internal_word_Relocation::spec(addr) : + internal_word_Relocation::spec_for_immediate(); + relocate(rh); } addi(dst, dst, MacroAssembler::largeoffset_si16_si16_lo(offset)); } @@ -714,6 +719,7 @@ address MacroAssembler::get_dest_of_bxx64_patchable_at(address instruction_addr, } } +#ifdef ASSERT void MacroAssembler::clobber_volatile_gprs(Register excluded_register) { const int magic_number = 0x42; @@ -729,6 +735,37 @@ void MacroAssembler::clobber_volatile_gprs(Register excluded_register) { } } +void MacroAssembler::clobber_nonvolatile_registers() { + BLOCK_COMMENT("clobber nonvolatile registers {"); + static const Register regs[] = { + R14, + R15, + // don't zap R16_thread + R17, + R18, + R19, + R20, + R21, + R22, + R23, + R24, + R25, + R26, + R27, + R28, + // don't zap R29_TOC + R30, + R31 + }; + Register bad = regs[0]; + load_const_optimized(bad, 0xbad0101babe11111); + for (uint32_t i = 1; i < (sizeof(regs) / sizeof(Register)); i++) { + mr(regs[i], bad); + } + BLOCK_COMMENT("} clobber nonvolatile registers"); +} +#endif // ASSERT + void MacroAssembler::clobber_carg_stack_slots(Register tmp) { const int magic_number = 0x43; @@ -1288,13 +1325,14 @@ int MacroAssembler::ic_check(int end_alignment) { void MacroAssembler::call_VM_base(Register oop_result, Register last_java_sp, address entry_point, - bool check_exceptions) { + bool check_exceptions, + Label* last_java_pc) { BLOCK_COMMENT("call_VM {"); // Determine last_java_sp register. if (!last_java_sp->is_valid()) { last_java_sp = R1_SP; } - set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, R11_scratch1); + set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, R11_scratch1, last_java_pc); // ARG1 must hold thread address. mr(R3_ARG1, R16_thread); @@ -1323,8 +1361,8 @@ void MacroAssembler::call_VM_leaf_base(address entry_point) { BLOCK_COMMENT("} call_VM_leaf"); } -void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { - call_VM_base(oop_result, noreg, entry_point, check_exceptions); +void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions, Label* last_java_pc) { + call_VM_base(oop_result, noreg, entry_point, check_exceptions, last_java_pc); } void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, @@ -2625,15 +2663,15 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register // Handle existing monitor. bind(object_has_monitor); - // The object's monitor m is unlocked iff m->owner is null, - // otherwise m->owner may contain a thread or a stack address. - // Try to CAS m->owner from null to current thread. + // Try to CAS owner (no owner => current thread's _lock_id). addi(temp, displaced_header, in_bytes(ObjectMonitor::owner_offset()) - markWord::monitor_value); + Register thread_id = displaced_header; + ld(thread_id, in_bytes(JavaThread::lock_id_offset()), R16_thread); cmpxchgd(/*flag=*/flag, /*current_value=*/current_header, /*compare_value=*/(intptr_t)0, - /*exchange_value=*/R16_thread, + /*exchange_value=*/thread_id, /*where=*/temp, MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, MacroAssembler::cmpxchgx_hint_acquire_lock()); @@ -2643,7 +2681,7 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register beq(flag, success); // Check for recursive locking. - cmpd(flag, current_header, R16_thread); + cmpd(flag, current_header, thread_id); bne(flag, failure); // Current thread already owns the lock. Just increment recursions. @@ -2652,10 +2690,12 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register addi(recursions, recursions, 1); std(recursions, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), temp); - // flag == EQ indicates success, increment held monitor count + // flag == EQ indicates success, increment held monitor count if LM_LEGACY is enabled // flag == NE indicates failure bind(success); - inc_held_monitor_count(temp); + if (LockingMode == LM_LEGACY) { + inc_held_monitor_count(temp); + } #ifdef ASSERT // Check that unlocked label is reached with flag == EQ. Label flag_correct; @@ -2675,7 +2715,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe Register temp, Register displaced_header, Register current_header) { assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_unlock_lightweight"); assert_different_registers(oop, box, temp, displaced_header, current_header); - Label success, failure, object_has_monitor, notRecursive; + Label success, failure, object_has_monitor, not_recursive; if (LockingMode == LM_LEGACY) { // Find the lock address and load the displaced header from the stack. @@ -2721,7 +2761,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe ld(displaced_header, in_bytes(ObjectMonitor::recursions_offset()), current_header); addic_(displaced_header, displaced_header, -1); - blt(CCR0, notRecursive); // Not recursive if negative after decrement. + blt(CCR0, not_recursive); // Not recursive if negative after decrement. // Recursive unlock std(displaced_header, in_bytes(ObjectMonitor::recursions_offset()), current_header); @@ -2730,7 +2770,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe } b(success); - bind(notRecursive); + bind(not_recursive); // Set owner to null. // Release to satisfy the JMM @@ -2760,10 +2800,12 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe std(current_header, in_bytes(JavaThread::unlocked_inflated_monitor_offset()), R16_thread); b(failure); // flag == NE - // flag == EQ indicates success, decrement held monitor count + // flag == EQ indicates success, decrement held monitor count if LM_LEGACY is enabled // flag == NE indicates failure bind(success); - dec_held_monitor_count(temp); + if (LockingMode == LM_LEGACY) { + dec_held_monitor_count(temp); + } #ifdef ASSERT // Check that unlocked label is reached with flag == EQ. Label flag_correct; @@ -2782,6 +2824,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister flag, Register obj, Register box, Register tmp1, Register tmp2, Register tmp3) { assert_different_registers(obj, box, tmp1, tmp2, tmp3); + assert(UseObjectMonitorTable || tmp3 == noreg, "tmp3 not needed"); assert(flag == CCR0, "bad condition register"); // Handle inflated monitor. @@ -2804,8 +2847,7 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla bne(CCR0, slow_path); } - const Register mark = tmp1; - const Register t = tmp3; // Usage of R0 allowed! + Register mark = tmp1; { // Lightweight locking @@ -2823,15 +2865,15 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla // when the lock stack is empty because of the _bad_oop_sentinel field. // Check if recursive. - subi(t, top, oopSize); - ldx(t, R16_thread, t); - cmpd(CCR0, obj, t); + subi(R0, top, oopSize); + ldx(R0, R16_thread, R0); + cmpd(CCR0, obj, R0); beq(CCR0, push); // Check for monitor (0b10) or locked (0b00). ld(mark, oopDesc::mark_offset_in_bytes(), obj); - andi_(t, mark, markWord::lock_mask_in_place); - cmpldi(CCR0, t, markWord::unlocked_value); + andi_(R0, mark, markWord::lock_mask_in_place); + cmpldi(CCR0, R0, markWord::unlocked_value); bgt(CCR0, inflated); bne(CCR0, slow_path); @@ -2854,13 +2896,15 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla // mark contains the tagged ObjectMonitor*. const uintptr_t monitor_tag = markWord::monitor_value; - const Register monitor = mark; + const Register monitor = UseObjectMonitorTable ? tmp1 : noreg; const Register owner_addr = tmp2; + const Register thread_id = UseObjectMonitorTable ? tmp3 : tmp1; Label monitor_locked; if (!UseObjectMonitorTable) { // Compute owner address. addi(owner_addr, mark, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag); + mark = noreg; } else { Label monitor_found; Register cache_addr = tmp2; @@ -2870,8 +2914,8 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla const int num_unrolled = 2; for (int i = 0; i < num_unrolled; i++) { - ld(tmp3, 0, cache_addr); - cmpd(CCR0, tmp3, obj); + ld(R0, 0, cache_addr); + cmpd(CCR0, R0, obj); beq(CCR0, monitor_found); addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference())); } @@ -2882,13 +2926,13 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla bind(loop); // Check for match. - ld(tmp3, 0, cache_addr); - cmpd(CCR0, tmp3, obj); + ld(R0, 0, cache_addr); + cmpd(CCR0, R0, obj); beq(CCR0, monitor_found); // Search until null encountered, guaranteed _null_sentinel at end. addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference())); - cmpdi(CCR1, tmp3, 0); + cmpdi(CCR1, R0, 0); bne(CCR1, loop); // Cache Miss, CCR0.NE set from cmp above b(slow_path); @@ -2900,18 +2944,20 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset())); } - // CAS owner (null => current thread). + // Try to CAS owner (no owner => current thread's _lock_id). + assert_different_registers(thread_id, monitor, owner_addr, box, R0); + ld(thread_id, in_bytes(JavaThread::lock_id_offset()), R16_thread); cmpxchgd(/*flag=*/CCR0, - /*current_value=*/t, + /*current_value=*/R0, /*compare_value=*/(intptr_t)0, - /*exchange_value=*/R16_thread, + /*exchange_value=*/thread_id, /*where=*/owner_addr, MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, MacroAssembler::cmpxchgx_hint_acquire_lock()); beq(CCR0, monitor_locked); // Check if recursive. - cmpd(CCR0, t, R16_thread); + cmpd(CCR0, R0, thread_id); bne(CCR0, slow_path); // Recursive. @@ -2934,7 +2980,6 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla } bind(locked); - inc_held_monitor_count(tmp1); #ifdef ASSERT // Check that locked label is reached with flag == EQ. @@ -3109,7 +3154,6 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f } bind(unlocked); - dec_held_monitor_count(t); #ifdef ASSERT // Check that unlocked label is reached with flag == EQ. @@ -3191,9 +3235,11 @@ void MacroAssembler::set_last_Java_frame(Register last_Java_sp, Register last_Ja std(last_Java_sp, in_bytes(JavaThread::last_Java_sp_offset()), R16_thread); } -void MacroAssembler::reset_last_Java_frame(void) { - asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), - R16_thread, "SP was not set, still zero"); +void MacroAssembler::reset_last_Java_frame(bool check_last_java_sp) { + if (check_last_java_sp) { + asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), + R16_thread, "SP was not set, still zero"); + } BLOCK_COMMENT("reset_last_Java_frame {"); li(R0, 0); @@ -3206,14 +3252,14 @@ void MacroAssembler::reset_last_Java_frame(void) { BLOCK_COMMENT("} reset_last_Java_frame"); } -void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1) { +void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, Label* jpc) { assert_different_registers(sp, tmp1); - // sp points to a TOP_IJAVA_FRAME, retrieve frame's PC via - // TOP_IJAVA_FRAME_ABI. - // FIXME: assert that we really have a TOP_IJAVA_FRAME here! - address entry = pc(); - load_const_optimized(tmp1, entry); + if (jpc == nullptr || jpc->is_bound()) { + load_const_optimized(tmp1, jpc == nullptr ? pc() : target(*jpc)); + } else { + load_const(tmp1, *jpc, R12_scratch2); + } set_last_Java_frame(/*sp=*/sp, /*pc=*/tmp1); } @@ -4530,9 +4576,9 @@ void MacroAssembler::asm_assert(bool check_equal, const char *msg) { #endif } +#ifdef ASSERT void MacroAssembler::asm_assert_mems_zero(bool check_equal, int size, int mem_offset, Register mem_base, const char* msg) { -#ifdef ASSERT switch (size) { case 4: lwz(R0, mem_offset, mem_base); @@ -4546,8 +4592,8 @@ void MacroAssembler::asm_assert_mems_zero(bool check_equal, int size, int mem_of ShouldNotReachHere(); } asm_assert(check_equal, msg); -#endif // ASSERT } +#endif // ASSERT void MacroAssembler::verify_coop(Register coop, const char* msg) { if (!VerifyOops) { return; } @@ -4691,6 +4737,8 @@ void MacroAssembler::cache_wbsync(bool is_presync) { } void MacroAssembler::push_cont_fastpath() { + if (!Continuations::enabled()) return; + Label done; ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); cmpld(CCR0, R1_SP, R0); @@ -4700,6 +4748,8 @@ void MacroAssembler::push_cont_fastpath() { } void MacroAssembler::pop_cont_fastpath() { + if (!Continuations::enabled()) return; + Label done; ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); cmpld(CCR0, R1_SP, R0); @@ -4711,6 +4761,7 @@ void MacroAssembler::pop_cont_fastpath() { // Note: Must preserve CCR0 EQ (invariant). void MacroAssembler::inc_held_monitor_count(Register tmp) { + assert(LockingMode == LM_LEGACY, ""); ld(tmp, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); #ifdef ASSERT Label ok; @@ -4726,6 +4777,7 @@ void MacroAssembler::inc_held_monitor_count(Register tmp) { // Note: Must preserve CCR0 EQ (invariant). void MacroAssembler::dec_held_monitor_count(Register tmp) { + assert(LockingMode == LM_LEGACY, ""); ld(tmp, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); #ifdef ASSERT Label ok; diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index 078efd509ca9d..237024fa05eaf 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -115,7 +115,13 @@ class MacroAssembler: public Assembler { // Global TOC. void calculate_address_from_global_toc(Register dst, address addr, bool hi16 = true, bool lo16 = true, - bool add_relocation = true, bool emit_dummy_addr = false); + bool add_relocation = true, bool emit_dummy_addr = false, + bool add_addr_to_reloc = true); + void calculate_address_from_global_toc(Register dst, Label& addr, + bool hi16 = true, bool lo16 = true, + bool add_relocation = true, bool emit_dummy_addr = false) { + calculate_address_from_global_toc(dst, target(addr), hi16, lo16, add_relocation, emit_dummy_addr, false); + } inline void calculate_address_from_global_toc_hi16only(Register dst, address addr) { calculate_address_from_global_toc(dst, addr, true, false); }; @@ -284,7 +290,10 @@ class MacroAssembler: public Assembler { // Clobbers all volatile, (non-floating-point) general-purpose registers for debugging purposes. // This is especially useful for making calls to the JRT in places in which this hasn't been done before; // e.g. with the introduction of LRBs (load reference barriers) for concurrent garbage collection. - void clobber_volatile_gprs(Register excluded_register = noreg); + void clobber_volatile_gprs(Register excluded_register = noreg) NOT_DEBUG_RETURN; + // Load bad values into registers that are nonvolatile according to the ABI except R16_thread and R29_TOC. + // This is done after vthread preemption and before vthread resume. + void clobber_nonvolatile_registers() NOT_DEBUG_RETURN; void clobber_carg_stack_slots(Register tmp); void save_nonvolatile_gprs( Register dst_base, int offset); @@ -398,7 +407,8 @@ class MacroAssembler: public Assembler { // the entry point address entry_point, // flag which indicates if exception should be checked - bool check_exception = true + bool check_exception = true, + Label* last_java_pc = nullptr ); // Support for VM calls. This is the base routine called by the @@ -411,7 +421,7 @@ class MacroAssembler: public Assembler { // Call into the VM. // Passes the thread pointer (in R3_ARG1) as a prepended argument. // Makes sure oop return values are visible to the GC. - void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true, Label* last_java_pc = nullptr); void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg3, bool check_exceptions = true); @@ -695,8 +705,8 @@ class MacroAssembler: public Assembler { // Support for last Java frame (but use call_VM instead where possible): // access R16_thread->last_Java_sp. void set_last_Java_frame(Register last_java_sp, Register last_Java_pc); - void reset_last_Java_frame(void); - void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1); + void reset_last_Java_frame(bool check_last_java_sp = true); + void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1, Label* jpc = nullptr); // Read vm result from thread: oop_result = R16_thread->result; void get_vm_result (Register oop_result); @@ -912,7 +922,7 @@ class MacroAssembler: public Assembler { private: void asm_assert_mems_zero(bool check_equal, int size, int mem_offset, Register mem_base, - const char* msg); + const char* msg) NOT_DEBUG_RETURN; public: diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp index e9c6fd38f4593..f98ad592388e7 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp @@ -191,8 +191,18 @@ inline void MacroAssembler::set_oop(AddressLiteral obj_addr, Register d) { } inline void MacroAssembler::pd_patch_instruction(address branch, address target, const char* file, int line) { - jint& stub_inst = *(jint*) branch; - stub_inst = patched_branch(target - branch, stub_inst, 0); + if (is_branch(branch)) { + jint& stub_inst = *(jint*) branch; + stub_inst = patched_branch(target - branch, stub_inst, 0); + } else if (is_calculate_address_from_global_toc_at(branch + BytesPerInstWord, branch)) { + const address inst1_addr = branch; + const address inst2_addr = branch + BytesPerInstWord; + patch_calculate_address_from_global_toc_at(inst2_addr, inst1_addr, target); + } else if (is_load_const_at(branch)) { + patch_const(branch, (long)target); + } else { + assert(false, "instruction at " PTR_FORMAT " not recognized", p2i(branch)); + } } // Relocation of conditional far branches. diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp index f61328fc7360c..dbc43c45e09b0 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp @@ -205,12 +205,14 @@ intptr_t NativeMovConstReg::data() const { // Therefore we use raw decoding. if (CompressedOops::is_null(no)) return 0; return cast_from_oop(CompressedOops::decode_raw(no)); - } else { - assert(MacroAssembler::is_load_const_from_method_toc_at(addr), "must be load_const_from_pool"); - + } else if (MacroAssembler::is_load_const_from_method_toc_at(addr)) { address ctable = cb->content_begin(); int offset = MacroAssembler::get_offset_of_load_const_from_method_toc_at(addr); return *(intptr_t *)(ctable + offset); + } else { + assert(MacroAssembler::is_calculate_address_from_global_toc_at(addr, addr - BytesPerInstWord), + "must be calculate_address_from_global_toc"); + return (intptr_t) MacroAssembler::get_address_of_calculate_address_from_global_toc_at(addr, addr - BytesPerInstWord); } } diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 142eaea26e4ef..cfed8674e898e 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -12108,15 +12108,31 @@ instruct cmpFastUnlock(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp ins_pipe(pipe_class_compare); %} -instruct cmpFastLockLightweight(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR1 cr1) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); +instruct cmpFastLockLightweight(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2) %{ + predicate(LockingMode == LM_LIGHTWEIGHT && !UseObjectMonitorTable); match(Set crx (FastLock oop box)); - effect(TEMP tmp1, TEMP tmp2, KILL cr1); + effect(TEMP tmp1, TEMP tmp2); format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2" %} ins_encode %{ __ fast_lock_lightweight($crx$$CondRegister, $oop$$Register, $box$$Register, - $tmp1$$Register, $tmp2$$Register, /*tmp3*/ R0); + $tmp1$$Register, $tmp2$$Register, noreg /*tmp3*/); + // If locking was successful, crx should indicate 'EQ'. + // The compiler generates a branch to the runtime call to + // _complete_monitor_locking_Java for the case where crx is 'NE'. + %} + ins_pipe(pipe_class_compare); +%} + +instruct cmpFastLockMonitorTable(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, flagsRegCR1 cr1) %{ + predicate(LockingMode == LM_LIGHTWEIGHT && UseObjectMonitorTable); + match(Set crx (FastLock oop box)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr1); + + format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2, $tmp3" %} + ins_encode %{ + __ fast_lock_lightweight($crx$$CondRegister, $oop$$Register, $box$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); // If locking was successful, crx should indicate 'EQ'. // The compiler generates a branch to the runtime call to // _complete_monitor_locking_Java for the case where crx is 'NE'. diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index aa8ae6070b6a6..84102ef41e819 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1602,6 +1602,7 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, #ifdef ASSERT __ load_const_optimized(tmp2, ContinuationEntry::cookie_value()); __ stw(tmp2, in_bytes(ContinuationEntry::cookie_offset()), R1_SP); + __ std(tmp2, _abi0(cr), R1_SP); #endif //ASSERT __ li(zero, 0); @@ -1645,6 +1646,10 @@ static void continuation_enter_cleanup(MacroAssembler* masm) { __ ld_ptr(tmp1, JavaThread::cont_entry_offset(), R16_thread); __ cmpd(CCR0, R1_SP, tmp1); __ asm_assert_eq(FILE_AND_LINE ": incorrect R1_SP"); + __ load_const_optimized(tmp1, ContinuationEntry::cookie_value()); + __ ld(tmp2, _abi0(cr), R1_SP); + __ cmpd(CCR0, tmp1, tmp2); + __ asm_assert_eq(FILE_AND_LINE ": cookie not found"); #endif __ ld_ptr(tmp1, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP); @@ -1853,6 +1858,7 @@ static void gen_continuation_enter(MacroAssembler* masm, // --- Thawing path __ bind(L_thaw); + ContinuationEntry::_thaw_call_pc_offset = __ pc() - start; __ add_const_optimized(R0, R29_TOC, MacroAssembler::offset_to_global_toc(StubRoutines::cont_thaw())); __ mtctr(R0); __ bctrl(); @@ -1863,6 +1869,7 @@ static void gen_continuation_enter(MacroAssembler* masm, // --- Normal exit (resolve/thawing) __ bind(L_exit); + ContinuationEntry::_cleanup_offset = __ pc() - start; continuation_enter_cleanup(masm); // Pop frame and return @@ -1970,6 +1977,10 @@ static void gen_continuation_yield(MacroAssembler* masm, __ bctr(); } +void SharedRuntime::continuation_enter_cleanup(MacroAssembler* masm) { + ::continuation_enter_cleanup(masm); +} + // --------------------------------------------------------------------------- // Generate a native wrapper for a given method. The method takes arguments // in the Java compiled code convention, marshals them to the native @@ -2190,9 +2201,9 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, intptr_t start_pc = (intptr_t)__ pc(); intptr_t vep_start_pc; intptr_t frame_done_pc; - intptr_t oopmap_pc; Label handle_pending_exception; + Label last_java_pc; Register r_callers_sp = R21; Register r_temp_1 = R22; @@ -2201,7 +2212,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, Register r_temp_4 = R25; Register r_temp_5 = R26; Register r_temp_6 = R27; - Register r_return_pc = R28; + Register r_last_java_pc = R28; Register r_carg1_jnienv = noreg; Register r_carg2_classorobject = noreg; @@ -2363,15 +2374,9 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // We MUST NOT touch any outgoing regs from this point on. // So if we must call out we must push a new frame. - // Get current pc for oopmap, and load it patchable relative to global toc. - oopmap_pc = (intptr_t) __ pc(); - __ calculate_address_from_global_toc(r_return_pc, (address)oopmap_pc, true, true, true, true); - - // We use the same pc/oopMap repeatedly when we call out. - oop_maps->add_gc_map(oopmap_pc - start_pc, oop_map); - - // r_return_pc now has the pc loaded that we will use when we finally call - // to native. + // The last java pc will also be used as resume pc if this is the wrapper for wait0. + // For this purpose the precise location matters but not for oopmap lookup. + __ calculate_address_from_global_toc(r_last_java_pc, last_java_pc, true, true, true, true); // Make sure that thread is non-volatile; it crosses a bunch of VM calls below. assert(R16_thread->is_nonvolatile(), "thread must be in non-volatile register"); @@ -2399,7 +2404,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Try fastpath for locking. if (LockingMode == LM_LIGHTWEIGHT) { // fast_lock kills r_temp_1, r_temp_2, r_temp_3. - __ compiler_fast_lock_lightweight_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); + Register r_temp_3_or_noreg = UseObjectMonitorTable ? r_temp_3 : noreg; + __ compiler_fast_lock_lightweight_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3_or_noreg); } else { // fast_lock kills r_temp_1, r_temp_2, r_temp_3. __ compiler_fast_lock_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); @@ -2416,9 +2422,14 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, RegisterSaver::push_frame_and_save_argument_registers(masm, R12_scratch2, frame_size, total_c_args, out_regs); // Do the call. - __ set_last_Java_frame(R11_scratch1, r_return_pc); - assert(r_return_pc->is_nonvolatile(), "expecting return pc to be in non-volatile register"); + __ set_last_Java_frame(R11_scratch1, r_last_java_pc); + assert(r_last_java_pc->is_nonvolatile(), "r_last_java_pc needs to be preserved accross complete_monitor_locking_C call"); + // The following call will not be preempted. + // push_cont_fastpath forces freeze slow path in case we try to preempt where we will pin the + // vthread to the carrier (see FreezeBase::recurse_freeze_native_frame()). + __ push_cont_fastpath(); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), r_oop, r_box, R16_thread); + __ pop_cont_fastpath(); __ reset_last_Java_frame(); RegisterSaver::restore_argument_registers_and_pop_frame(masm, frame_size, total_c_args, out_regs); @@ -2429,8 +2440,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ bind(locked); } - // Use that pc we placed in r_return_pc a while back as the current frame anchor. - __ set_last_Java_frame(R1_SP, r_return_pc); + __ set_last_Java_frame(R1_SP, r_last_java_pc); // Publish thread state // -------------------------------------------------------------------------- @@ -2490,8 +2500,6 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, break; } - Label after_transition; - // Publish thread state // -------------------------------------------------------------------------- @@ -2566,7 +2574,23 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ lwsync(); // Acquire safepoint and suspend state, release thread state. // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); __ stw(R0, thread_(thread_state)); - __ bind(after_transition); + + // Check preemption for Object.wait() + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + Label not_preempted; + __ ld(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread); + __ cmpdi(CCR0, R0, 0); + __ beq(CCR0, not_preempted); + __ mtlr(R0); + __ li(R0, 0); + __ std(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread); + __ blr(); + __ bind(not_preempted); + } + __ bind(last_java_pc); + // We use the same pc/oopMap repeatedly when we call out above. + intptr_t oopmap_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(oopmap_pc - start_pc, oop_map); } // Reguard any pages if necessary. @@ -2648,7 +2672,9 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Clear "last Java frame" SP and PC. // -------------------------------------------------------------------------- - __ reset_last_Java_frame(); + // Last java frame won't be set if we're resuming after preemption + bool maybe_preempted = LockingMode != LM_LEGACY && method->is_object_wait0(); + __ reset_last_Java_frame(!maybe_preempted /* check_last_java_sp */); // Unbox oop result, e.g. JNIHandles::resolve value. // -------------------------------------------------------------------------- @@ -2733,6 +2759,12 @@ uint SharedRuntime::out_preserve_stack_slots() { #endif } +VMReg SharedRuntime::thread_register() { + // On PPC virtual threads don't save the JavaThread* in their context (e.g. C1 stub frames). + ShouldNotCallThis(); + return nullptr; +} + #if defined(COMPILER1) || defined(COMPILER2) // Frame generation for deopt and uncommon trap blobs. static void push_skeleton_frame(MacroAssembler* masm, bool deopt, diff --git a/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp b/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp index cb4af1a3ff77b..07f1c9c1c6f16 100644 --- a/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp @@ -184,8 +184,9 @@ inline int StackChunkFrameStream::interpreter_frame_num_oops() const f.interpreted_frame_oop_map(&mask); return mask.num_oops() + 1 // for the mirror oop - + ((intptr_t*)f.interpreter_frame_monitor_begin() - - (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); + + (f.interpreter_frame_method()->is_native() ? 1 : 0) // temp oop slot + + pointer_delta_as_int((intptr_t*)f.interpreter_frame_monitor_begin(), + (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); } template<> diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index b3ace8898ad63..9f2b668b9c834 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -4483,6 +4483,10 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { address start = __ pc(); + if (kind == Continuation::thaw_top) { + __ clobber_nonvolatile_registers(); // Except R16_thread and R29_TOC + } + if (return_barrier) { __ mr(nvtmp, R3_RET); __ fmr(nvftmp, F1_RET); // preserve possible return value from a method returning to the return barrier DEBUG_ONLY(__ ld_ptr(tmp1, _abi0(callers_sp), R1_SP);) @@ -4571,6 +4575,41 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { return generate_cont_thaw("Cont thaw return barrier exception", Continuation::thaw_return_barrier_exception); } + address generate_cont_preempt_stub() { + if (!Continuations::enabled()) return nullptr; + StubCodeMark mark(this, "StubRoutines","Continuation preempt stub"); + address start = __ pc(); + + __ clobber_nonvolatile_registers(); // Except R16_thread and R29_TOC + + __ reset_last_Java_frame(false /*check_last_java_sp*/); + + // Set sp to enterSpecial frame, i.e. remove all frames copied into the heap. + __ ld_ptr(R1_SP, JavaThread::cont_entry_offset(), R16_thread); + + Label preemption_cancelled; + __ lbz(R11_scratch1, in_bytes(JavaThread::preemption_cancelled_offset()), R16_thread); + __ cmpwi(CCR0, R11_scratch1, 0); + __ bne(CCR0, preemption_cancelled); + + // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. + SharedRuntime::continuation_enter_cleanup(_masm); + __ pop_frame(); + __ restore_LR(R11_scratch1); + __ blr(); + + // We acquired the monitor after freezing the frames so call thaw to continue execution. + __ bind(preemption_cancelled); + __ li(R11_scratch1, 0); // false + __ stb(R11_scratch1, in_bytes(JavaThread::preemption_cancelled_offset()), R16_thread); + int simm16_offs = __ load_const_optimized(R11_scratch1, ContinuationEntry::thaw_call_pc_address(), R0, true); + __ ld(R11_scratch1, simm16_offs, R11_scratch1); + __ mtctr(R11_scratch1); + __ bctr(); + + return start; + } + // exception handler for upcall stubs address generate_upcall_stub_exception_handler() { StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); @@ -4646,6 +4685,7 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { StubRoutines::_cont_thaw = generate_cont_thaw(); StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); + StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); } void generate_final_stubs() { diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index cf3dd4cbd34c0..e320349583ddc 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -696,6 +696,17 @@ address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, return entry; } +address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { + if (!Continuations::enabled()) return nullptr; + address start = __ pc(); + + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R12_scratch2); + __ restore_interpreter_state(R11_scratch1, false, true /*restore_top_frame_sp*/); + __ blr(); + + return start; +} + // Helpers for commoning out cases in the various type of method entries. // Increment invocation count & check for overflow. @@ -1197,7 +1208,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { const Register signature_handler_fd = R11_scratch1; const Register pending_exception = R0; const Register result_handler_addr = R31; - const Register native_method_fd = R11_scratch1; + const Register native_method_fd = R12_scratch2; // preferred in MacroAssembler::branch_to const Register access_flags = R22_tmp2; const Register active_handles = R11_scratch1; // R26_monitor saved to state. const Register sync_state = R12_scratch2; @@ -1211,10 +1222,6 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { Label exception_return_sync_check; Label stack_overflow_return; - // Generate new interpreter state and jump to stack_overflow_return in case of - // a stack overflow. - //generate_compute_interpreter_state(stack_overflow_return); - Register size_of_parameters = R22_tmp2; generate_fixed_frame(true, size_of_parameters, noreg /* unused */); @@ -1253,8 +1260,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // access_flags = method->access_flags(); // Load access flags. - assert(access_flags->is_nonvolatile(), - "access_flags must be in a non-volatile register"); + assert(__ nonvolatile_accross_vthread_preemtion(access_flags), + "access_flags not preserved"); // Type check. assert(4 == sizeof(AccessFlags), "unexpected field size"); __ lwz(access_flags, method_(access_flags)); @@ -1315,8 +1322,12 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // convenient and the slow signature handler can use this same frame // anchor. + bool support_vthread_preemption = Continuations::enabled() && LockingMode != LM_LEGACY; + // We have a TOP_IJAVA_FRAME here, which belongs to us. - __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R12_scratch2/*tmp*/); + Label last_java_pc; + Label *resume_pc = support_vthread_preemption ? &last_java_pc : nullptr; + __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R3_ARG1/*tmp*/, resume_pc); // Now the interpreter frame (and its call chain) have been // invalidated and flushed. We are now protected against eager @@ -1335,16 +1346,11 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ call_stub(signature_handler_fd); - // Remove the register parameter varargs slots we allocated in - // compute_interpreter_state. SP+16 ends up pointing to the ABI - // outgoing argument area. - // - // Not needed on PPC64. - //__ add(SP, SP, Argument::n_int_register_parameters_c*BytesPerWord); - - assert(result_handler_addr->is_nonvolatile(), "result_handler_addr must be in a non-volatile register"); + assert(__ nonvolatile_accross_vthread_preemtion(result_handler_addr), + "result_handler_addr not preserved"); // Save across call to native method. __ mr(result_handler_addr, R3_RET); + __ ld(R11_scratch1, _abi0(callers_sp), R1_SP); // load FP __ isync(); // Acquire signature handler before trying to fetch the native entry point and klass mirror. @@ -1358,12 +1364,11 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ testbitdi(CCR0, R0, access_flags, JVM_ACC_STATIC_BIT); __ bfalse(CCR0, method_is_not_static); - __ ld(R11_scratch1, _abi0(callers_sp), R1_SP); - // Load mirror from interpreter frame. - __ ld(R12_scratch2, _ijava_state_neg(mirror), R11_scratch1); + // Load mirror from interpreter frame (FP in R11_scratch1) + __ ld(R21_tmp1, _ijava_state_neg(mirror), R11_scratch1); // R4_ARG2 = &state->_oop_temp; __ addi(R4_ARG2, R11_scratch1, _ijava_state_neg(oop_tmp)); - __ std(R12_scratch2/*mirror*/, _ijava_state_neg(oop_tmp), R11_scratch1); + __ std(R21_tmp1/*mirror*/, _ijava_state_neg(oop_tmp), R11_scratch1); BIND(method_is_not_static); } @@ -1397,7 +1402,18 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // Call the native method. Argument registers must not have been // overwritten since "__ call_stub(signature_handler);" (except for // ARG1 and ARG2 for static methods). + + if (support_vthread_preemption) { + // result_handler_addr is a nonvolatile register. Its value will be preserved across + // the native call but only if the call isn't preempted. To preserve its value even + // in the case of preemption we save it in the lresult slot. It is restored at + // resume_pc if, and only if the call was preempted. This works because only + // j.l.Object::wait calls are preempted which don't return a result. + __ std(result_handler_addr, _ijava_state_neg(lresult), R11_scratch1); + } + __ push_cont_fastpath(); __ call_c(native_method_fd); + __ pop_cont_fastpath(); __ li(R0, 0); __ ld(R11_scratch1, 0, R1_SP); @@ -1495,6 +1511,35 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ lwsync(); // Acquire safepoint and suspend state, release thread state. __ stw(R0/*thread_state*/, thread_(thread_state)); + if (support_vthread_preemption) { + // Check preemption for Object.wait() + Label not_preempted; + __ ld(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread); + __ cmpdi(CCR0, R0, 0); + __ beq(CCR0, not_preempted); + __ mtlr(R0); + __ li(R0, 0); + __ std(R0, in_bytes(JavaThread::preempt_alternate_return_offset()), R16_thread); + __ blr(); + + // Execution will be resumed here when the vthread becomes runnable again. + __ bind(*resume_pc); + __ restore_after_resume(R11_scratch1 /* fp */); + // We saved the result handler before the call + __ ld(result_handler_addr, _ijava_state_neg(lresult), R11_scratch1); +#ifdef ASSERT + // Clobber result slots. Only native methods returning void can be preemted currently. + __ load_const(R3_RET, UCONST64(0xbad01001)); + __ std(R3_RET, _ijava_state_neg(lresult), R11_scratch1); + __ std(R3_RET, _ijava_state_neg(fresult), R11_scratch1); + // reset_last_Java_frame() below asserts that a last java sp is set + __ asm_assert_mem8_is_zero(in_bytes(JavaThread::last_Java_sp_offset()), + R16_thread, FILE_AND_LINE ": Last java sp should not be set when resuming"); + __ std(R3_RET, in_bytes(JavaThread::last_Java_sp_offset()), R16_thread); +#endif + __ bind(not_preempted); + } + if (CheckJNICalls) { // clear_pending_jni_exception_check __ load_const_optimized(R0, 0L); diff --git a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp index dd35cce4cab95..ed932dddcd8d8 100644 --- a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp @@ -83,8 +83,8 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr // displaced header address in the object header - if it is not the same, get the // object header instead la(temp, Address(obj, hdr_offset)); - cmpxchgptr(hdr, disp_hdr, temp, t1, done, /*fallthough*/nullptr); // if the object header was the same, we're done + cmpxchgptr(hdr, disp_hdr, temp, t1, done, /*fallthough*/nullptr); // if the object header was not the same, it is now in the hdr register // => test if it is a stack pointer into the same stack (recursive locking), i.e.: // @@ -106,11 +106,12 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr sd(hdr, Address(disp_hdr, 0)); // otherwise we don't care about the result and handle locking via runtime call bnez(hdr, slow_case, /* is_far */ true); + // done bind(done); + inc_held_monitor_count(t0); } - increment(Address(xthread, JavaThread::held_monitor_count_offset())); return null_check_offset; } @@ -146,11 +147,11 @@ void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_ } else { cmpxchgptr(disp_hdr, hdr, obj, t1, done, &slow_case); } + // done bind(done); + dec_held_monitor_count(t0); } - - decrement(Address(xthread, JavaThread::held_monitor_count_offset())); } // Defines obj, preserves var_size_in_bytes diff --git a/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp b/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp index 5e4031727c827..6f59f5c2b9559 100644 --- a/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp @@ -165,7 +165,7 @@ int StubAssembler::call_RT(Register oop_result, Register metadata_result, addres } enum return_state_t { - does_not_return, requires_return + does_not_return, requires_return, requires_pop_epilogue_return }; // Implementation of StubFrame @@ -173,7 +173,7 @@ enum return_state_t { class StubFrame: public StackObj { private: StubAssembler* _sasm; - bool _return_state; + return_state_t _return_state; public: StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, return_state_t return_state=requires_return); @@ -187,8 +187,18 @@ void StubAssembler::prologue(const char* name, bool must_gc_arguments) { enter(); } -void StubAssembler::epilogue() { - leave(); +void StubAssembler::epilogue(bool use_pop) { + // Avoid using a leave instruction when this frame may + // have been frozen, since the current value of fp + // restored from the stub would be invalid. We still + // must restore the fp value saved on enter though. + if (use_pop) { + ld(fp, Address(sp)); + ld(ra, Address(sp, wordSize)); + addi(sp, sp, 2 * wordSize); + } else { + leave(); + } ret(); } @@ -208,10 +218,10 @@ void StubFrame::load_argument(int offset_in_words, Register reg) { StubFrame::~StubFrame() { - if (_return_state == requires_return) { - __ epilogue(); - } else { + if (_return_state == does_not_return) { __ should_not_reach_here(); + } else { + __ epilogue(_return_state == requires_pop_epilogue_return); } _sasm = nullptr; } @@ -266,6 +276,10 @@ static OopMap* generate_oop_map(StubAssembler* sasm, bool save_fpu_registers) { r->as_VMReg()); } + int sp_offset = cpu_reg_save_offsets[xthread->encoding()]; + oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset), + xthread->as_VMReg()); + // fpu_regs if (save_fpu_registers) { for (int i = 0; i < FrameMap::nof_fpu_regs; i++) { @@ -354,6 +368,16 @@ void Runtime1::initialize_pd() { } } +// return: offset in 64-bit words. +uint Runtime1::runtime_blob_current_thread_offset(frame f) { + CodeBlob* cb = f.cb(); + assert(cb == Runtime1::blob_for(C1StubId::monitorenter_id) || + cb == Runtime1::blob_for(C1StubId::monitorenter_nofpu_id), "must be"); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + int offset = cpu_reg_save_offsets[xthread->encoding()]; + return offset / 2; // SP offsets are in halfwords +} + // target: the entry point of the method that creates and posts the exception oop // has_argument: true if the exception needs arguments (passed in t0 and t1) @@ -879,7 +903,7 @@ OopMapSet* Runtime1::generate_code_for(C1StubId id, StubAssembler* sasm) { // fall through case C1StubId::monitorenter_id: { - StubFrame f(sasm, "monitorenter", dont_gc_arguments); + StubFrame f(sasm, "monitorenter", dont_gc_arguments, requires_pop_epilogue_return); OopMap* map = save_live_registers(sasm, save_fpu_registers); assert_cond(map != nullptr); diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 18657807bc6cb..7b0316e208fd3 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -45,7 +45,7 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, - Register tmp1Reg, Register tmp2Reg, Register tmp3Reg) { + Register tmp1Reg, Register tmp2Reg, Register tmp3Reg, Register tmp4Reg) { // Use cr register to indicate the fast_lock result: zero for success; non-zero for failure. Register flag = t1; Register oop = objectReg; @@ -104,9 +104,9 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, // markWord of object (disp_hdr) with the stack pointer. sub(disp_hdr, disp_hdr, sp); mv(tmp, (intptr_t) (~(os::vm_page_size()-1) | (uintptr_t)markWord::lock_mask_in_place)); - // If (mark & lock_mask) == 0 and mark - sp < page_size, we are stack-locking and goto label locked, - // hence we can store 0 as the displaced header in the box, which indicates that it is a - // recursive lock. + // If (mark & lock_mask) == 0 and mark - sp < page_size, we are stack-locking and goto label + // locked, hence we can store 0 as the displaced header in the box, which indicates that it + // is a recursive lock. andr(tmp/*==0?*/, disp_hdr, tmp); sd(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes())); beqz(tmp, locked); @@ -115,12 +115,12 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, // Handle existing monitor. bind(object_has_monitor); - // The object's monitor m is unlocked iff m->owner == nullptr, - // otherwise m->owner may contain a thread or a stack address. - // - // Try to CAS m->owner from null to current thread. + + // Try to CAS owner (no owner => current thread's _lock_id). add(tmp, disp_hdr, (in_bytes(ObjectMonitor::owner_offset()) - markWord::monitor_value)); - cmpxchg(/*memory address*/tmp, /*expected value*/zr, /*new value*/xthread, Assembler::int64, + Register tid = tmp4Reg; + ld(tid, Address(xthread, JavaThread::lock_id_offset())); + cmpxchg(/*memory address*/tmp, /*expected value*/zr, /*new value*/tid, Assembler::int64, Assembler::aq, Assembler::rl, /*result*/tmp3Reg); // cas succeeds if tmp3Reg == zr(expected) // Store a non-null value into the box to avoid looking like a re-entrant @@ -132,14 +132,16 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, beqz(tmp3Reg, locked); // CAS success means locking succeeded - bne(tmp3Reg, xthread, slow_path); // Check for recursive locking + bne(tmp3Reg, tid, slow_path); // Check for recursive locking // Recursive lock case increment(Address(disp_hdr, in_bytes(ObjectMonitor::recursions_offset()) - markWord::monitor_value), 1, tmp2Reg, tmp3Reg); bind(locked); mv(flag, zr); - increment(Address(xthread, JavaThread::held_monitor_count_offset()), 1, tmp2Reg, tmp3Reg); + if (LockingMode == LM_LEGACY) { + inc_held_monitor_count(t0); + } #ifdef ASSERT // Check that locked label is reached with flag == 0. @@ -253,7 +255,9 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, bind(unlocked); mv(flag, zr); - decrement(Address(xthread, JavaThread::held_monitor_count_offset()), 1, tmp1Reg, tmp2Reg); + if (LockingMode == LM_LEGACY) { + dec_held_monitor_count(t0); + } #ifdef ASSERT // Check that unlocked label is reached with flag == 0. @@ -273,12 +277,12 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, } void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, - Register tmp1, Register tmp2, Register tmp3) { + Register tmp1, Register tmp2, Register tmp3, Register tmp4) { // Flag register, zero for success; non-zero for failure. Register flag = t1; assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - assert_different_registers(obj, box, tmp1, tmp2, tmp3, flag, t0); + assert_different_registers(obj, box, tmp1, tmp2, tmp3, tmp4, flag, t0); mv(flag, 1); @@ -349,6 +353,7 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, bind(inflated); const Register tmp1_monitor = tmp1; + if (!UseObjectMonitorTable) { assert(tmp1_monitor == tmp1_mark, "should be the same here"); } else { @@ -395,13 +400,15 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, // Compute owner address. la(tmp2_owner_addr, owner_address); - // CAS owner (null => current thread). - cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ xthread, Assembler::int64, + // Try to CAS owner (no owner => current thread's _lock_id). + Register tid = tmp4; + ld(tid, Address(xthread, JavaThread::lock_id_offset())); + cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ tid, Assembler::int64, /*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner); beqz(tmp3_owner, monitor_locked); // Check if recursive. - bne(tmp3_owner, xthread, slow_path); + bne(tmp3_owner, tid, slow_path); // Recursive. increment(recursions_address, 1, tmp2, tmp3); @@ -414,7 +421,6 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, bind(locked); mv(flag, zr); - increment(Address(xthread, JavaThread::held_monitor_count_offset()), 1, tmp2, tmp3); #ifdef ASSERT // Check that locked label is reached with flag == 0. @@ -586,7 +592,6 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, bind(unlocked); mv(flag, zr); - decrement(Address(xthread, JavaThread::held_monitor_count_offset()), 1, tmp2, tmp3); #ifdef ASSERT // Check that unlocked label is reached with flag == 0. diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index 3304e3aef7237..8f9d9cd2ccd24 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -44,11 +44,15 @@ public: // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - void fast_lock(Register object, Register box, Register tmp1, Register tmp2, Register tmp3); + void fast_lock(Register object, Register box, + Register tmp1, Register tmp2, Register tmp3, Register tmp4); void fast_unlock(Register object, Register box, Register tmp1, Register tmp2); + // Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file. - void fast_lock_lightweight(Register object, Register box, Register tmp1, Register tmp2, Register tmp3); - void fast_unlock_lightweight(Register object, Register box, Register tmp1, Register tmp2, Register tmp3); + void fast_lock_lightweight(Register object, Register box, + Register tmp1, Register tmp2, Register tmp3, Register tmp4); + void fast_unlock_lightweight(Register object, Register box, + Register tmp1, Register tmp2, Register tmp3); void string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, diff --git a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp index cea4351151425..283334bbf4105 100644 --- a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp @@ -127,6 +127,11 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { } } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + assert(f.interpreter_frame_last_sp() == nullptr, "should be null for top frame"); + f.interpreter_frame_set_last_sp(f.unextended_sp()); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { assert(hf.fp() == hf.unextended_sp() + (f.fp() - f.unextended_sp()), ""); assert((f.at(frame::interpreter_frame_last_sp_offset) != 0) @@ -147,10 +152,16 @@ inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, co // extended_sp is already relativized by TemplateInterpreterGenerator::generate_normal_entry or // AbstractInterpreter::layout_activation + // The interpreter native wrapper code adds space in the stack equal to size_of_parameters() + // after the fixed part of the frame. For wait0 this is equal to 3 words (this + long parameter). + // We adjust by this size since otherwise the saved last sp will be less than the extended_sp. + DEBUG_ONLY(Method* m = hf.interpreter_frame_method();) + DEBUG_ONLY(int extra_space = m->is_object_wait0() ? m->size_of_parameters() : 0;) + assert((hf.fp() - hf.unextended_sp()) == (f.fp() - f.unextended_sp()), ""); assert(hf.unextended_sp() == (intptr_t*)hf.at(frame::interpreter_frame_last_sp_offset), ""); assert(hf.unextended_sp() <= (intptr_t*)hf.at(frame::interpreter_frame_initial_sp_offset), ""); - assert(hf.unextended_sp() > (intptr_t*)hf.at(frame::interpreter_frame_extended_sp_offset), ""); + assert(hf.unextended_sp() + extra_space > (intptr_t*)hf.at(frame::interpreter_frame_extended_sp_offset), ""); assert(hf.fp() > (intptr_t*)hf.at(frame::interpreter_frame_initial_sp_offset), ""); #ifdef ASSERT if (f.interpreter_frame_method()->max_locals() > 0) { @@ -203,7 +214,8 @@ inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { inline frame ThawBase::new_entry_frame() { intptr_t* sp = _cont.entrySP(); - return frame(sp, sp, _cont.entryFP(), _cont.entryPC()); // TODO PERF: This finds code blob and computes deopt state + // TODO PERF: This finds code blob and computes deopt state + return frame(sp, sp, _cont.entryFP(), _cont.entryPC()); } template frame ThawBase::new_stack_frame(const frame& hf, frame& caller, bool bottom) { @@ -215,7 +227,6 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& // If caller is interpreted it already made room for the callee arguments int overlap = caller.is_interpreted_frame() ? ContinuationHelper::InterpretedFrame::stack_argsize(hf) : 0; const int fsize = (int)(ContinuationHelper::InterpretedFrame::frame_bottom(hf) - hf.unextended_sp() - overlap); - const int locals = hf.interpreter_frame_method()->max_locals(); intptr_t* frame_sp = caller.unextended_sp() - fsize; intptr_t* fp = frame_sp + (hf.fp() - heap_sp); if ((intptr_t)fp % frame::frame_alignment != 0) { @@ -237,7 +248,7 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& int fsize = FKind::size(hf); intptr_t* frame_sp = caller.unextended_sp() - fsize; if (bottom || caller.is_interpreted_frame()) { - int argsize = hf.compiled_frame_stack_argsize(); + int argsize = FKind::stack_argsize(hf); fsize += argsize; frame_sp -= argsize; @@ -252,13 +263,16 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& intptr_t* fp; if (PreserveFramePointer) { // we need to recreate a "real" frame pointer, pointing into the stack - fp = frame_sp + FKind::size(hf) - 2; + fp = frame_sp + FKind::size(hf) - frame::sender_sp_offset; } else { - fp = FKind::stub - ? frame_sp + fsize - 2 // On RISCV, this value is used for the safepoint stub - : *(intptr_t**)(hf.sp() - 2); // we need to re-read fp because it may be an oop and we might have fixed the frame. + fp = FKind::stub || FKind::native + // fp always points to the address above the pushed return pc. We need correct address. + ? frame_sp + fsize - frame::sender_sp_offset + // we need to re-read fp because it may be an oop and we might have fixed the frame. + : *(intptr_t**)(hf.sp() - 2); } - return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); // TODO PERF : this computes deopt state; is it necessary? + // TODO PERF : this computes deopt state; is it necessary? + return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); } } @@ -279,6 +293,22 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) { patch_callee_link(caller, caller.fp()); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + intptr_t* fp = caller_sp - frame::sender_sp_offset; + patch_callee_link(f, fp); +} + +inline intptr_t* ThawBase::push_cleanup_continuation() { + frame enterSpecial = new_entry_frame(); + intptr_t* sp = enterSpecial.sp(); + + sp[-1] = (intptr_t)ContinuationEntry::cleanup_pc(); + sp[-2] = (intptr_t)enterSpecial.fp(); + + log_develop_trace(continuations, preempt)("push_cleanup_continuation initial sp: " INTPTR_FORMAT " final sp: " INTPTR_FORMAT, p2i(sp + 2 * frame::metadata_words), p2i(sp)); + return sp; +} + inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { // Make sure that last_sp is kept relativized. assert((intptr_t*)f.at_relative(frame::interpreter_frame_last_sp_offset) == f.unextended_sp(), ""); @@ -286,8 +316,11 @@ inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, c // Make sure that monitor_block_top is still relativized. assert(f.at_absolute(frame::interpreter_frame_monitor_block_top_offset) <= frame::interpreter_frame_initial_sp_offset, ""); + DEBUG_ONLY(Method* m = hf.interpreter_frame_method();) + DEBUG_ONLY(int extra_space = m->is_object_wait0() ? m->size_of_parameters() : 0;) // see comment in relativize_interpreted_frame_metadata() + // Make sure that extended_sp is kept relativized. - assert((intptr_t*)f.at_relative(frame::interpreter_frame_extended_sp_offset) < f.unextended_sp(), ""); + assert((intptr_t*)f.at_relative(frame::interpreter_frame_extended_sp_offset) < f.unextended_sp() + extra_space, ""); } #endif // CPU_RISCV_CONTINUATIONFREEZETHAW_RISCV_INLINE_HPP diff --git a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp index 1c668531a073d..8c0fb728dc7d8 100644 --- a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp @@ -40,6 +40,22 @@ static inline intptr_t** link_address(const frame& f) { : (intptr_t**)(f.unextended_sp() + f.cb()->frame_size() - 2); } +static inline void patch_return_pc_with_preempt_stub(frame& f) { + if (f.is_runtime_frame()) { + // Unlike x86 we don't know where in the callee frame the return pc is + // saved so we can't patch the return from the VM call back to Java. + // Instead, we will patch the return from the runtime stub back to the + // compiled method so that the target returns to the preempt cleanup stub. + intptr_t* caller_sp = f.sp() + f.cb()->frame_size(); + caller_sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub(); + } else { + // The target will check for preemption once it returns to the interpreter + // or the native wrapper code and will manually jump to the preempt stub. + JavaThread *thread = JavaThread::current(); + thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub()); + } +} + inline int ContinuationHelper::frame_align_words(int size) { #ifdef _LP64 return size & 1; @@ -72,12 +88,12 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, anchor->set_last_Java_fp(entry->entry_fp()); } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { intptr_t* fp = *(intptr_t**)(sp - 2); anchor->set_last_Java_fp(fp); } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { intptr_t* sp = f.sp(); address pc = *(address*)(sp - frame::sender_sp_ret_address_offset()); diff --git a/src/hotspot/cpu/riscv/frame_riscv.cpp b/src/hotspot/cpu/riscv/frame_riscv.cpp index 96dca4704ad95..ecc450bd6b254 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.cpp +++ b/src/hotspot/cpu/riscv/frame_riscv.cpp @@ -393,6 +393,36 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { return fr; } +#if defined(ASSERT) +static address get_register_address_in_stub(const frame& stub_fr, VMReg reg) { + RegisterMap map(nullptr, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + stub_fr.oop_map()->update_register_map(&stub_fr, &map); + return map.location(reg, stub_fr.sp()); +} +#endif + +JavaThread** frame::saved_thread_address(const frame& f) { + CodeBlob* cb = f.cb(); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + JavaThread** thread_addr; +#ifdef COMPILER1 + if (cb == Runtime1::blob_for(C1StubId::monitorenter_id) || + cb == Runtime1::blob_for(C1StubId::monitorenter_nofpu_id)) { + thread_addr = (JavaThread**)(f.sp() + Runtime1::runtime_blob_current_thread_offset(f)); + } else +#endif + { + // c2 only saves rbp in the stub frame so nothing to do. + thread_addr = nullptr; + } + assert(get_register_address_in_stub(f, SharedRuntime::thread_register()) == (address)thread_addr, "wrong thread address"); + return thread_addr; +} + //------------------------------------------------------------------------------ // frame::verify_deopt_original_pc // diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index 3692c99193e1a..b4540c45ab8f5 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.hpp @@ -111,7 +111,8 @@ sender_sp_offset = 0, // Interpreter frames - interpreter_frame_oop_temp_offset = 1, // for native calls only + interpreter_frame_result_handler_offset = 1, // for native calls only + interpreter_frame_oop_temp_offset = 0, // for native calls only interpreter_frame_sender_sp_offset = -3, // outgoing sp before a call to an invoked method diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index a1d806a17b30a..b00b4474d69da 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -721,7 +721,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); if (LockingMode == LM_MONITOR) { - call_VM(noreg, + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); } else { @@ -752,7 +752,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) if (LockingMode == LM_LIGHTWEIGHT) { lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); - j(count); + j(done); } else if (LockingMode == LM_LEGACY) { // Load (object->mark() | 1) into swap_reg ld(t0, Address(obj_reg, oopDesc::mark_offset_in_bytes())); @@ -781,19 +781,19 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) // Save the test result, for recursive case, the result is zero sd(swap_reg, Address(lock_reg, mark_offset)); - beqz(swap_reg, count); + bnez(swap_reg, slow_case); + + bind(count); + inc_held_monitor_count(t0); + j(done); } bind(slow_case); // Call the runtime routine for slow case - call_VM(noreg, + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); - j(done); - - bind(count); - increment(Address(xthread, JavaThread::held_monitor_count_offset())); bind(done); } @@ -839,12 +839,10 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) // Free entry sd(zr, Address(lock_reg, BasicObjectLock::obj_offset())); + Label slow_case; if (LockingMode == LM_LIGHTWEIGHT) { - Label slow_case; lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); - j(count); - - bind(slow_case); + j(done); } else if (LockingMode == LM_LEGACY) { // Load the old header from BasicLock structure ld(header_reg, Address(swap_reg, @@ -854,20 +852,19 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) beqz(header_reg, count); // Atomic swap back the old header - cmpxchg_obj_header(swap_reg, header_reg, obj_reg, tmp_reg, count, /*fallthrough*/nullptr); + cmpxchg_obj_header(swap_reg, header_reg, obj_reg, tmp_reg, count, &slow_case); + + bind(count); + dec_held_monitor_count(t0); + j(done); } + bind(slow_case); // Call the runtime routine for slow case. sd(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - j(done); - - bind(count); - decrement(Address(xthread, JavaThread::held_monitor_count_offset())); - bind(done); - restore_bcp(); } } @@ -1575,6 +1572,55 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, restore_locals(); } +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1) { + assert(arg_1 == c_rarg1, ""); + Label resume_pc, not_preempted; + +#ifdef ASSERT + { + Label L; + ld(t0, Address(xthread, JavaThread::preempt_alternate_return_offset())); + beqz(t0, L); + stop("Should not have alternate return address set"); + bind(L); + } +#endif /* ASSERT */ + + // Force freeze slow path. + push_cont_fastpath(); + + // Make VM call. In case of preemption set last_pc to the one we want to resume to. + la(t0, resume_pc); + sd(t0, Address(xthread, JavaThread::last_Java_pc_offset())); + call_VM_base(oop_result, noreg, noreg, entry_point, 1, false /*check_exceptions*/); + + pop_cont_fastpath(); + + // Check if preempted. + ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); + beqz(t1, not_preempted); + sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); + jr(t1); + + // In case of preemption, this is where we will resume once we finally acquire the monitor. + bind(resume_pc); + restore_after_resume(false /* is_native */); + + bind(not_preempted); +} + +void InterpreterMacroAssembler::restore_after_resume(bool is_native) { + la(t1, ExternalAddress(Interpreter::cont_resume_interpreter_adapter())); + jalr(t1); + if (is_native) { + // On resume we need to set up stack as expected + push(dtos); + push(ltos); + } +} + void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr, Register tmp) { assert_different_registers(obj, tmp, t0, mdo_addr.base()); Label update, next, none; diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index d3959d68d9cfb..ddc0a94a6e40e 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -59,6 +59,11 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_earlyret_value(TosState state); + void call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1); + void restore_after_resume(bool is_native); + void jump_to_entry(address entry); virtual void check_and_handle_popframe(Register java_thread); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 2e69fbf831a44..39fb2c7d60a62 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -36,6 +36,7 @@ #include "gc/shared/collectedHeap.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/accessDecorators.hpp" @@ -226,6 +227,36 @@ void MacroAssembler::pop_cont_fastpath(Register java_thread) { bind(done); } +void MacroAssembler::inc_held_monitor_count(Register tmp) { + Address dst(xthread, JavaThread::held_monitor_count_offset()); + ld(tmp, dst); + addi(tmp, tmp, 1); + sd(tmp, dst); +#ifdef ASSERT + Label ok; + test_bit(tmp, tmp, 63); + beqz(tmp, ok); + STOP("assert(held monitor count overflow)"); + should_not_reach_here(); + bind(ok); +#endif +} + +void MacroAssembler::dec_held_monitor_count(Register tmp) { + Address dst(xthread, JavaThread::held_monitor_count_offset()); + ld(tmp, dst); + addi(tmp, tmp, -1); + sd(tmp, dst); +#ifdef ASSERT + Label ok; + test_bit(tmp, tmp, 63); + beqz(tmp, ok); + STOP("assert(held monitor count underflow)"); + should_not_reach_here(); + bind(ok); +#endif +} + int MacroAssembler::align(int modulus, int extra_offset) { CompressibleRegion cr(this); intptr_t before = offset(); @@ -407,6 +438,10 @@ void MacroAssembler::reset_last_Java_frame(bool clear_fp) { sd(zr, Address(xthread, JavaThread::last_Java_pc_offset())); } +static bool is_preemptable(address entry_point) { + return entry_point == CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter); +} + void MacroAssembler::call_VM_base(Register oop_result, Register java_thread, Register last_java_sp, @@ -436,7 +471,12 @@ void MacroAssembler::call_VM_base(Register oop_result, assert(last_java_sp != fp, "can't use fp"); Label l; - set_last_Java_frame(last_java_sp, fp, l, t0); + if (is_preemptable(entry_point)) { + // skip setting last_pc since we already set it to desired value. + set_last_Java_frame(last_java_sp, fp, noreg); + } else { + set_last_Java_frame(last_java_sp, fp, l, t0); + } // do the call, remove parameters MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments, &l); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 17b0956b3ef90..e716641250711 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -812,8 +812,11 @@ class MacroAssembler: public Assembler { void push_CPU_state(bool save_vectors = false, int vector_size_in_bytes = 0); void pop_CPU_state(bool restore_vectors = false, int vector_size_in_bytes = 0); - void push_cont_fastpath(Register java_thread); - void pop_cont_fastpath(Register java_thread); + void push_cont_fastpath(Register java_thread = xthread); + void pop_cont_fastpath(Register java_thread = xthread); + + void inc_held_monitor_count(Register tmp); + void dec_held_monitor_count(Register tmp); // if heap base register is used - reinit it with the correct value void reinit_heapbase(); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 162fbcc802f0f..86eb624bb4866 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1259,20 +1259,19 @@ int MachCallRuntimeNode::ret_addr_offset() { // jal(addr) // or with far branches // jal(trampoline_stub) - // for real runtime callouts it will be 9 instructions + // for real runtime callouts it will be 8 instructions // see riscv_enc_java_to_runtime - // la(t0, retaddr) -> auipc + addi - // addi(sp, sp, -2 * wordSize) -> addi - // sd(t0, Address(sp, wordSize)) -> sd - // movptr(t1, addr, offset, t0) -> lui + lui + slli + add - // jalr(t1, offset) -> jalr + // la(t0, retaddr) -> auipc + addi + // sd(t0, Address(xthread, JavaThread::last_Java_pc_offset())) -> sd + // movptr(t1, addr, offset, t0) -> lui + lui + slli + add + // jalr(t1, offset) -> jalr if (CodeCache::contains(_entry_point)) { if (UseTrampolines) { return 1 * NativeInstruction::instruction_size; } return 3 * NativeInstruction::instruction_size; } else { - return 9 * NativeInstruction::instruction_size; + return 8 * NativeInstruction::instruction_size; } } @@ -2502,17 +2501,15 @@ encode %{ __ post_call_nop(); } else { Label retaddr; + // Make the anchor frame walkable __ la(t0, retaddr); - // Leave a breadcrumb for JavaFrameAnchor::capture_last_Java_pc() - __ addi(sp, sp, -2 * wordSize); - __ sd(t0, Address(sp, wordSize)); + __ sd(t0, Address(xthread, JavaThread::last_Java_pc_offset())); int32_t offset = 0; // No relocation needed __ movptr(t1, entry, offset, t0); // lui + lui + slli + add __ jalr(t1, offset); __ bind(retaddr); __ post_call_nop(); - __ addi(sp, sp, 2 * wordSize); } %} @@ -10539,17 +10536,19 @@ instruct tlsLoadP(javaThread_RegP dst) // inlined locking and unlocking // using t1 as the 'flag' register to bridge the BoolNode producers and consumers -instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, + iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegPNoSp tmp4) %{ predicate(LockingMode != LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4); ins_cost(10 * DEFAULT_COST); - format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3, #@cmpFastLock" %} + format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3,$tmp4 #@cmpFastLock" %} ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); + __ fast_lock($object$$Register, $box$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); %} ins_pipe(pipe_serial); @@ -10572,23 +10571,26 @@ instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iR ins_pipe(pipe_serial); %} -instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, + iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegPNoSp tmp4) %{ predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4); ins_cost(10 * DEFAULT_COST); - format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3 #@cmpFastLockLightweight" %} + format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3,$tmp4 #@cmpFastLockLightweight" %} ins_encode %{ - __ fast_lock_lightweight($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); + __ fast_lock_lightweight($object$$Register, $box$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); %} ins_pipe(pipe_serial); %} -instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) +instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, + iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object box)); @@ -10598,7 +10600,8 @@ instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNo format %{ "fastunlock $object,$box\t! kills $tmp1,$tmp2,$tmp3 #@cmpFastUnlockLightweight" %} ins_encode %{ - __ fast_unlock_lightweight($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); + __ fast_unlock_lightweight($object$$Register, $box$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); %} ins_pipe(pipe_serial); diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 29c96112eada8..5ab2a9f00b947 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -1050,12 +1050,14 @@ static void gen_continuation_enter(MacroAssembler* masm, __ bind(call_thaw); + ContinuationEntry::_thaw_call_pc_offset = __ pc() - start; __ rt_call(CAST_FROM_FN_PTR(address, StubRoutines::cont_thaw())); oop_maps->add_gc_map(__ pc() - start, map->deep_copy()); ContinuationEntry::_return_pc_offset = __ pc() - start; __ post_call_nop(); __ bind(exit); + ContinuationEntry::_cleanup_offset = __ pc() - start; continuation_enter_cleanup(masm); __ leave(); __ ret(); @@ -1149,6 +1151,10 @@ static void gen_continuation_yield(MacroAssembler* masm, oop_maps->add_gc_map(the_pc - start, map); } +void SharedRuntime::continuation_enter_cleanup(MacroAssembler* masm) { + ::continuation_enter_cleanup(masm); +} + static void gen_special_dispatch(MacroAssembler* masm, const methodHandle& method, const BasicType* sig_bt, @@ -1629,11 +1635,20 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, } // Change state to native (we save the return address in the thread, since it might not - // be pushed on the stack when we do a stack traversal). - // We use the same pc/oopMap repeatedly when we call out + // be pushed on the stack when we do a stack traversal). It is enough that the pc() + // points into the right code segment. It does not have to be the correct return pc. + // We use the same pc/oopMap repeatedly when we call out. Label native_return; - __ set_last_Java_frame(sp, noreg, native_return, t0); + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + // For convenience we use the pc we want to resume to in case of preemption on Object.wait. + __ set_last_Java_frame(sp, noreg, native_return, t0); + } else { + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + + __ set_last_Java_frame(sp, noreg, __ pc(), t0); + } Label dtrace_method_entry, dtrace_method_entry_done; if (DTraceMethodProbes) { @@ -1709,14 +1724,14 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Save the test result, for recursive case, the result is zero __ sd(swap_reg, Address(lock_reg, mark_word_offset)); __ bnez(swap_reg, slow_path_lock); + + __ bind(count); + __ inc_held_monitor_count(t0); } else { assert(LockingMode == LM_LIGHTWEIGHT, "must be"); __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); } - __ bind(count); - __ increment(Address(xthread, JavaThread::held_monitor_count_offset())); - // Slow path will re-enter here __ bind(lock_done); } @@ -1736,11 +1751,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Clobbers t1 __ rt_call(native_func); - __ bind(native_return); - - intptr_t return_pc = (intptr_t) __ pc(); - oop_maps->add_gc_map(return_pc - start, map); - // Verify or restore cpu control state after JNI call __ restore_cpu_control_state_after_jni(t0); @@ -1791,6 +1801,18 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ sw(t0, Address(t1)); __ bind(after_transition); + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + // Check preemption for Object.wait() + __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ beqz(t1, native_return); + __ sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ jr(t1); + __ bind(native_return); + + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + } + Label reguard; Label reguard_done; __ lbu(t0, Address(xthread, JavaThread::stack_guard_state_offset())); @@ -1814,7 +1836,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Simple recursive lock? __ ld(t0, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); __ bnez(t0, not_recursive); - __ decrement(Address(xthread, JavaThread::held_monitor_count_offset())); + __ dec_held_monitor_count(t0); __ j(done); } @@ -1837,11 +1859,10 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label count; __ cmpxchg_obj_header(x10, old_hdr, obj_reg, lock_tmp, count, &slow_path_unlock); __ bind(count); - __ decrement(Address(xthread, JavaThread::held_monitor_count_offset())); + __ dec_held_monitor_count(t0); } else { assert(LockingMode == LM_LIGHTWEIGHT, ""); __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); - __ decrement(Address(xthread, JavaThread::held_monitor_count_offset())); } // slow path re-enters here @@ -1909,8 +1930,14 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ mv(c_rarg1, lock_reg); __ mv(c_rarg2, xthread); - // Not a leaf but we have last_Java_frame setup as we want + // Not a leaf but we have last_Java_frame setup as we want. + // We don't want to unmount in case of contention since that would complicate preserving + // the arguments that had already been marshalled into the native convention. So we force + // the freeze slow path to find this native wrapper frame (see recurse_freeze_native_frame()) + // and pin the vthread. Otherwise the fast path won't find it since we don't walk the stack. + __ push_cont_fastpath(); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), 3); + __ pop_cont_fastpath(); restore_args(masm, total_c_args, c_arg, out_regs); #ifdef ASSERT @@ -2441,6 +2468,10 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } +VMReg SharedRuntime::thread_register() { + return xthread->as_VMReg(); +} + //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, diff --git a/src/hotspot/cpu/riscv/stackChunkFrameStream_riscv.inline.hpp b/src/hotspot/cpu/riscv/stackChunkFrameStream_riscv.inline.hpp index e226c7b7a53ab..fa8a8fb47f022 100644 --- a/src/hotspot/cpu/riscv/stackChunkFrameStream_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/stackChunkFrameStream_riscv.inline.hpp @@ -114,6 +114,7 @@ inline int StackChunkFrameStream::interpreter_frame_num_oops() const f.interpreted_frame_oop_map(&mask); return mask.num_oops() + 1 // for the mirror oop + + (f.interpreter_frame_method()->is_native() ? 1 : 0) // temp oop slot + pointer_delta_as_int((intptr_t*)f.interpreter_frame_monitor_begin(), (intptr_t*)f.interpreter_frame_monitor_end()) / BasicObjectLock::size(); } diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index bce0c8f1f3de7..e4ae4f3cfd1bd 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -4040,6 +4040,36 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_cont_preempt_stub() { + if (!Continuations::enabled()) return nullptr; + StubCodeMark mark(this, "StubRoutines","Continuation preempt stub"); + address start = __ pc(); + + __ reset_last_Java_frame(true); + + // Set sp to enterSpecial frame, i.e. remove all frames copied into the heap. + __ ld(sp, Address(xthread, JavaThread::cont_entry_offset())); + + Label preemption_cancelled; + __ lbu(t0, Address(xthread, JavaThread::preemption_cancelled_offset())); + __ bnez(t0, preemption_cancelled); + + // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. + SharedRuntime::continuation_enter_cleanup(_masm); + __ leave(); + __ ret(); + + // We acquired the monitor after freezing the frames so call thaw to continue execution. + __ bind(preemption_cancelled); + __ sb(zr, Address(xthread, JavaThread::preemption_cancelled_offset())); + __ la(fp, Address(sp, checked_cast(ContinuationEntry::size() + 2 * wordSize))); + __ la(t1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); + __ ld(t1, Address(t1)); + __ jr(t1); + + return start; + } + #if COMPILER2_OR_JVMCI #undef __ @@ -6402,6 +6432,7 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_cont_thaw = generate_cont_thaw(); StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); + StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); } void generate_final_stubs() { diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 0281c66b97db1..ac28f4b351435 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -539,6 +539,38 @@ address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, return entry; } +address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { + if (!Continuations::enabled()) return nullptr; + address start = __ pc(); + + __ restore_bcp(); + __ restore_locals(); + + // Restore constant pool cache + __ ld(xcpool, Address(fp, frame::interpreter_frame_cache_offset * wordSize)); + + // Restore Java expression stack pointer + __ ld(t0, Address(fp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ shadd(esp, t0, fp, t0, Interpreter::logStackElementSize); + // and NULL it as marker that esp is now tos until next java call + __ sd(zr, Address(fp, frame::interpreter_frame_last_sp_offset * wordSize)); + + // Restore machine SP + __ ld(t0, Address(fp, frame::interpreter_frame_extended_sp_offset * wordSize)); + __ shadd(sp, t0, fp, t0, LogBytesPerWord); + + // Restore method + __ ld(xmethod, Address(fp, frame::interpreter_frame_method_offset * wordSize)); + + // Restore dispatch + __ la(xdispatch, ExternalAddress((address)Interpreter::dispatch_table())); + + __ ret(); + + return start; +} + + // Helpers for commoning out cases in the various type of method entries. // @@ -1092,6 +1124,9 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // result handler is in x10 // set result handler __ mv(result_handler, x10); + // Save it in the frame in case of preemption; we cannot rely on callee saved registers. + __ sd(x10, Address(fp, frame::interpreter_frame_result_handler_offset * wordSize)); + // pass mirror handle if static call { Label L; @@ -1130,6 +1165,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // It is enough that the pc() points into the right code // segment. It does not have to be the correct return pc. + // For convenience we use the pc we want to resume to in + // case of preemption on Object.wait. Label native_return; __ set_last_Java_frame(esp, fp, native_return, x30); @@ -1151,9 +1188,13 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); __ sw(t0, Address(t1)); + __ push_cont_fastpath(); + // Call the native method. __ jalr(x28); - __ bind(native_return); + + __ pop_cont_fastpath(); + __ get_method(xmethod); // result potentially in x10 or f10 @@ -1219,6 +1260,23 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ mv(t0, _thread_in_Java); __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); + if (LockingMode != LM_LEGACY) { + // Check preemption for Object.wait() + Label not_preempted; + __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ beqz(t1, not_preempted); + __ sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ jr(t1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + // reload result_handler + __ ld(result_handler, Address(fp, frame::interpreter_frame_result_handler_offset * wordSize)); + __ bind(not_preempted); + } else { + // any pc will do so just use this one for LM_LEGACY to keep code together. + __ bind(native_return); + } + // reset_last_Java_frame __ reset_last_Java_frame(true); diff --git a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp index 2f629c108c956..8b30adb478559 100644 --- a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp +++ b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp @@ -208,6 +208,11 @@ void Runtime1::initialize_pd() { // Nothing to do. } +uint Runtime1::runtime_blob_current_thread_offset(frame f) { + Unimplemented(); + return 0; +} + OopMapSet* Runtime1::generate_exception_throw(StubAssembler* sasm, address target, bool has_argument) { // Make a frame and preserve the caller's caller-save registers. OopMap* oop_map = save_live_registers(sasm); diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index 084ee18921135..7223facaaeb2a 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -48,6 +48,10 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { Unimplemented(); } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + Unimplemented(); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { Unimplemented(); } @@ -83,6 +87,15 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) { Unimplemented(); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + Unimplemented(); +} + +inline intptr_t* ThawBase::push_cleanup_continuation() { + Unimplemented(); + return nullptr; +} + template inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { Unimplemented(); diff --git a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp index 4cea04595511e..8c57f1810e530 100644 --- a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp @@ -35,6 +35,10 @@ static inline intptr_t** link_address(const frame& f) { return nullptr; } +static inline void patch_return_pc_with_preempt_stub(frame& f) { + Unimplemented(); +} + inline int ContinuationHelper::frame_align_words(int size) { Unimplemented(); return 0; @@ -62,11 +66,11 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, Unimplemented(); } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { Unimplemented(); } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { Unimplemented(); return false; diff --git a/src/hotspot/cpu/s390/frame_s390.cpp b/src/hotspot/cpu/s390/frame_s390.cpp index 11c7386dfd776..75f29be13ab49 100644 --- a/src/hotspot/cpu/s390/frame_s390.cpp +++ b/src/hotspot/cpu/s390/frame_s390.cpp @@ -246,6 +246,11 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { return fr; } +JavaThread** frame::saved_thread_address(const frame& f) { + Unimplemented(); + return nullptr; +} + frame frame::sender_for_interpreter_frame(RegisterMap *map) const { // Pass callers sender_sp as unextended_sp. return frame(sender_sp(), sender_pc(), (intptr_t*)(ijava_state()->sender_sp)); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index ef7f3e22a04f5..9e1c5cbced364 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -3503,7 +3503,7 @@ void MacroAssembler::increment_counter_eq(address counter_address, Register tmp1 void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Register temp1, Register temp2) { assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_lock_lightweight"); - assert_different_registers(oop, box, temp1, temp2); + assert_different_registers(oop, box, temp1, temp2, Z_R0_scratch); Register displacedHeader = temp1; Register currentHeader = temp1; @@ -3572,14 +3572,13 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis Register zero = temp; Register monitor_tagged = displacedHeader; // Tagged with markWord::monitor_value. - // The object's monitor m is unlocked iff m->owner is null, - // otherwise m->owner may contain a thread or a stack address. - // Try to CAS m->owner from null to current thread. - // If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ. - // Otherwise, register zero is filled with the current owner. + // Try to CAS owner (no owner => current thread's _lock_id). + // If csg succeeds then CR=EQ, otherwise, register zero is filled + // with the current owner. z_lghi(zero, 0); - z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor_tagged); + z_lg(Z_R0_scratch, Address(Z_thread, JavaThread::lock_id_offset())); + z_csg(zero, Z_R0_scratch, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor_tagged); // Store a non-null value into the box. z_stg(box, BasicLock::displaced_header_offset_in_bytes(), box); @@ -3588,7 +3587,7 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis BLOCK_COMMENT("fast_path_recursive_lock {"); // Check if we are already the owner (recursive lock) - z_cgr(Z_thread, zero); // owner is stored in zero by "z_csg" above + z_cgr(Z_R0_scratch, zero); // owner is stored in zero by "z_csg" above z_brne(done); // not a recursive lock // Current thread already owns the lock. Just increment recursion count. @@ -3606,7 +3605,7 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis void MacroAssembler::compiler_fast_unlock_object(Register oop, Register box, Register temp1, Register temp2) { assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_unlock_lightweight"); - assert_different_registers(oop, box, temp1, temp2); + assert_different_registers(oop, box, temp1, temp2, Z_R0_scratch); Register displacedHeader = temp1; Register currentHeader = temp2; @@ -3653,7 +3652,8 @@ void MacroAssembler::compiler_fast_unlock_object(Register oop, Register box, Reg // Handle existing monitor. bind(object_has_monitor); - z_cg(Z_thread, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); + z_lg(Z_R0_scratch, Address(Z_thread, JavaThread::lock_id_offset())); + z_cg(Z_R0_scratch, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); z_brne(done); BLOCK_COMMENT("fast_path_recursive_unlock {"); @@ -6228,7 +6228,7 @@ void MacroAssembler::lightweight_unlock(Register obj, Register temp1, Register t } void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Register box, Register tmp1, Register tmp2) { - assert_different_registers(obj, box, tmp1, tmp2); + assert_different_registers(obj, box, tmp1, tmp2, Z_R0_scratch); // Handle inflated monitor. NearLabel inflated; @@ -6356,15 +6356,16 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Registe const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset() - monitor_tag); - // Try to CAS m->owner from null to current thread. - // If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ. - // Otherwise, register zero is filled with the current owner. + // Try to CAS owner (no owner => current thread's _lock_id). + // If csg succeeds then CR=EQ, otherwise, register zero is filled + // with the current owner. z_lghi(zero, 0); - z_csg(zero, Z_thread, owner_address); + z_lg(Z_R0_scratch, Address(Z_thread, JavaThread::lock_id_offset())); + z_csg(zero, Z_R0_scratch, owner_address); z_bre(monitor_locked); // Check if recursive. - z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction + z_cgr(Z_R0_scratch, zero); // zero contains the owner from z_csg instruction z_brne(slow_path); // Recursive diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 468610b588e91..c60f6ef32957b 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2387,6 +2387,11 @@ uint SharedRuntime::out_preserve_stack_slots() { return frame::z_jit_out_preserve_size/VMRegImpl::stack_slot_size; } +VMReg SharedRuntime::thread_register() { + Unimplemented(); + return nullptr; +} + // // Frame generation for deopt and uncommon trap blobs. // diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index 2c2e8ed9e3b3a..820417fc74322 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -721,6 +721,11 @@ address TemplateInterpreterGenerator::generate_safept_entry_for (TosState state, return entry; } +address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { + return nullptr; +} + + // // Helpers for commoning out cases in the various type of method entries. // diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 03ce06726c6e9..36a122d29464d 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -2854,6 +2854,26 @@ void Assembler::leal(Register dst, Address src) { emit_operand(dst, src, 0); } +#ifdef _LP64 +void Assembler::lea(Register dst, Label& L) { + emit_prefix_and_int8(get_prefixq(Address(), dst), (unsigned char)0x8D); + if (!L.is_bound()) { + // Patch @0x8D opcode + L.add_patch_at(code(), CodeBuffer::locator(offset() - 1, sect())); + // Register and [rip+disp] operand + emit_modrm(0b00, raw_encode(dst), 0b101); + emit_int32(0); + } else { + // Register and [rip+disp] operand + emit_modrm(0b00, raw_encode(dst), 0b101); + // Adjust displacement by sizeof lea instruction + int32_t disp = checked_cast(target(L) - (pc() + sizeof(int32_t))); + assert(is_simm32(disp), "must be 32bit offset [rip+offset]"); + emit_int32(disp); + } +} +#endif + void Assembler::lfence() { emit_int24(0x0F, (unsigned char)0xAE, (unsigned char)0xE8); } diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 420c28254d53f..7c6397083765c 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1625,6 +1625,10 @@ class Assembler : public AbstractAssembler { void leaq(Register dst, Address src); +#ifdef _LP64 + void lea(Register dst, Label& L); +#endif + void lfence(); void lock(); diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp index a0efde9b8164b..f53a25ed3e646 100644 --- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp @@ -109,10 +109,9 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr jcc(Assembler::notZero, slow_case); // done bind(done); + inc_held_monitor_count(); } - inc_held_monitor_count(); - return null_check_offset; } @@ -153,9 +152,9 @@ void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_ // we do unlocking via runtime call jcc(Assembler::notEqual, slow_case); // done + bind(done); + dec_held_monitor_count(); } - bind(done); - dec_held_monitor_count(); } diff --git a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp index 1ccb06df48937..5cc8ffd9befe4 100644 --- a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp +++ b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp @@ -206,9 +206,10 @@ int StubAssembler::call_RT(Register oop_result1, Register metadata_result, addre class StubFrame: public StackObj { private: StubAssembler* _sasm; + bool _use_pop_on_epilog; public: - StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments); + StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, bool use_pop_on_epilog = false); void load_argument(int offset_in_words, Register reg); ~StubFrame(); @@ -219,15 +220,20 @@ void StubAssembler::prologue(const char* name, bool must_gc_arguments) { enter(); } -void StubAssembler::epilogue() { - leave(); +void StubAssembler::epilogue(bool use_pop) { + // Avoid using a leave instruction when this frame may + // have been frozen, since the current value of rbp + // restored from the stub would be invalid. We still + // must restore the rbp value saved on enter though. + use_pop ? pop(rbp) : leave(); ret(0); } #define __ _sasm-> -StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments) { +StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, bool use_pop_on_epilog) { _sasm = sasm; + _use_pop_on_epilog = use_pop_on_epilog; __ prologue(name, must_gc_arguments); } @@ -239,7 +245,7 @@ void StubFrame::load_argument(int offset_in_words, Register reg) { StubFrame::~StubFrame() { - __ epilogue(); + __ epilogue(_use_pop_on_epilog); } #undef __ @@ -632,6 +638,15 @@ void Runtime1::initialize_pd() { // nothing to do } +// return: offset in 64-bit words. +uint Runtime1::runtime_blob_current_thread_offset(frame f) { +#ifdef _LP64 + return r15_off / 2; // rsp offsets are in halfwords +#else + Unimplemented(); + return 0; +#endif +} // Target: the entry point of the method that creates and posts the exception oop. // has_argument: true if the exception needs arguments (passed on the stack because @@ -1308,7 +1323,7 @@ OopMapSet* Runtime1::generate_code_for(C1StubId id, StubAssembler* sasm) { // fall through case C1StubId::monitorenter_id: { - StubFrame f(sasm, "monitorenter", dont_gc_arguments); + StubFrame f(sasm, "monitorenter", dont_gc_arguments, true /* use_pop_on_epilog */); OopMap* map = save_live_registers(sasm, 3, save_fpu_registers); // Called with store_parameter and not C abi diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 13200252bef73..06d93ddea26ee 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -311,61 +311,24 @@ void C2_MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmp // The object is inflated. tmpReg contains pointer to ObjectMonitor* + markWord::monitor_value #ifndef _LP64 - // The object is inflated. - - // boxReg refers to the on-stack BasicLock in the current frame. - // We'd like to write: - // set box->_displaced_header = markWord::unused_mark(). Any non-0 value suffices. - // This is convenient but results a ST-before-CAS penalty. The following CAS suffers - // additional latency as we have another ST in the store buffer that must drain. - - // avoid ST-before-CAS - // register juggle because we need tmpReg for cmpxchgptr below - movptr(scrReg, boxReg); - movptr(boxReg, tmpReg); // consider: LEA box, [tmp-2] - - // Optimistic form: consider XORL tmpReg,tmpReg - movptr(tmpReg, NULL_WORD); - - // Appears unlocked - try to swing _owner from null to non-null. - // Ideally, I'd manifest "Self" with get_thread and then attempt - // to CAS the register containing Self into m->Owner. - // But we don't have enough registers, so instead we can either try to CAS - // rsp or the address of the box (in scr) into &m->owner. If the CAS succeeds - // we later store "Self" into m->Owner. Transiently storing a stack address - // (rsp or the address of the box) into m->owner is harmless. - // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. - lock(); - cmpxchgptr(scrReg, Address(boxReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); - movptr(Address(scrReg, 0), 3); // box->_displaced_header = 3 - // If we weren't able to swing _owner from null to the BasicLock - // then take the slow path. - jccb (Assembler::notZero, NO_COUNT); - // update _owner from BasicLock to thread - get_thread (scrReg); // beware: clobbers ICCs - movptr(Address(boxReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), scrReg); - xorptr(boxReg, boxReg); // set icc.ZFlag = 1 to indicate success - - // If the CAS fails we can either retry or pass control to the slow path. - // We use the latter tactic. - // Pass the CAS result in the icc.ZFlag into DONE_LABEL - // If the CAS was successful ... - // Self has acquired the lock - // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. - // Intentional fall-through into DONE_LABEL ... -#else // _LP64 + // Just take slow path to avoid dealing with 64 bit atomic instructions here. + orl(boxReg, 1); // set ICC.ZF=0 to indicate failure +#else + // Unconditionally set box->_displaced_header = markWord::unused_mark(). + // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. + movptr(Address(boxReg, 0), checked_cast(markWord::unused_mark().value())); + // It's inflated and we use scrReg for ObjectMonitor* in this section. + movptr(boxReg, Address(r15_thread, JavaThread::lock_id_offset())); movq(scrReg, tmpReg); xorq(tmpReg, tmpReg); lock(); - cmpxchgptr(thread, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); - // Unconditionally set box->_displaced_header = markWord::unused_mark(). - // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. - movptr(Address(boxReg, 0), checked_cast(markWord::unused_mark().value())); + cmpxchgptr(boxReg, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); + // Propagate ICC.ZF from CAS above into DONE_LABEL. - jccb(Assembler::equal, COUNT); // CAS above succeeded; propagate ZF = 1 (success) + jccb(Assembler::equal, COUNT); // CAS above succeeded; propagate ZF = 1 (success) - cmpptr(thread, rax); // Check if we are already the owner (recursive lock) + cmpptr(boxReg, rax); // Check if we are already the owner (recursive lock) jccb(Assembler::notEqual, NO_COUNT); // If not recursive, ZF = 0 at this point (fail) incq(Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); xorq(rax, rax); // Set ZF = 1 (success) for recursive lock, denoting locking success @@ -377,9 +340,12 @@ void C2_MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmp jccb(Assembler::notZero, NO_COUNT); // jump if ZFlag == 0 bind(COUNT); - // Count monitors in fast path - increment(Address(thread, JavaThread::held_monitor_count_offset())); - + if (LockingMode == LM_LEGACY) { +#ifdef _LP64 + // Count monitors in fast path + increment(Address(thread, JavaThread::held_monitor_count_offset())); +#endif + } xorl(tmpReg, tmpReg); // Set ZF == 1 bind(NO_COUNT); @@ -441,6 +407,11 @@ void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register t // It's inflated. +#ifndef _LP64 + // Just take slow path to avoid dealing with 64 bit atomic instructions here. + orl(boxReg, 1); // set ICC.ZF=0 to indicate failure + jmpb(DONE_LABEL); +#else // Despite our balanced locking property we still check that m->_owner == Self // as java routines or native JNI code called by this thread might // have released the lock. @@ -489,12 +460,7 @@ void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register t // Save the monitor pointer in the current thread, so we can try to // reacquire the lock in SharedRuntime::monitor_exit_helper(). andptr(tmpReg, ~(int32_t)markWord::monitor_value); -#ifndef _LP64 - get_thread(boxReg); - movptr(Address(boxReg, JavaThread::unlocked_inflated_monitor_offset()), tmpReg); -#else // _LP64 movptr(Address(r15_thread, JavaThread::unlocked_inflated_monitor_offset()), tmpReg); -#endif orl (boxReg, 1); // set ICC.ZF=0 to indicate failure jmpb (DONE_LABEL); @@ -502,6 +468,7 @@ void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register t bind (LSuccess); testl (boxReg, 0); // set ICC.ZF=1 to indicate success jmpb (DONE_LABEL); +#endif // _LP64 if (LockingMode == LM_LEGACY) { bind (Stacked); @@ -518,13 +485,13 @@ void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register t jccb(Assembler::notZero, NO_COUNT); bind(COUNT); - // Count monitors in fast path -#ifndef _LP64 - get_thread(tmpReg); - decrementl(Address(tmpReg, JavaThread::held_monitor_count_offset())); -#else // _LP64 - decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); + + if (LockingMode == LM_LEGACY) { + // Count monitors in fast path +#ifdef _LP64 + decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); #endif + } xorl(tmpReg, tmpReg); // Set ZF == 1 @@ -602,6 +569,11 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist { // Handle inflated monitor. bind(inflated); +#ifndef _LP64 + // Just take slow path to avoid dealing with 64 bit atomic instructions here. + orl(box, 1); // set ICC.ZF=0 to indicate failure + jmpb(slow_path); +#else const Register monitor = t; if (!UseObjectMonitorTable) { @@ -647,27 +619,30 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist Label monitor_locked; // Lock the monitor. - // CAS owner (null => current thread). + if (UseObjectMonitorTable) { + // Cache the monitor for unlock before trashing box. On failure to acquire + // the lock, the slow path will reset the entry accordingly (see CacheSetter). + movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor); + } + + // Try to CAS owner (no owner => current thread's _lock_id). xorptr(rax_reg, rax_reg); - lock(); cmpxchgptr(thread, owner_address); + movptr(box, Address(thread, JavaThread::lock_id_offset())); + lock(); cmpxchgptr(box, owner_address); jccb(Assembler::equal, monitor_locked); // Check if recursive. - cmpptr(thread, rax_reg); + cmpptr(box, rax_reg); jccb(Assembler::notEqual, slow_path); // Recursive. increment(recursions_address); bind(monitor_locked); - if (UseObjectMonitorTable) { - // Cache the monitor for unlock - movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor); - } +#endif // _LP64 } bind(locked); - increment(Address(thread, JavaThread::held_monitor_count_offset())); // Set ZF = 1 xorl(rax_reg, rax_reg); @@ -777,6 +752,11 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, bind(inflated); +#ifndef _LP64 + // Just take slow path to avoid dealing with 64 bit atomic instructions here. + orl(t, 1); // set ICC.ZF=0 to indicate failure + jmpb(slow_path); +#else if (!UseObjectMonitorTable) { assert(mark == monitor, "should be the same here"); } else { @@ -828,10 +808,10 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, // Recursive unlock. bind(recursive); decrement(recursions_address); +#endif // _LP64 } bind(unlocked); - decrement(Address(thread, JavaThread::held_monitor_count_offset())); xorl(t, t); // Fast Unlock ZF = 1 #ifdef ASSERT diff --git a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp index ecc2d81e67253..ba8fcb3aa9c51 100644 --- a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -126,6 +126,11 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { } } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + assert(f.interpreter_frame_last_sp() == nullptr, "should be null for top frame"); + f.interpreter_frame_set_last_sp(f.unextended_sp()); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { assert(hf.fp() == hf.unextended_sp() + (f.fp() - f.unextended_sp()), ""); assert((f.at(frame::interpreter_frame_last_sp_offset) != 0) @@ -136,7 +141,10 @@ inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, co assert((intptr_t*)hf.at_relative(frame::interpreter_frame_last_sp_offset) == hf.unextended_sp(), ""); // Make sure that locals is already relativized. - assert((*hf.addr_at(frame::interpreter_frame_locals_offset) == frame::sender_sp_offset + f.interpreter_frame_method()->max_locals() - 1), ""); + DEBUG_ONLY(Method* m = f.interpreter_frame_method();) + // Frames for native methods have 2 extra words (temp oop/result handler) before fixed part of frame. + DEBUG_ONLY(int max_locals = !m->is_native() ? m->max_locals() : m->size_of_parameters() + 2;) + assert((*hf.addr_at(frame::interpreter_frame_locals_offset) == frame::sender_sp_offset + max_locals - 1), ""); // Make sure that monitor_block_top is already relativized. assert(hf.at_absolute(frame::interpreter_frame_monitor_block_top_offset) <= frame::interpreter_frame_initial_sp_offset, ""); @@ -207,7 +215,6 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& // If caller is interpreted it already made room for the callee arguments int overlap = caller.is_interpreted_frame() ? ContinuationHelper::InterpretedFrame::stack_argsize(hf) : 0; const int fsize = (int)(ContinuationHelper::InterpretedFrame::frame_bottom(hf) - hf.unextended_sp() - overlap); - const int locals = hf.interpreter_frame_method()->max_locals(); intptr_t* frame_sp = caller.unextended_sp() - fsize; intptr_t* fp = frame_sp + (hf.fp() - heap_sp); DEBUG_ONLY(intptr_t* unextended_sp = fp + *hf.addr_at(frame::interpreter_frame_last_sp_offset);) @@ -217,7 +224,10 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& // we need to set the locals so that the caller of new_stack_frame() can call // ContinuationHelper::InterpretedFrame::frame_bottom intptr_t locals_offset = *hf.addr_at(frame::interpreter_frame_locals_offset); - assert((int)locals_offset == frame::sender_sp_offset + locals - 1, ""); + DEBUG_ONLY(Method* m = hf.interpreter_frame_method();) + // Frames for native methods have 2 extra words (temp oop/result handler) before fixed part of frame. + DEBUG_ONLY(const int max_locals = !m->is_native() ? m->max_locals() : m->size_of_parameters() + 2;) + assert((int)locals_offset == frame::sender_sp_offset + max_locals - 1, ""); // copy relativized locals from the heap frame *f.addr_at(frame::interpreter_frame_locals_offset) = locals_offset; return f; @@ -225,7 +235,7 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& int fsize = FKind::size(hf); intptr_t* frame_sp = caller.unextended_sp() - fsize; if (bottom || caller.is_interpreted_frame()) { - int argsize = hf.compiled_frame_stack_argsize(); + int argsize = FKind::stack_argsize(hf); fsize += argsize; frame_sp -= argsize; @@ -242,8 +252,9 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& // we need to recreate a "real" frame pointer, pointing into the stack fp = frame_sp + FKind::size(hf) - frame::sender_sp_offset; } else { - // we need to re-read fp because it may be an oop and we might have fixed the frame. - fp = *(intptr_t**)(hf.sp() - frame::sender_sp_offset); + fp = FKind::stub || FKind::native + ? frame_sp + fsize - frame::sender_sp_offset // fp always points to the address below the pushed return pc. We need correct address. + : *(intptr_t**)(hf.sp() - frame::sender_sp_offset); // we need to re-read fp because it may be an oop and we might have fixed the frame. } return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); // TODO PERF : this computes deopt state; is it necessary? } @@ -266,6 +277,22 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) { patch_callee_link(caller, caller.fp()); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + intptr_t* fp = caller_sp - frame::sender_sp_offset; + patch_callee_link(f, fp); +} + +inline intptr_t* ThawBase::push_cleanup_continuation() { + frame enterSpecial = new_entry_frame(); + intptr_t* sp = enterSpecial.sp(); + + sp[-1] = (intptr_t)ContinuationEntry::cleanup_pc(); + sp[-2] = (intptr_t)enterSpecial.fp(); + + log_develop_trace(continuations, preempt)("push_cleanup_continuation initial sp: " INTPTR_FORMAT " final sp: " INTPTR_FORMAT, p2i(sp + 2 * frame::metadata_words), p2i(sp)); + return sp; +} + inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { // Make sure that last_sp is kept relativized. assert((intptr_t*)f.at_relative(frame::interpreter_frame_last_sp_offset) == f.unextended_sp(), ""); diff --git a/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp b/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp index dab6cec2a5fa5..46fe0946951e5 100644 --- a/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,20 @@ static inline intptr_t** link_address(const frame& f) { : (intptr_t**)(f.unextended_sp() + f.cb()->frame_size() - frame::sender_sp_offset); } +static inline void patch_return_pc_with_preempt_stub(frame& f) { + if (f.is_runtime_frame()) { + // Patch the pc of the now old last Java frame (we already set the anchor to enterSpecial) + // so that when target goes back to Java it will actually return to the preempt cleanup stub. + intptr_t* sp = f.sp(); + sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub(); + } else { + // The target will check for preemption once it returns to the interpreter + // or the native wrapper code and will manually jump to the preempt stub. + JavaThread *thread = JavaThread::current(); + thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub()); + } +} + inline int ContinuationHelper::frame_align_words(int size) { #ifdef _LP64 return size & 1; @@ -72,12 +86,12 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, anchor->set_last_Java_fp(entry->entry_fp()); } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { intptr_t* fp = *(intptr_t**)(sp - frame::sender_sp_offset); anchor->set_last_Java_fp(fp); } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { intptr_t* sp = f.sp(); address pc = *(address*)(sp - frame::sender_sp_ret_address_offset()); diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp index aeb77763e7fff..4e28dc125341a 100644 --- a/src/hotspot/cpu/x86/frame_x86.cpp +++ b/src/hotspot/cpu/x86/frame_x86.cpp @@ -409,6 +409,36 @@ frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { return fr; } +#if defined(ASSERT) +static address get_register_address_in_stub(const frame& stub_fr, VMReg reg) { + RegisterMap map(nullptr, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + stub_fr.oop_map()->update_register_map(&stub_fr, &map); + return map.location(reg, stub_fr.sp()); +} +#endif + +JavaThread** frame::saved_thread_address(const frame& f) { + CodeBlob* cb = f.cb(); + assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame"); + + JavaThread** thread_addr; +#ifdef COMPILER1 + if (cb == Runtime1::blob_for(C1StubId::monitorenter_id) || + cb == Runtime1::blob_for(C1StubId::monitorenter_nofpu_id)) { + thread_addr = (JavaThread**)(f.sp() + Runtime1::runtime_blob_current_thread_offset(f)); + } else +#endif + { + // c2 only saves rbp in the stub frame so nothing to do. + thread_addr = nullptr; + } + assert(get_register_address_in_stub(f, SharedRuntime::thread_register()) == (address)thread_addr, "wrong thread address"); + return thread_addr; +} + //------------------------------------------------------------------------------ // frame::verify_deopt_original_pc // diff --git a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp index 12ac26aff21ac..873cfbdcea0ec 100644 --- a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp +++ b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp @@ -34,7 +34,9 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define SUPPORTS_NATIVE_CX8 +#ifdef _LP64 #define SUPPORT_MONITOR_COUNT +#endif #define CPU_MULTI_COPY_ATOMIC diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index f4b95d846c61e..3a3f01a640983 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -336,6 +336,66 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, restore_locals(); } +#ifdef _LP64 +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1) { + assert(arg_1 == c_rarg1, ""); + Label resume_pc, not_preempted; + +#ifdef ASSERT + { + Label L; + cmpptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); + jcc(Assembler::equal, L); + stop("Should not have alternate return address set"); + bind(L); + } +#endif /* ASSERT */ + + // Force freeze slow path. + push_cont_fastpath(); + + // Make VM call. In case of preemption set last_pc to the one we want to resume to. + // Note: call_VM_helper requires last_Java_pc for anchor to be at the top of the stack. + lea(rscratch1, resume_pc); + push(rscratch1); + MacroAssembler::call_VM_helper(oop_result, entry_point, 1, false /*check_exceptions*/); + pop(rscratch1); + + pop_cont_fastpath(); + + // Check if preempted. + movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); + cmpptr(rscratch1, NULL_WORD); + jccb(Assembler::zero, not_preempted); + movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); + jmp(rscratch1); + + // In case of preemption, this is where we will resume once we finally acquire the monitor. + bind(resume_pc); + restore_after_resume(false /* is_native */); + + bind(not_preempted); +} + +void InterpreterMacroAssembler::restore_after_resume(bool is_native) { + lea(rscratch1, ExternalAddress(Interpreter::cont_resume_interpreter_adapter())); + call(rscratch1); + if (is_native) { + // On resume we need to set up stack as expected. + push(dtos); + push(ltos); + } +} +#else +void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1) { + MacroAssembler::call_VM(oop_result, entry_point, arg_1); +} +#endif // _LP64 + void InterpreterMacroAssembler::check_and_handle_popframe(Register java_thread) { if (JvmtiExport::can_pop_frame()) { Label L; @@ -1154,7 +1214,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) { "The argument is only for looks. It must be c_rarg1"); if (LockingMode == LM_MONITOR) { - call_VM(noreg, + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); } else { @@ -1241,14 +1301,14 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) { jcc(Assembler::notZero, slow_case); bind(count_locking); + inc_held_monitor_count(); } - inc_held_monitor_count(); jmp(done); bind(slow_case); // Call the runtime routine for slow case - call_VM(noreg, + call_VM_preemptable(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg); bind(done); @@ -1321,8 +1381,8 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) { jcc(Assembler::notZero, slow_case); bind(count_locking); + dec_held_monitor_count(); } - dec_held_monitor_count(); jmp(done); bind(slow_case); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp index a23058486c34e..a122161b1c79a 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp @@ -63,6 +63,11 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_earlyret_value(TosState state); + void call_VM_preemptable(Register oop_result, + address entry_point, + Register arg_1); + void restore_after_resume(bool is_native); + // Interpreter-specific registers void save_bcp() { movptr(Address(rbp, frame::interpreter_frame_bcp_offset * wordSize), _bcp_register); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 344759bd0a898..9854aa5e55a16 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -35,6 +35,7 @@ #include "gc/shared/tlab_globals.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" #include "jvm.h" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -528,7 +529,6 @@ void MacroAssembler::call_VM_leaf_base(address entry_point, int num_args) { // restore stack pointer addq(rsp, frame::arg_reg_save_area_bytes); #endif - } void MacroAssembler::cmp64(Register src1, AddressLiteral src2, Register rscratch) { @@ -3040,25 +3040,13 @@ void MacroAssembler::pop_cont_fastpath() { } void MacroAssembler::inc_held_monitor_count() { -#ifndef _LP64 - Register thread = rax; - push(thread); - get_thread(thread); - incrementl(Address(thread, JavaThread::held_monitor_count_offset())); - pop(thread); -#else // LP64 +#ifdef _LP64 incrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); #endif } void MacroAssembler::dec_held_monitor_count() { -#ifndef _LP64 - Register thread = rax; - push(thread); - get_thread(thread); - decrementl(Address(thread, JavaThread::held_monitor_count_offset())); - pop(thread); -#else // LP64 +#ifdef _LP64 decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); #endif } @@ -3155,6 +3143,17 @@ void MacroAssembler::set_last_Java_frame(Register java_thread, movptr(Address(java_thread, JavaThread::last_Java_sp_offset()), last_java_sp); } +#ifdef _LP64 +void MacroAssembler::set_last_Java_frame(Register last_java_sp, + Register last_java_fp, + Label &L, + Register scratch) { + lea(scratch, L); + movptr(Address(r15_thread, JavaThread::last_Java_pc_offset()), scratch); + set_last_Java_frame(r15_thread, last_java_sp, last_java_fp, nullptr, scratch); +} +#endif + void MacroAssembler::shlptr(Register dst, int imm8) { LP64_ONLY(shlq(dst, imm8)) NOT_LP64(shll(dst, imm8)); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 42c2d6b64cdd4..c6e5b2a115f03 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -111,7 +111,8 @@ class MacroAssembler: public Assembler { op == 0xEB /* short jmp */ || (op & 0xF0) == 0x70 /* short jcc */ || (op == 0x0F && (branch[1] & 0xF0) == 0x80) /* jcc */ || - (op == 0xC7 && branch[1] == 0xF8) /* xbegin */, + (op == 0xC7 && branch[1] == 0xF8) /* xbegin */ || + (op == 0x8D) /* lea */, "Invalid opcode at patch point"); if (op == 0xEB || (op & 0xF0) == 0x70) { @@ -122,7 +123,7 @@ class MacroAssembler: public Assembler { file == nullptr ? "" : file, line); *disp = (char)imm8; } else { - int* disp = (int*) &branch[(op == 0x0F || op == 0xC7)? 2: 1]; + int* disp = (int*) &branch[(op == 0x0F || op == 0xC7 || op == 0x8D) ? 2 : 1]; int imm32 = checked_cast(target - (address) &disp[1]); *disp = imm32; } @@ -335,6 +336,13 @@ class MacroAssembler: public Assembler { address last_java_pc, Register rscratch); +#ifdef _LP64 + void set_last_Java_frame(Register last_java_sp, + Register last_java_fp, + Label &last_java_pc, + Register scratch); +#endif + void reset_last_Java_frame(Register thread, bool clear_fp); // thread in the default location (r15_thread on 64bit) @@ -954,7 +962,7 @@ class MacroAssembler: public Assembler { void atomic_incptr(AddressLiteral counter_addr, Register rscratch = noreg) { LP64_ONLY(atomic_incq(counter_addr, rscratch)) NOT_LP64(atomic_incl(counter_addr, rscratch)) ; } void atomic_incptr(Address counter_addr) { LP64_ONLY(atomic_incq(counter_addr)) NOT_LP64(atomic_incl(counter_addr)) ; } - void lea(Register dst, Address adr) { Assembler::lea(dst, adr); } + using Assembler::lea; void lea(Register dst, AddressLiteral adr); void lea(Address dst, AddressLiteral adr, Register rscratch); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp index c6ab00cc9eefa..ee99f49321a23 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp @@ -2056,6 +2056,11 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } +VMReg SharedRuntime::thread_register() { + Unimplemented(); + return nullptr; +} + //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { // allocate space for the code diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 174e2e0277903..b98a17848df9f 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -171,6 +171,7 @@ class RegisterSaver { static int rax_offset_in_bytes(void) { return BytesPerInt * rax_off; } static int rdx_offset_in_bytes(void) { return BytesPerInt * rdx_off; } static int rbx_offset_in_bytes(void) { return BytesPerInt * rbx_off; } + static int r15_offset_in_bytes(void) { return BytesPerInt * r15_off; } static int xmm0_offset_in_bytes(void) { return BytesPerInt * xmm0_off; } static int return_offset_in_bytes(void) { return BytesPerInt * return_off; } @@ -1420,7 +1421,7 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, // Kills: // rbx // -void static continuation_enter_cleanup(MacroAssembler* masm) { +static void continuation_enter_cleanup(MacroAssembler* masm) { #ifdef ASSERT Label L_good_sp; __ cmpptr(rsp, Address(r15_thread, JavaThread::cont_entry_offset())); @@ -1610,6 +1611,7 @@ static void gen_continuation_enter(MacroAssembler* masm, __ bind(L_thaw); + ContinuationEntry::_thaw_call_pc_offset = __ pc() - start; __ call(RuntimeAddress(StubRoutines::cont_thaw())); ContinuationEntry::_return_pc_offset = __ pc() - start; @@ -1619,7 +1621,7 @@ static void gen_continuation_enter(MacroAssembler* masm, // --- Normal exit (resolve/thawing) __ bind(L_exit); - + ContinuationEntry::_cleanup_offset = __ pc() - start; continuation_enter_cleanup(masm); __ pop(rbp); __ ret(0); @@ -1712,6 +1714,10 @@ static void gen_continuation_yield(MacroAssembler* masm, __ ret(0); } +void SharedRuntime::continuation_enter_cleanup(MacroAssembler* masm) { + ::continuation_enter_cleanup(masm); +} + static void gen_special_dispatch(MacroAssembler* masm, const methodHandle& method, const BasicType* sig_bt, @@ -2180,11 +2186,16 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // points into the right code segment. It does not have to be the correct return pc. // We use the same pc/oopMap repeatedly when we call out - intptr_t the_pc = (intptr_t) __ pc(); - oop_maps->add_gc_map(the_pc - start, map); - - __ set_last_Java_frame(rsp, noreg, (address)the_pc, rscratch1); + Label native_return; + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + // For convenience we use the pc we want to resume to in case of preemption on Object.wait. + __ set_last_Java_frame(rsp, noreg, native_return, rscratch1); + } else { + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + __ set_last_Java_frame(rsp, noreg, __ pc(), rscratch1); + } // We have all of the arguments setup at this point. We must not touch any register // argument registers at this point (what if we save/restore them there are no oop? @@ -2271,12 +2282,13 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Save the test result, for recursive case, the result is zero __ movptr(Address(lock_reg, mark_word_offset), swap_reg); __ jcc(Assembler::notEqual, slow_path_lock); + + __ bind(count_mon); + __ inc_held_monitor_count(); } else { assert(LockingMode == LM_LIGHTWEIGHT, "must be"); __ lightweight_lock(lock_reg, obj_reg, swap_reg, r15_thread, rscratch1, slow_path_lock); } - __ bind(count_mon); - __ inc_held_monitor_count(); // Slow path will re-enter here __ bind(lock_done); @@ -2367,6 +2379,20 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); __ bind(after_transition); + if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + // Check preemption for Object.wait() + __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); + __ cmpptr(rscratch1, NULL_WORD); + __ jccb(Assembler::equal, native_return); + __ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); + __ jmp(rscratch1); + __ bind(native_return); + + intptr_t the_pc = (intptr_t) __ pc(); + oop_maps->add_gc_map(the_pc - start, map); + } + + Label reguard; Label reguard_done; __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled); @@ -2416,7 +2442,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, } else { assert(LockingMode == LM_LIGHTWEIGHT, "must be"); __ lightweight_unlock(obj_reg, swap_reg, r15_thread, lock_reg, slow_path_unlock); - __ dec_held_monitor_count(); } // slow path re-enters here @@ -2490,8 +2515,14 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ mov(c_rarg1, lock_reg); __ mov(c_rarg2, r15_thread); - // Not a leaf but we have last_Java_frame setup as we want + // Not a leaf but we have last_Java_frame setup as we want. + // We don't want to unmount in case of contention since that would complicate preserving + // the arguments that had already been marshalled into the native convention. So we force + // the freeze slow path to find this native wrapper frame (see recurse_freeze_native_frame()) + // and pin the vthread. Otherwise the fast path won't find it since we don't walk the stack. + __ push_cont_fastpath(); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), 3); + __ pop_cont_fastpath(); restore_args(masm, total_c_args, c_arg, out_regs); #ifdef ASSERT @@ -2606,6 +2637,10 @@ uint SharedRuntime::in_preserve_stack_slots() { return 4 + 2 * VerifyStackAtCalls; } +VMReg SharedRuntime::thread_register() { + return r15_thread->as_VMReg(); +} + //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { // Allocate space for the code diff --git a/src/hotspot/cpu/x86/stackChunkFrameStream_x86.inline.hpp b/src/hotspot/cpu/x86/stackChunkFrameStream_x86.inline.hpp index d69facadbeafd..6289b903ab1e4 100644 --- a/src/hotspot/cpu/x86/stackChunkFrameStream_x86.inline.hpp +++ b/src/hotspot/cpu/x86/stackChunkFrameStream_x86.inline.hpp @@ -114,6 +114,7 @@ inline int StackChunkFrameStream::interpreter_frame_num_oops() const f.interpreted_frame_oop_map(&mask); return mask.num_oops() + 1 // for the mirror oop + + (f.interpreter_frame_method()->is_native() ? 1 : 0) // temp oop slot + pointer_delta_as_int((intptr_t*)f.interpreter_frame_monitor_begin(), (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 82d2fd1e73b96..3979237619c92 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -35,6 +35,7 @@ #include "prims/jvmtiExport.hpp" #include "prims/upcallLinker.hpp" #include "runtime/arguments.hpp" +#include "runtime/continuationEntry.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" @@ -3781,6 +3782,36 @@ address StubGenerator::generate_cont_returnBarrier_exception() { return generate_cont_thaw("Cont thaw return barrier exception", Continuation::thaw_return_barrier_exception); } +address StubGenerator::generate_cont_preempt_stub() { + if (!Continuations::enabled()) return nullptr; + StubCodeMark mark(this, "StubRoutines","Continuation preempt stub"); + address start = __ pc(); + + __ reset_last_Java_frame(true); + + // Set rsp to enterSpecial frame, i.e. remove all frames copied into the heap. + __ movptr(rsp, Address(r15_thread, JavaThread::cont_entry_offset())); + + Label preemption_cancelled; + __ movbool(rscratch1, Address(r15_thread, JavaThread::preemption_cancelled_offset())); + __ testbool(rscratch1); + __ jcc(Assembler::notZero, preemption_cancelled); + + // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. + SharedRuntime::continuation_enter_cleanup(_masm); + __ pop(rbp); + __ ret(0); + + // We acquired the monitor after freezing the frames so call thaw to continue execution. + __ bind(preemption_cancelled); + __ movbool(Address(r15_thread, JavaThread::preemption_cancelled_offset()), false); + __ lea(rbp, Address(rsp, checked_cast(ContinuationEntry::size()))); + __ movptr(rscratch1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); + __ jmp(rscratch1); + + return start; +} + // exception handler for upcall stubs address StubGenerator::generate_upcall_stub_exception_handler() { StubCodeMark mark(this, "StubRoutines", "upcall stub exception handler"); @@ -3953,6 +3984,7 @@ void StubGenerator::generate_continuation_stubs() { StubRoutines::_cont_thaw = generate_cont_thaw(); StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); + StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); } void StubGenerator::generate_final_stubs() { diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index c6fa31c5213b0..f883b6453a690 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -602,6 +602,8 @@ class StubGenerator: public StubCodeGenerator { address generate_cont_returnBarrier(); address generate_cont_returnBarrier_exception(); + address generate_cont_preempt_stub(); + // Continuation point for throwing of implicit exceptions that are // not handled in the current activation. Fabricates an exception // oop and initiates normal exception dispatching in this diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index f866423d1231e..af57fda23fd21 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -34,7 +34,7 @@ static bool returns_to_call_stub(address return_pc) { return return_pc == _call_ enum platform_dependent_constants { // simply increase sizes if too small (assembler will crash if too small) _initial_stubs_code_size = 20000 WINDOWS_ONLY(+1000), - _continuation_stubs_code_size = 1000 LP64_ONLY(+1000), + _continuation_stubs_code_size = 1000 LP64_ONLY(+2000), // AVX512 intrinsics add more code in 64-bit VM, // Windows have more code to save/restore registers _compiler_stubs_code_size = 20000 LP64_ONLY(+47000) WINDOWS_ONLY(+2000), diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index 76ad498be0e78..beb80d83e2ecd 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -387,6 +387,26 @@ address TemplateInterpreterGenerator::generate_safept_entry_for( return entry; } +address TemplateInterpreterGenerator::generate_cont_resume_interpreter_adapter() { + if (!Continuations::enabled()) return nullptr; + address start = __ pc(); + + __ restore_bcp(); + __ restore_locals(); + + // Get return address before adjusting rsp + __ movptr(rax, Address(rsp, 0)); + + // Restore stack bottom + __ movptr(rcx, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ lea(rsp, Address(rbp, rcx, Address::times_ptr)); + // and NULL it as marker that esp is now tos until next java call + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + + __ jmp(rax); + + return start; +} // Helpers for commoning out cases in the various type of method entries. @@ -1029,7 +1049,10 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // It is enough that the pc() points into the right code // segment. It does not have to be the correct return pc. - __ set_last_Java_frame(rsp, rbp, (address) __ pc(), rscratch1); + // For convenience we use the pc we want to resume to in + // case of preemption on Object.wait. + Label native_return; + __ set_last_Java_frame(rsp, rbp, native_return, rscratch1); #endif // _LP64 // change thread state @@ -1049,11 +1072,15 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_native); + __ push_cont_fastpath(); + // Call the native method. __ call(rax); // 32: result potentially in rdx:rax or ST0 // 64: result potentially in rax or xmm0 + __ pop_cont_fastpath(); + // Verify or restore cpu control state after JNI call __ restore_cpu_control_state_after_jni(rscratch1); @@ -1077,10 +1104,10 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { Label push_double; ExternalAddress float_handler(AbstractInterpreter::result_handler(T_FLOAT)); ExternalAddress double_handler(AbstractInterpreter::result_handler(T_DOUBLE)); - __ cmpptr(Address(rbp, (frame::interpreter_frame_oop_temp_offset + 1)*wordSize), + __ cmpptr(Address(rbp, (frame::interpreter_frame_result_handler_offset)*wordSize), float_handler.addr(), noreg); __ jcc(Assembler::equal, push_double); - __ cmpptr(Address(rbp, (frame::interpreter_frame_oop_temp_offset + 1)*wordSize), + __ cmpptr(Address(rbp, (frame::interpreter_frame_result_handler_offset)*wordSize), double_handler.addr(), noreg); __ jcc(Assembler::notEqual, L); __ bind(push_double); @@ -1150,6 +1177,24 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // change thread state __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java); +#ifdef _LP64 + if (LockingMode != LM_LEGACY) { + // Check preemption for Object.wait() + Label not_preempted; + __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); + __ cmpptr(rscratch1, NULL_WORD); + __ jccb(Assembler::equal, not_preempted); + __ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); + __ jmp(rscratch1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + __ bind(not_preempted); + } else { + // any pc will do so just use this one for LM_LEGACY to keep code together. + __ bind(native_return); + } +#endif // _LP64 + // reset_last_Java_frame __ reset_last_Java_frame(thread, true); diff --git a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp index cb05ab1389efe..b1bd138e3574d 100644 --- a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp @@ -48,6 +48,10 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { Unimplemented(); } +inline void FreezeBase::prepare_freeze_interpreted_top_frame(frame& f) { + Unimplemented(); +} + inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { Unimplemented(); } @@ -83,6 +87,15 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) { Unimplemented(); } +inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { + Unimplemented(); +} + +inline intptr_t* ThawBase::push_cleanup_continuation() { + Unimplemented(); + return nullptr; +} + template inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { Unimplemented(); diff --git a/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp b/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp index 56da168d9b1d4..9829d32dc7fb7 100644 --- a/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp @@ -33,6 +33,10 @@ static inline intptr_t** link_address(const frame& f) { return nullptr; } +static inline void patch_return_pc_with_preempt_stub(frame& f) { + Unimplemented(); +} + inline int ContinuationHelper::frame_align_words(int size) { Unimplemented(); return 0; @@ -60,11 +64,11 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, Unimplemented(); } -#ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { Unimplemented(); } +#ifdef ASSERT inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { Unimplemented(); return false; diff --git a/src/hotspot/cpu/zero/frame_zero.cpp b/src/hotspot/cpu/zero/frame_zero.cpp index 5ddd23a9d59ef..400766298f572 100644 --- a/src/hotspot/cpu/zero/frame_zero.cpp +++ b/src/hotspot/cpu/zero/frame_zero.cpp @@ -72,6 +72,11 @@ bool frame::upcall_stub_frame_is_first() const { return false; } +JavaThread** frame::saved_thread_address(const frame& f) { + Unimplemented(); + return nullptr; +} + frame frame::sender_for_nonentry_frame(RegisterMap *map) const { assert(zeroframe()->is_interpreter_frame() || zeroframe()->is_fake_stub_frame(), "wrong type of frame"); diff --git a/src/hotspot/cpu/zero/globalDefinitions_zero.hpp b/src/hotspot/cpu/zero/globalDefinitions_zero.hpp index 0f78b3eeefba2..d05e5deb0f8c9 100644 --- a/src/hotspot/cpu/zero/globalDefinitions_zero.hpp +++ b/src/hotspot/cpu/zero/globalDefinitions_zero.hpp @@ -36,8 +36,6 @@ // The default padding size for data structures to avoid false sharing. #define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE -#define SUPPORT_MONITOR_COUNT - #include // Indicates whether the C calling conventions require that diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index 18ceb9514d3be..094ddc05a2eca 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -85,6 +85,11 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } +VMReg SharedRuntime::thread_register() { + Unimplemented(); + return nullptr; +} + JRT_LEAF(void, zero_stub()) ShouldNotCallThis(); JRT_END diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp index aab43e733964e..c7e6a13983129 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp @@ -346,9 +346,6 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) { success = false; } } - if (success) { - THREAD->inc_held_monitor_count(); - } } if (!success) { CALL_VM_NOCHECK(InterpreterRuntime::monitorenter(thread, monitor)); @@ -499,9 +496,6 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) { success = false; } } - if (success) { - THREAD->dec_held_monitor_count(); - } } if (!success) { InterpreterRuntime::monitorexit(monitor); diff --git a/src/hotspot/share/c1/c1_MacroAssembler.hpp b/src/hotspot/share/c1/c1_MacroAssembler.hpp index 1e193ce086961..0fe0d0ff285f4 100644 --- a/src/hotspot/share/c1/c1_MacroAssembler.hpp +++ b/src/hotspot/share/c1/c1_MacroAssembler.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,7 +89,7 @@ class StubAssembler: public C1_MacroAssembler { int call_RT(Register oop_result1, Register metadata_result, address entry, Register arg1, Register arg2, Register arg3); void prologue(const char* name, bool must_gc_arguments); - void epilogue(); + void epilogue(bool use_pop = false); }; #endif // SHARE_C1_C1_MACROASSEMBLER_HPP diff --git a/src/hotspot/share/c1/c1_Runtime1.hpp b/src/hotspot/share/c1/c1_Runtime1.hpp index 330c40675041b..6aed71fdfe524 100644 --- a/src/hotspot/share/c1/c1_Runtime1.hpp +++ b/src/hotspot/share/c1/c1_Runtime1.hpp @@ -135,6 +135,9 @@ class Runtime1: public AllStatic { static void initialize(BufferBlob* blob); static void initialize_pd(); + // return offset in words + static uint runtime_blob_current_thread_offset(frame f); + // stubs static CodeBlob* blob_for (C1StubId id); static address entry_for(C1StubId id) { return blob_for(id)->code_begin(); } diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 3dce1a343d896..0b6704c1a5b22 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1686,6 +1686,7 @@ bool java_lang_Thread::is_in_VTMS_transition(oop java_thread) { } void java_lang_Thread::set_is_in_VTMS_transition(oop java_thread, bool val) { + assert(is_in_VTMS_transition(java_thread) != val, "already %s transition", val ? "inside" : "outside"); java_thread->bool_field_put_volatile(_jvmti_is_in_VTMS_transition_offset, val); } @@ -2021,12 +2022,20 @@ int java_lang_VirtualThread::static_vthread_scope_offset; int java_lang_VirtualThread::_carrierThread_offset; int java_lang_VirtualThread::_continuation_offset; int java_lang_VirtualThread::_state_offset; +int java_lang_VirtualThread::_next_offset; +int java_lang_VirtualThread::_onWaitingList_offset; +int java_lang_VirtualThread::_notified_offset; +int java_lang_VirtualThread::_timeout_offset; #define VTHREAD_FIELDS_DO(macro) \ macro(static_vthread_scope_offset, k, "VTHREAD_SCOPE", continuationscope_signature, true); \ macro(_carrierThread_offset, k, "carrierThread", thread_signature, false); \ macro(_continuation_offset, k, "cont", continuation_signature, false); \ - macro(_state_offset, k, "state", int_signature, false) + macro(_state_offset, k, "state", int_signature, false); \ + macro(_next_offset, k, "next", vthread_signature, false); \ + macro(_onWaitingList_offset, k, "onWaitingList", bool_signature, false); \ + macro(_notified_offset, k, "notified", bool_signature, false); \ + macro(_timeout_offset, k, "timeout", long_signature, false); void java_lang_VirtualThread::compute_offsets() { @@ -2052,6 +2061,56 @@ int java_lang_VirtualThread::state(oop vthread) { return vthread->int_field_acquire(_state_offset); } +void java_lang_VirtualThread::set_state(oop vthread, int state) { + vthread->release_int_field_put(_state_offset, state); +} + +int java_lang_VirtualThread::cmpxchg_state(oop vthread, int old_state, int new_state) { + jint* addr = vthread->field_addr(_state_offset); + int res = Atomic::cmpxchg(addr, old_state, new_state); + return res; +} + +oop java_lang_VirtualThread::next(oop vthread) { + return vthread->obj_field(_next_offset); +} + +void java_lang_VirtualThread::set_next(oop vthread, oop next_vthread) { + vthread->obj_field_put(_next_offset, next_vthread); +} + +// Add vthread to the waiting list if it's not already in it. Multiple threads +// could be trying to add vthread to the list at the same time, so we control +// access with a cmpxchg on onWaitingList. The winner adds vthread to the list. +// Method returns true if we added vthread to the list, false otherwise. +bool java_lang_VirtualThread::set_onWaitingList(oop vthread, OopHandle& list_head) { + jboolean* addr = vthread->field_addr(_onWaitingList_offset); + jboolean vthread_on_list = Atomic::load(addr); + if (!vthread_on_list) { + vthread_on_list = Atomic::cmpxchg(addr, (jboolean)JNI_FALSE, (jboolean)JNI_TRUE); + if (!vthread_on_list) { + for (;;) { + oop head = list_head.resolve(); + java_lang_VirtualThread::set_next(vthread, head); + if (list_head.cmpxchg(head, vthread) == head) return true; + } + } + } + return false; // already on waiting list +} + +void java_lang_VirtualThread::set_notified(oop vthread, jboolean value) { + vthread->bool_field_put_volatile(_notified_offset, value); +} + +jlong java_lang_VirtualThread::timeout(oop vthread) { + return vthread->long_field(_timeout_offset); +} + +void java_lang_VirtualThread::set_timeout(oop vthread, jlong value) { + vthread->long_field_put(_timeout_offset, value); +} + JavaThreadStatus java_lang_VirtualThread::map_state_to_thread_status(int state) { JavaThreadStatus status = JavaThreadStatus::NEW; switch (state & ~SUSPENDED) { @@ -2065,6 +2124,9 @@ JavaThreadStatus java_lang_VirtualThread::map_state_to_thread_status(int state) case UNPARKED: case YIELDING: case YIELDED: + case UNBLOCKED: + case WAITING: + case TIMED_WAITING: status = JavaThreadStatus::RUNNABLE; break; case PARKED: @@ -2075,6 +2137,16 @@ JavaThreadStatus java_lang_VirtualThread::map_state_to_thread_status(int state) case TIMED_PINNED: status = JavaThreadStatus::PARKED_TIMED; break; + case BLOCKING: + case BLOCKED: + status = JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER; + break; + case WAIT: + status = JavaThreadStatus::IN_OBJECT_WAIT; + break; + case TIMED_WAIT: + status = JavaThreadStatus::IN_OBJECT_WAIT_TIMED; + break; case TERMINATED: status = JavaThreadStatus::TERMINATED; break; @@ -2084,6 +2156,13 @@ JavaThreadStatus java_lang_VirtualThread::map_state_to_thread_status(int state) return status; } +bool java_lang_VirtualThread::is_preempted(oop vthread) { + oop continuation = java_lang_VirtualThread::continuation(vthread); + assert(continuation != nullptr, "vthread with no continuation"); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(continuation); + return chunk != nullptr && chunk->preempted(); +} + #if INCLUDE_CDS void java_lang_VirtualThread::serialize_offsets(SerializeClosure* f) { VTHREAD_FIELDS_DO(FIELD_SERIALIZE_OFFSET); diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 6ab73b161f4f0..c968859c591e5 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -530,6 +530,11 @@ class java_lang_VirtualThread : AllStatic { static int _carrierThread_offset; static int _continuation_offset; static int _state_offset; + static int _next_offset; + static int _onWaitingList_offset; + static int _notified_offset; + static int _recheckInterval_offset; + static int _timeout_offset; JFR_ONLY(static int _jfr_epoch_offset;) public: enum { @@ -545,6 +550,13 @@ class java_lang_VirtualThread : AllStatic { UNPARKED = 9, YIELDING = 10, YIELDED = 11, + BLOCKING = 12, + BLOCKED = 13, + UNBLOCKED = 14, + WAITING = 15, + WAIT = 16, // waiting in Object.wait + TIMED_WAITING = 17, + TIMED_WAIT = 18, // waiting in timed-Object.wait TERMINATED = 99, // additional state bits @@ -564,6 +576,15 @@ class java_lang_VirtualThread : AllStatic { static oop carrier_thread(oop vthread); static oop continuation(oop vthread); static int state(oop vthread); + static void set_state(oop vthread, int state); + static int cmpxchg_state(oop vthread, int old_state, int new_state); + static oop next(oop vthread); + static void set_next(oop vthread, oop next_vthread); + static bool set_onWaitingList(oop vthread, OopHandle& list_head); + static jlong timeout(oop vthread); + static void set_timeout(oop vthread, jlong value); + static void set_notified(oop vthread, jboolean value); + static bool is_preempted(oop vthread); static JavaThreadStatus map_state_to_thread_status(int state); }; diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index d8018cd0c8af2..d42e718a155d2 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -516,6 +516,8 @@ class SerializeClosure; template(checkIndex_name, "checkIndex") \ template(jfr_epoch_name, "jfr_epoch") \ template(maxThawingSize_name, "maxThawingSize") \ + template(lockStackSize_name, "lockStackSize") \ + template(objectWaiter_name, "objectWaiter") \ \ /* name symbols needed by intrinsics */ \ VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, template, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \ @@ -564,6 +566,7 @@ class SerializeClosure; template(continuation_signature, "Ljdk/internal/vm/Continuation;") \ template(continuationscope_signature, "Ljdk/internal/vm/ContinuationScope;") \ template(stackchunk_signature, "Ljdk/internal/vm/StackChunk;") \ + template(vthread_signature, "Ljava/lang/VirtualThread;") \ template(object_void_signature, "(Ljava/lang/Object;)V") \ template(object_int_signature, "(Ljava/lang/Object;)I") \ template(long_object_long_signature, "(JLjava/lang/Object;)J") \ diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 7fb72997749dc..754a7786605ff 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -708,7 +708,8 @@ void nmethod::preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map // handle the case of an anchor explicitly set in continuation code that doesn't have a callee JavaThread* thread = reg_map->thread(); - if (thread->has_last_Java_frame() && fr.sp() == thread->last_Java_sp()) { + if ((thread->has_last_Java_frame() && fr.sp() == thread->last_Java_sp()) + JVMTI_ONLY(|| (method()->is_continuation_enter_intrinsic() && thread->on_monitor_waited_event()))) { return; } @@ -1298,7 +1299,7 @@ nmethod::nmethod( _comp_level = CompLevel_none; _compiler_type = type; _orig_pc_offset = 0; - _num_stack_arg_slots = _method->constMethod()->num_stack_arg_slots(); + _num_stack_arg_slots = 0; if (offsets->value(CodeOffsets::Exceptions) != -1) { // Continuation enter intrinsic diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 23040aa1dcd45..d4fddd222b2bb 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -1145,6 +1145,12 @@ JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide); JNIEXPORT void JNICALL JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter); +JNIEXPORT void JNICALL +JVM_VirtualThreadPinnedEvent(JNIEnv* env, jclass clazz, jstring op); + +JNIEXPORT jobject JNICALL +JVM_TakeVirtualThreadListToUnblock(JNIEnv* env, jclass ignored); + /* * Core reflection support. */ diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 525258b1ebd0c..1d8268dbda66d 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -735,7 +735,7 @@ JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* current, B assert(Universe::heap()->is_in_or_null(elem->obj()), "must be null or an object"); #ifdef ASSERT - current->last_frame().interpreter_frame_verify_monitor(elem); + if (!current->preempting()) current->last_frame().interpreter_frame_verify_monitor(elem); #endif JRT_END diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 87b124e9d7968..3406c85df7fe0 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -234,8 +234,10 @@ class MaskFillerForNative: public NativeSignatureIterator { private: uintptr_t * _mask; // the bit mask to be filled int _size; // the mask size in bits + int _num_oops; void set_one(int i) { + _num_oops++; i *= InterpreterOopMap::bits_per_entry; assert(0 <= i && i < _size, "offset out of bounds"); _mask[i / BitsPerWord] |= (((uintptr_t) 1 << InterpreterOopMap::oop_bit_number) << (i % BitsPerWord)); @@ -253,6 +255,7 @@ class MaskFillerForNative: public NativeSignatureIterator { MaskFillerForNative(const methodHandle& method, uintptr_t* mask, int size) : NativeSignatureIterator(method) { _mask = mask; _size = size; + _num_oops = 0; // initialize with 0 int i = (size + BitsPerWord - 1) / BitsPerWord; while (i-- > 0) _mask[i] = 0; @@ -261,6 +264,8 @@ class MaskFillerForNative: public NativeSignatureIterator { void generate() { iterate(); } + + int num_oops() { return _num_oops; } }; bool OopMapCacheEntry::verify_mask(CellTypeState* vars, CellTypeState* stack, int max_locals, int stack_top) { @@ -319,6 +324,7 @@ void OopMapCacheEntry::fill_for_native(const methodHandle& mh) { // fill mask for parameters MaskFillerForNative mf(mh, bit_mask(), mask_size()); mf.generate(); + _num_oops = mf.num_oops(); } diff --git a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp index 58559481e5ddc..dc3e4b67b33fd 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.cpp +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -212,6 +212,7 @@ address TemplateInterpreter::_throw_ClassCastException_entry = nu address TemplateInterpreter::_throw_NullPointerException_entry = nullptr; address TemplateInterpreter::_throw_StackOverflowError_entry = nullptr; address TemplateInterpreter::_throw_exception_entry = nullptr; +address TemplateInterpreter::_cont_resume_interpreter_adapter = nullptr; #ifndef PRODUCT EntryPoint TemplateInterpreter::_trace_code; diff --git a/src/hotspot/share/interpreter/templateInterpreter.hpp b/src/hotspot/share/interpreter/templateInterpreter.hpp index 9d3c5545fa063..224d9b973edf7 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.hpp +++ b/src/hotspot/share/interpreter/templateInterpreter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,6 +111,8 @@ class TemplateInterpreter: public AbstractInterpreter { static address _throw_StackOverflowError_entry; + static address _cont_resume_interpreter_adapter; + static address _remove_activation_entry; // continuation address if an exception is not handled by current frame static address _remove_activation_preserving_args_entry; // continuation address when current frame is being popped @@ -154,6 +156,8 @@ class TemplateInterpreter: public AbstractInterpreter { static address throw_NullPointerException_entry() { return _throw_NullPointerException_entry; } static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; } + static address cont_resume_interpreter_adapter() { return _cont_resume_interpreter_adapter; } + // Code generation #ifndef PRODUCT static address trace_code (TosState state) { return _trace_code.entry(state); } diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 3f497c3360b7e..91c302888431e 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -175,7 +175,9 @@ void TemplateInterpreterGenerator::generate_all() { Interpreter::_throw_StackOverflowError_entry = generate_StackOverflowError_handler(); } - + { CodeletMark cm(_masm, "preemption resume adapter"); + Interpreter::_cont_resume_interpreter_adapter = generate_cont_resume_interpreter_adapter(); + } #define method_entry(kind) \ { CodeletMark cm(_masm, "method entry point (kind = " #kind ")"); \ diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp index b0afcb5279522..d3ddf2a830ff0 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp @@ -56,6 +56,7 @@ class TemplateInterpreterGenerator: public AbstractInterpreterGenerator { address generate_earlyret_entry_for(TosState state); address generate_deopt_entry_for(TosState state, int step, address continuation = nullptr); address generate_safept_entry_for(TosState state, address runtime_entry); + address generate_cont_resume_interpreter_adapter(); void generate_throw_exception(); void lock_method(); diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index ee50b2eb3d850..fa572537c2f96 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -641,9 +641,6 @@ void BytecodeInterpreter::run(interpreterState istate) { success = false; } } - if (success) { - THREAD->inc_held_monitor_count(); - } } if (!success) { CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); @@ -745,9 +742,6 @@ void BytecodeInterpreter::run(interpreterState istate) { success = false; } } - if (success) { - THREAD->inc_held_monitor_count(); - } } if (!success) { CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); @@ -1680,9 +1674,6 @@ void BytecodeInterpreter::run(interpreterState istate) { success = false; } } - if (success) { - THREAD->inc_held_monitor_count(); - } } if (!success) { CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); @@ -1720,9 +1711,6 @@ void BytecodeInterpreter::run(interpreterState istate) { success = false; } } - if (success) { - THREAD->dec_held_monitor_count(); - } } if (!success) { InterpreterRuntime::monitorexit(most_recent); @@ -3166,9 +3154,6 @@ void BytecodeInterpreter::run(interpreterState istate) { success = false; } } - if (success) { - THREAD->dec_held_monitor_count(); - } } if (!success) { InterpreterRuntime::monitorexit(end); @@ -3245,9 +3230,6 @@ void BytecodeInterpreter::run(interpreterState istate) { } } } - if (dec_monitor_count) { - THREAD->dec_held_monitor_count(); - } } } } diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp b/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp index 04df0012d675a..699d50e778f94 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp @@ -71,6 +71,7 @@ class ZeroInterpreter: public AbstractInterpreter { static address throw_NullPointerException_entry() { return nullptr; } static address throw_ArithmeticException_entry() { return nullptr; } static address throw_StackOverflowError_entry() { return nullptr; } + static address cont_resume_interpreter_adapter() { return nullptr; } # include "zeroInterpreter_zero.hpp" }; diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index babb24f28ea1d..f8b792f30944c 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -156,6 +156,12 @@ + + + + + + diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 35e50bd6318f0..9fc6be3d4c844 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -104,7 +104,7 @@ void JfrThreadLocal::initialize_main_thread(JavaThread* jt) { assert(Thread::is_starting_thread(jt), "invariant"); assert(jt->threadObj() == nullptr, "invariant"); assert(jt->jfr_thread_local()->_jvm_thread_id == 0, "invariant"); - jt->jfr_thread_local()->_jvm_thread_id = 1; + jt->jfr_thread_local()->_jvm_thread_id = ThreadIdentifier::initial(); } static void send_java_thread_start_event(JavaThread* jt) { diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index a0ffe74873ba6..bde157a34e138 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -227,6 +227,7 @@ nonstatic_field(JavaThread, _vthread, OopHandle) \ nonstatic_field(JavaThread, _scopedValueCache, OopHandle) \ nonstatic_field(JavaThread, _anchor, JavaFrameAnchor) \ + nonstatic_field(JavaThread, _lock_id, int64_t) \ nonstatic_field(JavaThread, _vm_result, oop) \ nonstatic_field(JavaThread, _stack_overflow_state._stack_overflow_limit, address) \ volatile_nonstatic_field(JavaThread, _exception_oop, oop) \ @@ -327,11 +328,12 @@ \ nonstatic_field(ObjArrayKlass, _element_klass, Klass*) \ \ - unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \ + volatile_nonstatic_field(ObjectMonitor, _owner, int64_t) \ volatile_nonstatic_field(ObjectMonitor, _recursions, intptr_t) \ volatile_nonstatic_field(ObjectMonitor, _cxq, ObjectWaiter*) \ volatile_nonstatic_field(ObjectMonitor, _EntryList, ObjectWaiter*) \ - volatile_nonstatic_field(ObjectMonitor, _succ, JavaThread*) \ + volatile_nonstatic_field(ObjectMonitor, _succ, int64_t) \ + volatile_nonstatic_field(ObjectMonitor, _stack_locker, BasicLock*) \ \ volatile_nonstatic_field(oopDesc, _mark, markWord) \ volatile_nonstatic_field(oopDesc, _metadata._klass, Klass*) \ @@ -779,7 +781,9 @@ AARCH64_ONLY(declare_constant(NMethodPatchingType::conc_instruction_and_data_patch)) \ AARCH64_ONLY(declare_constant(NMethodPatchingType::conc_data_patch)) \ \ + declare_constant(ObjectMonitor::NO_OWNER) \ declare_constant(ObjectMonitor::ANONYMOUS_OWNER) \ + declare_constant(ObjectMonitor::DEFLATER_MARKER) \ \ declare_constant(ReceiverTypeData::receiver_type_row_cell_count) \ declare_constant(ReceiverTypeData::receiver0_offset) \ @@ -809,6 +813,7 @@ declare_constant(markWord::hash_mask_in_place) \ \ declare_constant(markWord::unlocked_value) \ + declare_constant(markWord::marked_value) \ \ declare_constant(markWord::no_hash_in_place) \ declare_constant(markWord::no_lock_in_place) \ diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 990637c295143..a9024bf4a9f18 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1600,6 +1600,7 @@ void InstanceKlass::call_class_initializer(TRAPS) { THREAD->name()); } if (h_method() != nullptr) { + ThreadInClassInitializer ticl(THREAD, this); // Track class being initialized JavaCallArguments args; // No arguments JavaValue result(T_VOID); JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args) diff --git a/src/hotspot/share/oops/instanceStackChunkKlass.cpp b/src/hotspot/share/oops/instanceStackChunkKlass.cpp index a2227b0a76a5e..2b819b27f642e 100644 --- a/src/hotspot/share/oops/instanceStackChunkKlass.cpp +++ b/src/hotspot/share/oops/instanceStackChunkKlass.cpp @@ -163,6 +163,23 @@ void InstanceStackChunkKlass::oop_oop_iterate_stack_slow(stackChunkOop chunk, Oo chunk->iterate_stack(&frame_closure); } +template +void InstanceStackChunkKlass::oop_oop_iterate_lockstack(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr) { + if (LockingMode != LM_LIGHTWEIGHT) { + return; + } + + StackChunkOopIterateFilterClosure cl(closure, mr); + if (chunk->has_bitmap()) { + chunk->iterate_lockstack(&cl); + } else { + chunk->iterate_lockstack(&cl); + } +} + +template void InstanceStackChunkKlass::oop_oop_iterate_lockstack(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr); +template void InstanceStackChunkKlass::oop_oop_iterate_lockstack(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr); + #ifdef ASSERT class DescribeStackChunkClosure { @@ -224,7 +241,7 @@ class PrintStackChunkClosure { frame f = fs.to_frame(); _st->print_cr("-- frame sp: " PTR_FORMAT " interpreted: %d size: %d argsize: %d", p2i(fs.sp()), fs.is_interpreted(), f.frame_size(), - fs.is_interpreted() ? 0 : f.compiled_frame_stack_argsize()); + fs.is_interpreted() || fs.is_stub() ? 0 : f.compiled_frame_stack_argsize()); #ifdef ASSERT f.print_value_on(_st); #else diff --git a/src/hotspot/share/oops/instanceStackChunkKlass.hpp b/src/hotspot/share/oops/instanceStackChunkKlass.hpp index aa59b46f42c9d..19ce37c4ddddf 100644 --- a/src/hotspot/share/oops/instanceStackChunkKlass.hpp +++ b/src/hotspot/share/oops/instanceStackChunkKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,6 +177,9 @@ class InstanceStackChunkKlass: public InstanceKlass { template inline void oop_oop_iterate_stack_with_bitmap(stackChunkOop chunk, OopClosureType* closure, intptr_t* start, intptr_t* end); + template + void oop_oop_iterate_lockstack(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr); + void do_methods(stackChunkOop chunk, OopIterateClosure* cl); void oop_oop_iterate_stack_slow(stackChunkOop chunk, OopIterateClosure* closure, MemRegion mr); diff --git a/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp b/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp index 38aab2c78a78d..3374895c6a147 100644 --- a/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,6 +65,7 @@ void InstanceStackChunkKlass::oop_oop_iterate(oop obj, OopClosureType* closure) } oop_oop_iterate_stack(chunk, closure); oop_oop_iterate_header(chunk, closure); + oop_oop_iterate_lockstack(chunk, closure, chunk->range()); } template @@ -73,6 +74,7 @@ void InstanceStackChunkKlass::oop_oop_iterate_reverse(oop obj, OopClosureType* c stackChunkOop chunk = stackChunkOopDesc::cast(obj); oop_oop_iterate_stack(chunk, closure); oop_oop_iterate_header(chunk, closure); + oop_oop_iterate_lockstack(chunk, closure, chunk->range()); } template @@ -85,6 +87,7 @@ void InstanceStackChunkKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* c } oop_oop_iterate_stack_bounded(chunk, closure, mr); oop_oop_iterate_header_bounded(chunk, closure, mr); + oop_oop_iterate_lockstack(chunk, closure, mr); } template diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index a1b380d364655..26a047449ed6e 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -867,6 +867,11 @@ bool Method::needs_clinit_barrier() const { return is_static() && !method_holder()->is_initialized(); } +bool Method::is_object_wait0() const { + return klass_name() == vmSymbols::java_lang_Object() + && name() == vmSymbols::wait_name(); +} + objArrayHandle Method::resolved_checked_exceptions_impl(Method* method, TRAPS) { int length = method->checked_exceptions_length(); if (length == 0) { // common case diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index d42089d3a5cc9..232e5fb577eaa 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -586,6 +586,9 @@ class Method : public Metadata { // returns true if the method name is bool is_object_initializer() const; + // returns true if the method name is wait0 + bool is_object_wait0() const; + // compiled code support // NOTE: code() is inherently racy as deopt can be clearing code // simultaneously. Use with caution. diff --git a/src/hotspot/share/oops/oopHandle.hpp b/src/hotspot/share/oops/oopHandle.hpp index 983ddf9ae5ca3..4f0532534a8b0 100644 --- a/src/hotspot/share/oops/oopHandle.hpp +++ b/src/hotspot/share/oops/oopHandle.hpp @@ -71,6 +71,7 @@ class OopHandle { inline void replace(oop obj); inline oop xchg(oop new_value); + inline oop cmpxchg(oop old_value, oop new_value); oop* ptr_raw() const { return _obj; } }; diff --git a/src/hotspot/share/oops/oopHandle.inline.hpp b/src/hotspot/share/oops/oopHandle.inline.hpp index 23a13cb35e34e..06acd2912745c 100644 --- a/src/hotspot/share/oops/oopHandle.inline.hpp +++ b/src/hotspot/share/oops/oopHandle.inline.hpp @@ -65,4 +65,8 @@ inline oop OopHandle::xchg(oop new_value) { return NativeAccess::oop_atomic_xchg(_obj, new_value); } +inline oop OopHandle::cmpxchg(oop old_value, oop new_value) { + return NativeAccess::oop_atomic_cmpxchg(_obj, old_value, new_value); +} + #endif // SHARE_OOPS_OOPHANDLE_INLINE_HPP diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp index 344fef1530837..7319e93b6679c 100644 --- a/src/hotspot/share/oops/stackChunkOop.cpp +++ b/src/hotspot/share/oops/stackChunkOop.cpp @@ -65,6 +65,21 @@ class FrameOopIterator : public OopIterator { } }; +class LockStackOopIterator : public OopIterator { +private: + const stackChunkOop _chunk; +public: + LockStackOopIterator(const stackChunkOop chunk) : _chunk(chunk) {} + + virtual void oops_do(OopClosure* cl) override { + int cnt = _chunk->lockstack_size(); + oop* lockstack_start = (oop*)_chunk->start_address(); + for (int i = 0; i < cnt; i++) { + cl->do_oop(&lockstack_start[i]); + } + } +}; + frame stackChunkOopDesc::top_frame(RegisterMap* map) { assert(!is_empty(), ""); StackChunkFrameStream fs(this); @@ -224,6 +239,14 @@ class EncodeGCModeConcurrentFrameClosure { return true; } + + bool do_lockstack() { + BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); + LockStackOopIterator iterator(_chunk); + bs_chunk->encode_gc_mode(_chunk, &iterator); + + return true; + } }; bool stackChunkOopDesc::try_acquire_relativization() { @@ -298,6 +321,7 @@ void stackChunkOopDesc::relativize_derived_pointers_concurrently() { DerivedPointersSupport::RelativizeClosure derived_cl; EncodeGCModeConcurrentFrameClosure frame_cl(this, &derived_cl); iterate_stack(&frame_cl); + frame_cl.do_lockstack(); release_relativization(); } @@ -320,6 +344,14 @@ class TransformStackChunkClosure { return true; } + + bool do_lockstack() { + BarrierSetStackChunk* bs_chunk = BarrierSet::barrier_set()->barrier_set_stack_chunk(); + LockStackOopIterator iterator(_chunk); + bs_chunk->encode_gc_mode(_chunk, &iterator); + + return true; + } }; void stackChunkOopDesc::transform() { @@ -332,6 +364,7 @@ void stackChunkOopDesc::transform() { TransformStackChunkClosure closure(this); iterate_stack(&closure); + closure.do_lockstack(); } template @@ -408,6 +441,35 @@ void stackChunkOopDesc::fix_thawed_frame(const frame& f, const RegisterMapT* map template void stackChunkOopDesc::fix_thawed_frame(const frame& f, const RegisterMap* map); template void stackChunkOopDesc::fix_thawed_frame(const frame& f, const SmallRegisterMap* map); +void stackChunkOopDesc::transfer_lockstack(oop* dst, bool requires_barriers) { + const bool requires_gc_barriers = is_gc_mode() || requires_barriers; + const bool requires_uncompress = has_bitmap() && UseCompressedOops; + const auto load_and_clear_obj = [&](intptr_t* at) -> oop { + if (requires_gc_barriers) { + if (requires_uncompress) { + oop value = HeapAccess<>::oop_load(reinterpret_cast(at)); + HeapAccess<>::oop_store(reinterpret_cast(at), nullptr); + return value; + } else { + oop value = HeapAccess<>::oop_load(reinterpret_cast(at)); + HeapAccess<>::oop_store(reinterpret_cast(at), nullptr); + return value; + } + } else { + oop value = *reinterpret_cast(at); + return value; + } + }; + + const int cnt = lockstack_size(); + intptr_t* lockstack_start = start_address(); + for (int i = 0; i < cnt; i++) { + oop mon_owner = load_and_clear_obj(&lockstack_start[i]); + assert(oopDesc::is_oop(mon_owner), "not an oop"); + dst[i] = mon_owner; + } +} + void stackChunkOopDesc::print_on(bool verbose, outputStream* st) const { if (*((juint*)this) == badHeapWordVal) { st->print_cr("BAD WORD"); @@ -456,9 +518,9 @@ class VerifyStackChunkFrameClosure { int _num_interpreted_frames; int _num_i2c; - VerifyStackChunkFrameClosure(stackChunkOop chunk, int num_frames, int size) + VerifyStackChunkFrameClosure(stackChunkOop chunk) : _chunk(chunk), _sp(nullptr), _cb(nullptr), _callee_interpreted(false), - _size(size), _argsize(0), _num_oops(0), _num_frames(num_frames), _num_interpreted_frames(0), _num_i2c(0) {} + _size(0), _argsize(0), _num_oops(0), _num_frames(0), _num_interpreted_frames(0), _num_i2c(0) {} template bool do_frame(const StackChunkFrameStream& f, const RegisterMapT* map) { @@ -552,11 +614,8 @@ bool stackChunkOopDesc::verify(size_t* out_size, int* out_oops, int* out_frames, assert((size == 0) == is_empty(), ""); const StackChunkFrameStream first(this); - const bool has_safepoint_stub_frame = first.is_stub(); - VerifyStackChunkFrameClosure closure(this, - has_safepoint_stub_frame ? 1 : 0, // Iterate_stack skips the safepoint stub - has_safepoint_stub_frame ? first.frame_size() : 0); + VerifyStackChunkFrameClosure closure(this); iterate_stack(&closure); assert(!is_empty() || closure._cb == nullptr, ""); diff --git a/src/hotspot/share/oops/stackChunkOop.hpp b/src/hotspot/share/oops/stackChunkOop.hpp index 250acc8d16591..38f26d091f08e 100644 --- a/src/hotspot/share/oops/stackChunkOop.hpp +++ b/src/hotspot/share/oops/stackChunkOop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,8 @@ class frame; class MemRegion; class RegisterMap; class VMRegImpl; +class ObjectMonitor; +class ObjectWaiter; typedef VMRegImpl* VMReg; // A continuation stack-chunk oop. @@ -58,6 +60,8 @@ class stackChunkOopDesc : public instanceOopDesc { static const uint8_t FLAG_NOTIFY_RELATIVIZE = 1 << 2; // Someone is waiting for relativization to complete static const uint8_t FLAG_GC_MODE = 1 << 3; // Once true it and FLAG_HAS_INTERPRETED_FRAMES can't change static const uint8_t FLAG_HAS_BITMAP = 1 << 4; // Can only be true if FLAG_GC_MODE is true + static const uint8_t FLAG_HAS_LOCKSTACK = 1 << 5; // LockStack was copied into stackChunk + static const uint8_t FLAG_PREEMPTED = 1 << 6; // Continuation was unmounted from inside VM bool try_acquire_relativization(); void release_relativization(); @@ -91,6 +95,15 @@ class stackChunkOopDesc : public instanceOopDesc { inline int max_thawing_size() const; inline void set_max_thawing_size(int value); + inline uint8_t lockstack_size() const; + inline void set_lockstack_size(uint8_t value); + + inline ObjectWaiter* object_waiter() const; + inline void set_object_waiter(ObjectWaiter* obj_waiter); + + inline ObjectMonitor* current_pending_monitor() const; + inline ObjectMonitor* current_waiting_monitor() const; + inline oop cont() const; template inline oop cont() const; @@ -127,6 +140,12 @@ class stackChunkOopDesc : public instanceOopDesc { inline bool has_mixed_frames() const; inline void set_has_mixed_frames(bool value); + inline bool preempted() const; + inline void set_preempted(bool value); + + inline bool has_lockstack() const; + inline void set_has_lockstack(bool value); + inline bool is_gc_mode() const; inline bool is_gc_mode_acquire() const; inline void set_gc_mode(bool value); @@ -147,6 +166,11 @@ class stackChunkOopDesc : public instanceOopDesc { template void fix_thawed_frame(const frame& f, const RegisterMapT* map); + void transfer_lockstack(oop* start, bool requires_barriers); + + template + inline void iterate_lockstack(StackChunkLockStackClosureType* closure); + template inline void iterate_stack(StackChunkFrameClosureType* closure); diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index 7b955d551d7ae..d647904bf5b63 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,9 +36,10 @@ #include "oops/access.inline.hpp" #include "oops/instanceStackChunkKlass.inline.hpp" #include "runtime/continuationJavaClasses.inline.hpp" -#include "runtime/frame.inline.hpp" +#include "runtime/frame.hpp" #include "runtime/globals.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/objectMonitor.hpp" #include "runtime/registerMap.hpp" #include "runtime/smallRegisterMap.inline.hpp" #include "utilities/macros.hpp" @@ -88,6 +89,12 @@ inline void stackChunkOopDesc::set_max_thawing_size(int value) { jdk_internal_vm_StackChunk::set_maxThawingSize(this, (jint)value); } +inline uint8_t stackChunkOopDesc::lockstack_size() const { return jdk_internal_vm_StackChunk::lockStackSize(as_oop()); } +inline void stackChunkOopDesc::set_lockstack_size(uint8_t value) { jdk_internal_vm_StackChunk::set_lockStackSize(this, value); } + +inline ObjectWaiter* stackChunkOopDesc::object_waiter() const { return (ObjectWaiter*)jdk_internal_vm_StackChunk::objectWaiter(as_oop()); } +inline void stackChunkOopDesc::set_object_waiter(ObjectWaiter* obj) { jdk_internal_vm_StackChunk::set_objectWaiter(this, (address)obj); } + inline oop stackChunkOopDesc::cont() const { return jdk_internal_vm_StackChunk::cont(as_oop()); } inline void stackChunkOopDesc::set_cont(oop value) { jdk_internal_vm_StackChunk::set_cont(this, value); } template @@ -154,10 +161,35 @@ inline void stackChunkOopDesc::clear_flags() { inline bool stackChunkOopDesc::has_mixed_frames() const { return is_flag(FLAG_HAS_INTERPRETED_FRAMES); } inline void stackChunkOopDesc::set_has_mixed_frames(bool value) { - assert((flags() & ~FLAG_HAS_INTERPRETED_FRAMES) == 0, "other flags should not be set"); + assert((flags() & ~(FLAG_HAS_INTERPRETED_FRAMES | FLAG_PREEMPTED)) == 0, "other flags should not be set"); set_flag(FLAG_HAS_INTERPRETED_FRAMES, value); } +inline bool stackChunkOopDesc::preempted() const { return is_flag(FLAG_PREEMPTED); } +inline void stackChunkOopDesc::set_preempted(bool value) { + assert(preempted() != value, ""); + set_flag(FLAG_PREEMPTED, value); +} + +inline ObjectMonitor* stackChunkOopDesc::current_pending_monitor() const { + ObjectWaiter* waiter = object_waiter(); + if (waiter != nullptr && waiter->at_monitorenter()) { + return waiter->monitor(); + } + return nullptr; +} + +inline ObjectMonitor* stackChunkOopDesc::current_waiting_monitor() const { + ObjectWaiter* waiter = object_waiter(); + if (waiter != nullptr && waiter->is_wait()) { + return waiter->monitor(); + } + return nullptr; +} + +inline bool stackChunkOopDesc::has_lockstack() const { return is_flag(FLAG_HAS_LOCKSTACK); } +inline void stackChunkOopDesc::set_has_lockstack(bool value) { set_flag(FLAG_HAS_LOCKSTACK, value); } + inline bool stackChunkOopDesc::is_gc_mode() const { return is_flag(FLAG_GC_MODE); } inline bool stackChunkOopDesc::is_gc_mode_acquire() const { return is_flag_acquire(FLAG_GC_MODE); } inline void stackChunkOopDesc::set_gc_mode(bool value) { set_flag(FLAG_GC_MODE, value); } @@ -180,6 +212,16 @@ void stackChunkOopDesc::do_barriers(const StackChunkFrameStream& f, do_barriers0(f, map); } +template +inline void stackChunkOopDesc::iterate_lockstack(StackChunkLockStackClosureType* closure) { + assert(LockingMode == LM_LIGHTWEIGHT, ""); + int cnt = lockstack_size(); + intptr_t* lockstart_addr = start_address(); + for (int i = 0; i < cnt; i++) { + closure->do_oop((OopT*)&lockstart_addr[i]); + } +} + template inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure) { has_mixed_frames() ? iterate_stack(closure) @@ -200,15 +242,14 @@ inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure RegisterMap::ProcessFrames::skip, RegisterMap::WalkContinuation::include); full_map.set_include_argument_oops(false); + closure->do_frame(f, map); f.next(&full_map); - assert(!f.is_done(), ""); assert(f.is_compiled(), ""); should_continue = closure->do_frame(f, &full_map); f.next(map); - f.handle_deopted(); // the stub caller might be deoptimized (as it's not at a call) } assert(!f.is_stub(), ""); @@ -269,7 +310,7 @@ inline MemRegion stackChunkOopDesc::range() { } inline int stackChunkOopDesc::relativize_usp_offset(const frame& fr, const int usp_offset_in_bytes) const { - assert(fr.is_compiled_frame() || fr.cb()->is_safepoint_stub(), ""); + assert(fr.is_compiled_frame() || fr.cb()->is_runtime_stub(), ""); assert(is_in_chunk(fr.unextended_sp()), ""); intptr_t* base = fr.real_fp(); // equal to the caller's sp diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index babc487acfdaf..73f3caec003b6 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -3678,6 +3678,12 @@ bool LibraryCallKit::inline_native_setCurrentThread() { thread_obj_handle = _gvn.transform(thread_obj_handle); const TypePtr *adr_type = _gvn.type(thread_obj_handle)->isa_ptr(); access_store_at(nullptr, thread_obj_handle, adr_type, arr, _gvn.type(arr), T_OBJECT, IN_NATIVE | MO_UNORDERED); + + // Change the lock_id of the JavaThread + Node* tid = load_field_from_object(arr, "tid", "J"); + Node* thread_id_offset = basic_plus_adr(thread, in_bytes(JavaThread::lock_id_offset())); + Node* tid_memory = store_to_memory(control(), thread_id_offset, tid, T_LONG, Compile::AliasIdxRaw, MemNode::unordered, true); + JFR_ONLY(extend_setCurrentThread(thread, arr);) return true; } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index f0f14a0503162..8bc03ce2efb42 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3089,6 +3089,10 @@ JVM_ENTRY(void, JVM_SetCurrentThread(JNIEnv* env, jobject thisThread, jobject theThread)) oop threadObj = JNIHandles::resolve(theThread); thread->set_vthread(threadObj); + + // Set lock id of new current Thread + thread->set_lock_id(java_lang_Thread::thread_id(threadObj)); + JFR_ONLY(Jfr::on_set_current_thread(thread, threadObj);) JVM_END @@ -3955,6 +3959,39 @@ JVM_ENTRY(void, JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jbool #endif JVM_END +JVM_ENTRY(void, JVM_VirtualThreadPinnedEvent(JNIEnv* env, jclass ignored, jstring op)) +#if INCLUDE_JFR + freeze_result result = THREAD->last_freeze_fail_result(); + assert(result != freeze_ok, "sanity check"); + EventVirtualThreadPinned event(UNTIMED); + event.set_starttime(THREAD->last_freeze_fail_time()); + if (event.should_commit()) { + ResourceMark rm(THREAD); + const char *str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(op)); + THREAD->post_vthread_pinned_event(&event, str, result); + } +#endif +JVM_END + +JVM_ENTRY(jobject, JVM_TakeVirtualThreadListToUnblock(JNIEnv* env, jclass ignored)) + ParkEvent* parkEvent = ObjectMonitor::vthread_unparker_ParkEvent(); + assert(parkEvent != nullptr, "not initialized"); + + OopHandle& list_head = ObjectMonitor::vthread_cxq_head(); + oop vthread_head = nullptr; + while (true) { + if (list_head.peek() != nullptr) { + for (;;) { + oop head = list_head.resolve(); + if (list_head.cmpxchg(head, nullptr) == head) { + return JNIHandles::make_local(THREAD, head); + } + } + } + ThreadBlockInVM tbivm(THREAD); + parkEvent->park(); + } +JVM_END /* * Return the current class's class file version. The low order 16 bits of the * returned jint contain the class's major version. The high order 16 bits diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 097ce33154051..ff3dad679606c 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -1350,10 +1350,6 @@ JvmtiEnv::GetOwnedMonitorInfo(jthread thread, jint* owned_monitor_count_ptr, job JavaThread* calling_thread = JavaThread::current(); HandleMark hm(calling_thread); - // growable array of jvmti monitors info on the C-heap - GrowableArray *owned_monitors_list = - new (mtServiceability) GrowableArray(1, mtServiceability); - JvmtiVTMSTransitionDisabler disabler(thread); ThreadsListHandle tlh(calling_thread); @@ -1361,23 +1357,29 @@ JvmtiEnv::GetOwnedMonitorInfo(jthread thread, jint* owned_monitor_count_ptr, job oop thread_oop = nullptr; jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, calling_thread, &java_thread, &thread_oop); if (err != JVMTI_ERROR_NONE) { - delete owned_monitors_list; return err; } - if (java_thread != nullptr) { - Handle thread_handle(calling_thread, thread_oop); - EscapeBarrier eb(true, calling_thread, java_thread); - if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) { - delete owned_monitors_list; - return JVMTI_ERROR_OUT_OF_MEMORY; - } - // get owned monitors info with handshake - GetOwnedMonitorInfoClosure op(this, calling_thread, owned_monitors_list); - JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle); - err = op.result(); + if (LockingMode == LM_LEGACY && java_thread == nullptr) { + *owned_monitor_count_ptr = 0; + return JVMTI_ERROR_NONE; } + // growable array of jvmti monitors info on the C-heap + GrowableArray *owned_monitors_list = + new (mtServiceability) GrowableArray(1, mtServiceability); + + Handle thread_handle(calling_thread, thread_oop); + EscapeBarrier eb(java_thread != nullptr, calling_thread, java_thread); + if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) { + delete owned_monitors_list; + return JVMTI_ERROR_OUT_OF_MEMORY; + } + // get owned monitors info with handshake + GetOwnedMonitorInfoClosure op(this, calling_thread, owned_monitors_list); + JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle); + err = op.result(); + jint owned_monitor_count = owned_monitors_list->length(); if (err == JVMTI_ERROR_NONE) { if ((err = allocate(owned_monitor_count * sizeof(jobject *), @@ -1408,10 +1410,6 @@ JvmtiEnv::GetOwnedMonitorStackDepthInfo(jthread thread, jint* monitor_info_count JavaThread* calling_thread = JavaThread::current(); HandleMark hm(calling_thread); - // growable array of jvmti monitors info on the C-heap - GrowableArray *owned_monitors_list = - new (mtServiceability) GrowableArray(1, mtServiceability); - JvmtiVTMSTransitionDisabler disabler(thread); ThreadsListHandle tlh(calling_thread); @@ -1419,23 +1417,29 @@ JvmtiEnv::GetOwnedMonitorStackDepthInfo(jthread thread, jint* monitor_info_count oop thread_oop = nullptr; jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, calling_thread, &java_thread, &thread_oop); if (err != JVMTI_ERROR_NONE) { - delete owned_monitors_list; return err; } - if (java_thread != nullptr) { - Handle thread_handle(calling_thread, thread_oop); - EscapeBarrier eb(true, calling_thread, java_thread); - if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) { - delete owned_monitors_list; - return JVMTI_ERROR_OUT_OF_MEMORY; - } - // get owned monitors info with handshake - GetOwnedMonitorInfoClosure op(this, calling_thread, owned_monitors_list); - JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle); - err = op.result(); + if (LockingMode == LM_LEGACY && java_thread == nullptr) { + *monitor_info_count_ptr = 0; + return JVMTI_ERROR_NONE; } + // growable array of jvmti monitors info on the C-heap + GrowableArray *owned_monitors_list = + new (mtServiceability) GrowableArray(1, mtServiceability); + + Handle thread_handle(calling_thread, thread_oop); + EscapeBarrier eb(java_thread != nullptr, calling_thread, java_thread); + if (!eb.deoptimize_objects(MaxJavaStackTraceDepth)) { + delete owned_monitors_list; + return JVMTI_ERROR_OUT_OF_MEMORY; + } + // get owned monitors info with handshake + GetOwnedMonitorInfoClosure op(this, calling_thread, owned_monitors_list); + JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle); + err = op.result(); + jint owned_monitor_count = owned_monitors_list->length(); if (err == JVMTI_ERROR_NONE) { if ((err = allocate(owned_monitor_count * sizeof(jvmtiMonitorStackDepthInfo), diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index c28afbb1c51d0..422bbced802c5 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -652,13 +652,19 @@ JavaThread* JvmtiEnvBase::get_JavaThread_or_null(oop vthread) { } // An unmounted vthread may have an empty stack. -// Otherwise, it always has the yield0() and yield() frames we need to hide. -// The methods yield0() and yield() are annotated with the @JvmtiHideEvents. +// If unmounted from Java, it always has the yield0() and yield() frames we +// need to hide. The methods yield0() and yield() are annotated with the @JvmtiHideEvents. javaVFrame* -JvmtiEnvBase::skip_yield_frames_for_unmounted_vthread(javaVFrame* jvf) { +JvmtiEnvBase::skip_yield_frames_for_unmounted_vthread(oop vthread, javaVFrame* jvf) { if (jvf == nullptr) { return jvf; // empty stack is possible } + if (java_lang_VirtualThread::is_preempted(vthread)) { + // Top method should not be from Continuation class. + assert(jvf->method()->method_holder() != vmClasses::Continuation_klass(), ""); + return jvf; + } + assert(jvf->method()->jvmti_hide_events(), "sanity check"); assert(jvf->method()->method_holder() == vmClasses::Continuation_klass(), "expected Continuation class"); jvf = jvf->java_sender(); // skip yield0 frame @@ -721,7 +727,7 @@ JvmtiEnvBase::get_vthread_jvf(oop vthread) { } else { vframeStream vfs(cont); jvf = vfs.at_end() ? nullptr : vfs.asJavaVFrame(); - jvf = skip_yield_frames_for_unmounted_vthread(jvf); + jvf = skip_yield_frames_for_unmounted_vthread(vthread, jvf); } return jvf; } @@ -1023,18 +1029,18 @@ JvmtiEnvBase::get_owned_monitors(JavaThread *calling_thread, JavaThread* java_th } jvmtiError -JvmtiEnvBase::get_owned_monitors(JavaThread* calling_thread, JavaThread* java_thread, javaVFrame* jvf, - GrowableArray *owned_monitors_list) { +JvmtiEnvBase::get_owned_monitors(JavaThread* calling_thread, JavaThread* carrier, javaVFrame* jvf, + GrowableArray *owned_monitors_list, oop vthread) { jvmtiError err = JVMTI_ERROR_NONE; Thread *current_thread = Thread::current(); - assert(java_thread->is_handshake_safe_for(current_thread), + assert(carrier == nullptr || carrier->is_handshake_safe_for(current_thread), "call by myself or at handshake"); int depth = 0; for ( ; jvf != nullptr; jvf = jvf->java_sender()) { if (MaxJavaStackTraceDepth == 0 || depth++ < MaxJavaStackTraceDepth) { // check for stack too deep // Add locked objects for this frame into list. - err = get_locked_objects_in_frame(calling_thread, java_thread, jvf, owned_monitors_list, depth - 1); + err = get_locked_objects_in_frame(calling_thread, carrier, jvf, owned_monitors_list, depth - 1, vthread); if (err != JVMTI_ERROR_NONE) { return err; } @@ -1043,7 +1049,7 @@ JvmtiEnvBase::get_owned_monitors(JavaThread* calling_thread, JavaThread* java_th // Get off stack monitors. (e.g. acquired via jni MonitorEnter). JvmtiMonitorClosure jmc(calling_thread, owned_monitors_list, this); - ObjectSynchronizer::owned_monitors_iterate(&jmc, java_thread); + ObjectSynchronizer::owned_monitors_iterate(&jmc, carrier != nullptr ? carrier->threadObj() : vthread); err = jmc.error(); return err; @@ -1051,8 +1057,9 @@ JvmtiEnvBase::get_owned_monitors(JavaThread* calling_thread, JavaThread* java_th // Save JNI local handles for any objects that this frame owns. jvmtiError -JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread* java_thread, - javaVFrame *jvf, GrowableArray* owned_monitors_list, jint stack_depth) { +JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread* target, + javaVFrame *jvf, GrowableArray* owned_monitors_list, + jint stack_depth, oop vthread) { jvmtiError err = JVMTI_ERROR_NONE; Thread* current_thread = Thread::current(); ResourceMark rm(current_thread); @@ -1069,9 +1076,17 @@ JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread // at a safepoint or the calling thread is operating on itself so // it cannot leave the underlying wait() call. // Save object of current wait() call (if any) for later comparison. - ObjectMonitor *mon = java_thread->current_waiting_monitor(); - if (mon != nullptr) { - wait_obj = mon->object(); + if (target != nullptr) { + ObjectMonitor *mon = target->current_waiting_monitor(); + if (mon != nullptr) wait_obj = mon->object(); + } else { + assert(vthread != nullptr, "no vthread oop"); + oop cont = java_lang_VirtualThread::continuation(vthread); + assert(cont != nullptr, "vthread with no continuation"); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(cont); + assert(chunk != nullptr, "unmounted vthread should have a chunk"); + ObjectMonitor *mon = chunk->current_waiting_monitor(); + if (mon != nullptr) wait_obj = mon->object(); } } oop pending_obj = nullptr; @@ -1080,9 +1095,17 @@ JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread // at a safepoint or the calling thread is operating on itself so // it cannot leave the underlying enter() call. // Save object of current enter() call (if any) for later comparison. - ObjectMonitor *mon = java_thread->current_pending_monitor(); - if (mon != nullptr) { - pending_obj = mon->object(); + if (target != nullptr) { + ObjectMonitor *mon = target->current_pending_monitor(); + if (mon != nullptr) pending_obj = mon->object(); + } else { + assert(vthread != nullptr, "no vthread oop"); + oop cont = java_lang_VirtualThread::continuation(vthread); + assert(cont != nullptr, "vthread with no continuation"); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(cont); + assert(chunk != nullptr, "unmounted vthread should have a chunk"); + ObjectMonitor *mon = chunk->current_pending_monitor(); + if (mon != nullptr) pending_obj = mon->object(); } } @@ -1533,9 +1556,13 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec waiter != nullptr && (nWait == 0 || waiter != mon->first_waiter()); waiter = mon->next_waiter(waiter)) { JavaThread *w = mon->thread_of_waiter(waiter); - oop thread_oop = get_vthread_or_thread_oop(w); - if (thread_oop->is_a(vmClasses::BaseVirtualThread_klass())) { + if (w == nullptr) { skipped++; + } else { + oop thread_oop = get_vthread_or_thread_oop(w); + if (thread_oop->is_a(vmClasses::BaseVirtualThread_klass())) { + skipped++; + } } nWait++; } @@ -1583,15 +1610,19 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec jint skipped = 0; for (int i = 0; i < nWait; i++) { JavaThread *w = mon->thread_of_waiter(waiter); - oop thread_oop = get_vthread_or_thread_oop(w); - bool is_virtual = thread_oop->is_a(vmClasses::BaseVirtualThread_klass()); - assert(w != nullptr, "sanity check"); + bool is_virtual; + if (w == nullptr) { + is_virtual = true; + } else { + oop thread_oop = get_vthread_or_thread_oop(w); + is_virtual = thread_oop->is_a(vmClasses::BaseVirtualThread_klass()); + } if (is_virtual) { skipped++; } else { // If the thread was found on the ObjectWaiter list, then // it has not been notified. - Handle th(current_thread, get_vthread_or_thread_oop(w)); + Handle th(current_thread, w->threadObj()); ret.notify_waiters[i - skipped] = (jthread)jni_reference(calling_thread, th); } waiter = mon->next_waiter(waiter); @@ -2509,18 +2540,18 @@ GetOwnedMonitorInfoClosure::do_thread(Thread *target) { void GetOwnedMonitorInfoClosure::do_vthread(Handle target_h) { - assert(_target_jt != nullptr, "sanity check"); Thread* current = Thread::current(); ResourceMark rm(current); // vframes are resource allocated HandleMark hm(current); javaVFrame *jvf = JvmtiEnvBase::get_vthread_jvf(target_h()); - if (!_target_jt->is_exiting() && _target_jt->threadObj() != nullptr) { + if (_target_jt == nullptr || (!_target_jt->is_exiting() && _target_jt->threadObj() != nullptr)) { _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _target_jt, jvf, - _owned_monitors_list); + _owned_monitors_list, + target_h()); } } @@ -2538,6 +2569,13 @@ GetCurrentContendedMonitorClosure::do_thread(Thread *target) { void GetCurrentContendedMonitorClosure::do_vthread(Handle target_h) { if (_target_jt == nullptr) { + oop cont = java_lang_VirtualThread::continuation(target_h()); + assert(cont != nullptr, "vthread with no continuation"); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(cont); + assert(chunk != nullptr, "unmounted vthread should have a chunk"); + if (chunk->current_pending_monitor() != nullptr) { + *_owned_monitor_ptr = JNIHandles::make_local(_calling_thread, chunk->current_pending_monitor()->object()); + } _result = JVMTI_ERROR_NONE; // target virtual thread is unmounted return; } diff --git a/src/hotspot/share/prims/jvmtiEnvBase.hpp b/src/hotspot/share/prims/jvmtiEnvBase.hpp index e8769d423c531..217731cd8f00a 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.hpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.hpp @@ -358,7 +358,7 @@ class JvmtiEnvBase : public CHeapObj { JavaThread* java_thread, javaVFrame *jvf, GrowableArray* owned_monitors_list, - jint depth); + jint depth, oop vthread = nullptr); public: static javaVFrame* jvf_for_thread_and_depth(JavaThread* java_thread, jint depth); @@ -366,7 +366,7 @@ class JvmtiEnvBase : public CHeapObj { static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd); // check and skip frames hidden in mount/unmount transitions - static javaVFrame* skip_yield_frames_for_unmounted_vthread(javaVFrame* jvf); + static javaVFrame* skip_yield_frames_for_unmounted_vthread(oop vthread, javaVFrame* jvf); static javaVFrame* check_and_skip_hidden_frames(bool is_in_VTMS_transition, javaVFrame* jvf); static javaVFrame* check_and_skip_hidden_frames(JavaThread* jt, javaVFrame* jvf); @@ -422,8 +422,8 @@ class JvmtiEnvBase : public CHeapObj { jobject* monitor_ptr, bool is_virtual); jvmtiError get_owned_monitors(JavaThread* calling_thread, JavaThread* java_thread, GrowableArray *owned_monitors_list); - jvmtiError get_owned_monitors(JavaThread* calling_thread, JavaThread* java_thread, javaVFrame* jvf, - GrowableArray *owned_monitors_list); + jvmtiError get_owned_monitors(JavaThread* calling_thread, JavaThread* carrier, javaVFrame* jvf, + GrowableArray *owned_monitors_list, oop vthread); static jvmtiError check_top_frame(Thread* current_thread, JavaThread* java_thread, jvalue value, TosState tos, Handle* ret_ob_h); jvmtiError force_early_return(jthread thread, jvalue value, TosState tos); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index b2e9fb9a4ab92..087f426c537e0 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -2870,6 +2870,19 @@ void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mnt } } +void JvmtiExport::vthread_post_monitor_waited(JavaThread *current, ObjectMonitor *obj_mntr, jboolean timed_out) { + Handle vthread(current, current->vthread()); + + // Finish the VTMS transition temporarily to post the event. + JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)vthread.raw_value(), false); + + // Post event. + JvmtiExport::post_monitor_waited(current, obj_mntr, timed_out); + + // Go back to VTMS transition state. + JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount((jthread)vthread.raw_value(), true); +} + void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { if (object == nullptr) { return; diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index e98020aef1d3b..e0fb84f2c032f 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -397,6 +397,7 @@ class JvmtiExport : public AllStatic { static void post_monitor_contended_entered(JavaThread *thread, ObjectMonitor *obj_mntr) NOT_JVMTI_RETURN; static void post_monitor_wait(JavaThread *thread, oop obj, jlong timeout) NOT_JVMTI_RETURN; static void post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mntr, jboolean timed_out) NOT_JVMTI_RETURN; + static void vthread_post_monitor_waited(JavaThread *current, ObjectMonitor *obj_mntr, jboolean timed_out) NOT_JVMTI_RETURN; static void post_object_free(JvmtiEnv* env, GrowableArray* objects) NOT_JVMTI_RETURN; static void post_resource_exhausted(jint resource_exhausted_flags, const char* detail) NOT_JVMTI_RETURN; static void record_vm_internal_object_allocation(oop object) NOT_JVMTI_RETURN; diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index bc91c1050731a..97a4ae10970be 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -2306,10 +2306,11 @@ bool StackRefCollector::report_native_stack_refs(jmethodID method) { _blk->set_context(_thread_tag, _tid, _depth, method); if (_is_top_frame) { // JNI locals for the top frame. - assert(_java_thread != nullptr, "sanity"); - _java_thread->active_handles()->oops_do(_blk); - if (_blk->stopped()) { - return false; + if (_java_thread != nullptr) { + _java_thread->active_handles()->oops_do(_blk); + if (_blk->stopped()) { + return false; + } } } else { if (_last_entry_frame != nullptr) { diff --git a/src/hotspot/share/runtime/basicLock.hpp b/src/hotspot/share/runtime/basicLock.hpp index 193aa63cf8ff4..cbdb65c8ec6a2 100644 --- a/src/hotspot/share/runtime/basicLock.hpp +++ b/src/hotspot/share/runtime/basicLock.hpp @@ -88,6 +88,7 @@ class BasicObjectLock { public: // Manipulation oop obj() const { return _obj; } + oop* obj_adr() { return &_obj; } void set_obj(oop obj) { _obj = obj; } BasicLock* lock() { return &_lock; } diff --git a/src/hotspot/share/runtime/continuation.cpp b/src/hotspot/share/runtime/continuation.cpp index fb697dd4920dd..fd9e46154983d 100644 --- a/src/hotspot/share/runtime/continuation.cpp +++ b/src/hotspot/share/runtime/continuation.cpp @@ -26,6 +26,8 @@ #include "classfile/vmSymbols.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "oops/method.inline.hpp" +#include "oops/oop.inline.hpp" +#include "prims/jvmtiThreadState.inline.hpp" #include "runtime/continuation.hpp" #include "runtime/continuationEntry.inline.hpp" #include "runtime/continuationHelper.inline.hpp" @@ -33,6 +35,7 @@ #include "runtime/continuationWrapper.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" +#include "runtime/jniHandles.inline.hpp" #include "runtime/osThread.hpp" #include "runtime/vframe.inline.hpp" #include "runtime/vframe_hp.hpp" @@ -54,6 +57,112 @@ JVM_ENTRY(void, CONT_unpin(JNIEnv* env, jclass cls)) { } JVM_END +#if INCLUDE_JVMTI +class JvmtiUnmountBeginMark : public StackObj { + Handle _vthread; + JavaThread* _target; + freeze_result _result; + bool _failed; + + public: + JvmtiUnmountBeginMark(JavaThread* t) : + _vthread(t, t->vthread()), _target(t), _result(freeze_pinned_native), _failed(false) { + assert(!_target->is_in_VTMS_transition(), "must be"); + + if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount((jthread)_vthread.raw_value(), true); + + // Don't preempt if there is a pending popframe or earlyret operation. This can + // be installed in start_VTMS_transition() so we need to check it here. + if (JvmtiExport::can_pop_frame() || JvmtiExport::can_force_early_return()) { + JvmtiThreadState* state = _target->jvmti_thread_state(); + if (_target->has_pending_popframe() || (state != nullptr && state->is_earlyret_pending())) { + _failed = true; + } + } + + // Don't preempt in case there is an async exception installed since + // we would incorrectly throw it during the unmount logic in the carrier. + if (_target->has_async_exception_condition()) { + _failed = true; + } + } else { + _target->set_is_in_VTMS_transition(true); + java_lang_Thread::set_is_in_VTMS_transition(_vthread(), true); + } + } + ~JvmtiUnmountBeginMark() { + assert(!_target->is_suspended(), "must be"); + + assert(_target->is_in_VTMS_transition(), "must be"); + assert(java_lang_Thread::is_in_VTMS_transition(_vthread()), "must be"); + + // Read it again since for late binding agents the flag could have + // been set while blocked in the allocation path during freeze. + bool jvmti_present = JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events(); + + if (_result != freeze_ok) { + // Undo transition + if (jvmti_present) { + JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)_vthread.raw_value(), false); + } else { + _target->set_is_in_VTMS_transition(false); + java_lang_Thread::set_is_in_VTMS_transition(_vthread(), false); + } + } + } + void set_result(freeze_result res) { _result = res; } + bool failed() { return _failed; } +}; + +static bool is_vthread_safe_to_preempt_for_jvmti(JavaThread* target) { + if (target->is_in_VTMS_transition()) { + // We caught target at the end of a mount transition. + return false; + } + return true; +} +#endif // INCLUDE_JVMTI + +static bool is_vthread_safe_to_preempt(JavaThread* target, oop vthread) { + assert(java_lang_VirtualThread::is_instance(vthread), ""); + if (java_lang_VirtualThread::state(vthread) != java_lang_VirtualThread::RUNNING) { // inside transition + return false; + } + return JVMTI_ONLY(is_vthread_safe_to_preempt_for_jvmti(target)) NOT_JVMTI(true); +} + +typedef freeze_result (*FreezeContFnT)(JavaThread*, intptr_t*); + +static void verify_preempt_preconditions(JavaThread* target, oop continuation) { + assert(target == JavaThread::current(), "no support for external preemption"); + assert(target->has_last_Java_frame(), ""); + assert(!target->preempting(), ""); + assert(target->last_continuation() != nullptr, ""); + assert(target->last_continuation()->cont_oop(target) == continuation, ""); + assert(Continuation::continuation_scope(continuation) == java_lang_VirtualThread::vthread_scope(), ""); + assert(!target->has_pending_exception(), ""); +} + +freeze_result Continuation::try_preempt(JavaThread* target, oop continuation) { + verify_preempt_preconditions(target, continuation); + + if (LockingMode == LM_LEGACY) { + return freeze_unsupported; + } + + if (!is_vthread_safe_to_preempt(target, target->vthread())) { + return freeze_pinned_native; + } + + JVMTI_ONLY(JvmtiUnmountBeginMark jubm(target);) + JVMTI_ONLY(if (jubm.failed()) return freeze_pinned_native;) + freeze_result res = CAST_TO_FN_PTR(FreezeContFnT, freeze_preempt_entry())(target, target->last_Java_sp()); + log_trace(continuations, preempt)("try_preempt: %d", res); + JVMTI_ONLY(jubm.set_result(res);) + return res; +} + #ifndef PRODUCT static jlong java_tid(JavaThread* thread) { return java_lang_Thread::thread_id(thread->threadObj()); diff --git a/src/hotspot/share/runtime/continuation.hpp b/src/hotspot/share/runtime/continuation.hpp index e579e993f07dc..e678e0bd42b39 100644 --- a/src/hotspot/share/runtime/continuation.hpp +++ b/src/hotspot/share/runtime/continuation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,9 +47,26 @@ void continuations_init(); class javaVFrame; class JavaThread; +// should match Continuation.pinnedReason() in Continuation.java +enum freeze_result { + freeze_ok = 0, + freeze_ok_bottom = 1, + freeze_pinned_cs = 2, + freeze_pinned_native = 3, + freeze_pinned_monitor = 4, + freeze_exception = 5, + freeze_not_mounted = 6, + freeze_unsupported = 7 +}; + class Continuation : AllStatic { public: + enum preempt_kind { + freeze_on_monitorenter, + freeze_on_wait + }; + enum thaw_kind { thaw_top = 0, thaw_return_barrier = 1, @@ -69,9 +86,12 @@ class Continuation : AllStatic { static void init(); static address freeze_entry(); + static address freeze_preempt_entry(); static int prepare_thaw(JavaThread* thread, bool return_barrier); static address thaw_entry(); + static freeze_result try_preempt(JavaThread* target, oop continuation); + static ContinuationEntry* get_continuation_entry_for_continuation(JavaThread* thread, oop continuation); static ContinuationEntry* get_continuation_entry_for_sp(JavaThread* thread, intptr_t* const sp); static ContinuationEntry* get_continuation_entry_for_entry_frame(JavaThread* thread, const frame& f); diff --git a/src/hotspot/share/runtime/continuationEntry.cpp b/src/hotspot/share/runtime/continuationEntry.cpp index 31b062292f0a2..254287df18d80 100644 --- a/src/hotspot/share/runtime/continuationEntry.cpp +++ b/src/hotspot/share/runtime/continuationEntry.cpp @@ -36,16 +36,23 @@ #include "runtime/stubRoutines.hpp" int ContinuationEntry::_return_pc_offset = 0; +int ContinuationEntry::_thaw_call_pc_offset = 0; +int ContinuationEntry::_cleanup_offset = 0; address ContinuationEntry::_return_pc = nullptr; +address ContinuationEntry::_thaw_call_pc = nullptr; +address ContinuationEntry::_cleanup_pc = nullptr; nmethod* ContinuationEntry::_enter_special = nullptr; int ContinuationEntry::_interpreted_entry_offset = 0; void ContinuationEntry::set_enter_code(nmethod* nm, int interpreted_entry_offset) { assert(_return_pc_offset != 0, ""); _return_pc = nm->code_begin() + _return_pc_offset; + _thaw_call_pc = nm->code_begin() + _thaw_call_pc_offset; + _cleanup_pc = nm->code_begin() + _cleanup_offset; _enter_special = nm; _interpreted_entry_offset = interpreted_entry_offset; + assert(_enter_special->code_contains(compiled_entry()), "entry not in enterSpecial"); assert(_enter_special->code_contains(interpreted_entry()), "entry not in enterSpecial"); assert(interpreted_entry() < compiled_entry(), "unexpected code layout"); diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp index ac76cd6f0882d..990ee6c6a4257 100644 --- a/src/hotspot/share/runtime/continuationEntry.hpp +++ b/src/hotspot/share/runtime/continuationEntry.hpp @@ -57,11 +57,16 @@ class ContinuationEntry { public: static int _return_pc_offset; // friend gen_continuation_enter + static int _thaw_call_pc_offset; + static int _cleanup_offset; + static void set_enter_code(nmethod* nm, int interpreted_entry_offset); static bool is_interpreted_call(address call_address); private: static address _return_pc; + static address _thaw_call_pc; + static address _cleanup_pc; static nmethod* _enter_special; static int _interpreted_entry_offset; @@ -101,6 +106,9 @@ class ContinuationEntry { intptr_t* entry_sp() const { return (intptr_t*)this; } intptr_t* entry_fp() const; + static address thaw_call_pc_address() { return (address)&_thaw_call_pc; } + static address cleanup_pc() { return _cleanup_pc; } + static address compiled_entry(); static address interpreted_entry(); diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index ce2e2bdb9ff80..be4fef5255cad 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -55,6 +55,7 @@ #include "runtime/javaThread.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/keepStackGCProcessed.hpp" +#include "runtime/objectMonitor.inline.hpp" #include "runtime/orderAccess.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/smallRegisterMap.inline.hpp" @@ -66,6 +67,7 @@ #include "utilities/debug.hpp" #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" +#include "utilities/vmError.hpp" #if INCLUDE_ZGC #include "gc/z/zStackChunkGCData.inline.hpp" #endif @@ -179,6 +181,7 @@ static void verify_continuation(oop continuation) { Continuation::debug_verify_c static void do_deopt_after_thaw(JavaThread* thread); static bool do_verify_after_thaw(JavaThread* thread, stackChunkOop chunk, outputStream* st); static void log_frames(JavaThread* thread); +static void log_frames_after_thaw(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp, bool preempted); static void print_frame_layout(const frame& f, bool callee_complete, outputStream* st = tty); #define assert_pfl(p, ...) \ @@ -198,27 +201,8 @@ static void verify_continuation(oop continuation) { } #define assert_pfl(p, ...) #endif -// should match Continuation.preemptStatus() in Continuation.java -enum freeze_result { - freeze_ok = 0, - freeze_ok_bottom = 1, - freeze_pinned_cs = 2, - freeze_pinned_native = 3, - freeze_pinned_monitor = 4, - freeze_exception = 5 -}; - -const char* freeze_result_names[6] = { - "freeze_ok", - "freeze_ok_bottom", - "freeze_pinned_cs", - "freeze_pinned_native", - "freeze_pinned_monitor", - "freeze_exception" -}; - static freeze_result is_pinned0(JavaThread* thread, oop cont_scope, bool safepoint); -template static inline int freeze_internal(JavaThread* current, intptr_t* const sp); +template static inline freeze_result freeze_internal(JavaThread* current, intptr_t* const sp); static inline int prepare_thaw_internal(JavaThread* thread, bool return_barrier); template static inline intptr_t* thaw_internal(JavaThread* thread, const Continuation::thaw_kind kind); @@ -234,7 +218,7 @@ static JRT_BLOCK_ENTRY(int, freeze(JavaThread* current, intptr_t* sp)) current->set_cont_fastpath(nullptr); } - return ConfigT::freeze(current, sp); + return checked_cast(ConfigT::freeze(current, sp)); JRT_END JRT_LEAF(int, Continuation::prepare_thaw(JavaThread* thread, bool return_barrier)) @@ -245,8 +229,11 @@ template static JRT_LEAF(intptr_t*, thaw(JavaThread* thread, int kind)) // TODO: JRT_LEAF and NoHandleMark is problematic for JFR events. // vFrameStreamCommon allocates Handles in RegisterMap for continuations. + // Also the preemption case with JVMTI events enabled might safepoint so + // undo the NoSafepointVerifier here and rely on handling by ContinuationWrapper. // JRT_ENTRY instead? ResetNoHandleMark rnhm; + DEBUG_ONLY(PauseNoSafepointVerifier pnsv(&__nsv);) // we might modify the code cache via BarrierSetNMethod::nmethod_entry_barrier MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); @@ -268,8 +255,12 @@ class Config { typedef Config SelfT; using OopT = std::conditional_t; - static int freeze(JavaThread* thread, intptr_t* const sp) { - return freeze_internal(thread, sp); + static freeze_result freeze(JavaThread* thread, intptr_t* const sp) { + return freeze_internal(thread, sp); + } + + static freeze_result freeze_preempt(JavaThread* thread, intptr_t* const sp) { + return freeze_internal(thread, sp); } static intptr_t* thaw(JavaThread* thread, Continuation::thaw_kind kind) { @@ -312,14 +303,13 @@ static oop get_continuation(JavaThread* thread) { assert(thread->threadObj() != nullptr, ""); return java_lang_Thread::continuation(thread->threadObj()); } +#endif // ASSERT inline void clear_anchor(JavaThread* thread) { thread->frame_anchor()->clear(); } -static void set_anchor(JavaThread* thread, intptr_t* sp) { - address pc = ContinuationHelper::return_address_at( - sp - frame::sender_sp_ret_address_offset()); +static void set_anchor(JavaThread* thread, intptr_t* sp, address pc) { assert(pc != nullptr, ""); JavaFrameAnchor* anchor = thread->frame_anchor(); @@ -330,7 +320,12 @@ static void set_anchor(JavaThread* thread, intptr_t* sp) { assert(thread->has_last_Java_frame(), ""); assert(thread->last_frame().cb() != nullptr, ""); } -#endif // ASSERT + +static void set_anchor(JavaThread* thread, intptr_t* sp) { + address pc = ContinuationHelper::return_address_at( + sp - frame::sender_sp_ret_address_offset()); + set_anchor(thread, sp, pc); +} static void set_anchor_to_entry(JavaThread* thread, ContinuationEntry* entry) { JavaFrameAnchor* anchor = thread->frame_anchor(); @@ -373,11 +368,16 @@ class FreezeBase : public StackObj { JavaThread* const _thread; ContinuationWrapper& _cont; bool _barriers; // only set when we allocate a chunk - const bool _preempt; // used only on the slow path - const intptr_t * const _frame_sp; // Top frame sp for this freeze intptr_t* _bottom_address; + // Used for preemption only + const bool _preempt; + frame _last_frame; + + // Used to support freezing with held monitors + int _monitors_in_lockstack; + int _freeze_size; // total size of all frames plus metadata in words. int _total_align_size; @@ -397,7 +397,7 @@ class FreezeBase : public StackObj { NOT_PRODUCT(int _frames;) DEBUG_ONLY(intptr_t* _last_write;) - inline FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp); + inline FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp, bool preempt); public: NOINLINE freeze_result freeze_slow(); @@ -408,6 +408,8 @@ class FreezeBase : public StackObj { inline int size_if_fast_freeze_available(); + inline frame& last_frame() { return _last_frame; } + #ifdef ASSERT bool check_valid_fast_path(); #endif @@ -429,9 +431,9 @@ class FreezeBase : public StackObj { private: // slow path frame freeze_start_frame(); - frame freeze_start_frame_safepoint_stub(frame f); + frame freeze_start_frame_on_preempt(); NOINLINE freeze_result recurse_freeze(frame& f, frame& caller, int callee_argsize, bool callee_interpreted, bool top); - inline frame freeze_start_frame_yield_stub(frame f); + inline frame freeze_start_frame_yield_stub(); template inline freeze_result recurse_freeze_java_frame(const frame& f, frame& caller, int fsize, int argsize); inline void before_freeze_java_frame(const frame& f, const frame& caller, int fsize, int argsize, bool is_bottom_frame); @@ -441,8 +443,11 @@ class FreezeBase : public StackObj { NOINLINE freeze_result recurse_freeze_interpreted_frame(frame& f, frame& caller, int callee_argsize, bool callee_interpreted); freeze_result recurse_freeze_compiled_frame(frame& f, frame& caller, int callee_argsize, bool callee_interpreted); NOINLINE freeze_result recurse_freeze_stub_frame(frame& f, frame& caller); + NOINLINE freeze_result recurse_freeze_native_frame(frame& f, frame& caller); NOINLINE void finish_freeze(const frame& f, const frame& top); + void freeze_lockstack(stackChunkOop chunk); + inline bool stack_overflow(); static frame sender(const frame& f) { return f.is_interpreted_frame() ? sender(f) @@ -452,6 +457,7 @@ class FreezeBase : public StackObj { inline void set_top_frame_metadata_pd(const frame& hf); inline void patch_pd(frame& callee, const frame& caller); void adjust_interpreted_frame_unextended_sp(frame& f); + static inline void prepare_freeze_interpreted_top_frame(frame& f); static inline void relativize_interpreted_frame_metadata(const frame& f, const frame& hf); protected: @@ -465,8 +471,8 @@ class Freeze : public FreezeBase { stackChunkOop allocate_chunk(size_t stack_size, int argsize_md); public: - inline Freeze(JavaThread* thread, ContinuationWrapper& cont, intptr_t* frame_sp) - : FreezeBase(thread, cont, frame_sp) {} + inline Freeze(JavaThread* thread, ContinuationWrapper& cont, intptr_t* frame_sp, bool preempt) + : FreezeBase(thread, cont, frame_sp, preempt) {} freeze_result try_freeze_fast(); @@ -474,8 +480,8 @@ class Freeze : public FreezeBase { virtual stackChunkOop allocate_chunk_slow(size_t stack_size, int argsize_md) override { return allocate_chunk(stack_size, argsize_md); } }; -FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* frame_sp) : - _thread(thread), _cont(cont), _barriers(false), _preempt(false), _frame_sp(frame_sp) { +FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* frame_sp, bool preempt) : + _thread(thread), _cont(cont), _barriers(false), _preempt(preempt), _last_frame(false /* no initialization */) { DEBUG_ONLY(_jvmti_event_collector = nullptr;) assert(_thread != nullptr, ""); @@ -506,16 +512,27 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* #else static const int doYield_stub_frame_size = frame::native_abi_reg_args_size >> LogBytesPerWord; #endif - assert(SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, ""); + // With preemption doYield() might not have been resolved yet + assert(_preempt || SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, ""); + + if (preempt) { + _last_frame = _thread->last_frame(); + } // properties of the continuation on the stack; all sizes are in words - _cont_stack_top = frame_sp + doYield_stub_frame_size; // we don't freeze the doYield stub frame + _cont_stack_top = frame_sp + (!preempt ? doYield_stub_frame_size : 0); // we don't freeze the doYield stub frame _cont_stack_bottom = _cont.entrySP() + (_cont.argsize() == 0 ? frame::metadata_words_at_top : 0) - ContinuationHelper::frame_align_words(_cont.argsize()); // see alignment in thaw log_develop_trace(continuations)("freeze size: %d argsize: %d top: " INTPTR_FORMAT " bottom: " INTPTR_FORMAT, cont_size(), _cont.argsize(), p2i(_cont_stack_top), p2i(_cont_stack_bottom)); assert(cont_size() > 0, ""); + + if (LockingMode != LM_LIGHTWEIGHT) { + _monitors_in_lockstack = 0; + } else { + _monitors_in_lockstack = _thread->lock_stack().monitor_count(); + } } void FreezeBase::init_rest() { // we want to postpone some initialization after chunk handling @@ -524,6 +541,14 @@ void FreezeBase::init_rest() { // we want to postpone some initialization after NOT_PRODUCT(_frames = 0;) } +void FreezeBase::freeze_lockstack(stackChunkOop chunk) { + assert(chunk->sp_address() - chunk->start_address() >= _monitors_in_lockstack, "no room for lockstack"); + + _thread->lock_stack().move_to_address((oop*)chunk->start_address()); + chunk->set_lockstack_size(checked_cast(_monitors_in_lockstack)); + chunk->set_has_lockstack(true); +} + void FreezeBase::copy_to_chunk(intptr_t* from, intptr_t* to, int size) { stackChunkOop chunk = _cont.tail(); chunk->copy_from_stack_to_chunk(from, to, size); @@ -554,7 +579,7 @@ freeze_result Freeze::try_freeze_fast() { DEBUG_ONLY(_fast_freeze_size = size_if_fast_freeze_available();) assert(_fast_freeze_size == 0, ""); - stackChunkOop chunk = allocate_chunk(cont_size() + frame::metadata_words, _cont.argsize() + frame::metadata_words_at_top); + stackChunkOop chunk = allocate_chunk(cont_size() + frame::metadata_words + _monitors_in_lockstack, _cont.argsize() + frame::metadata_words_at_top); if (freeze_fast_new_chunk(chunk)) { return freeze_ok; } @@ -587,6 +612,8 @@ int FreezeBase::size_if_fast_freeze_available() { total_size_needed -= _cont.argsize() + frame::metadata_words_at_top; } + total_size_needed += _monitors_in_lockstack; + int chunk_free_room = chunk_sp - frame::metadata_words_at_bottom; bool available = chunk_free_room >= total_size_needed; log_develop_trace(continuations)("chunk available: %s size: %d argsize: %d top: " INTPTR_FORMAT " bottom: " INTPTR_FORMAT, @@ -668,7 +695,7 @@ bool FreezeBase::freeze_fast_new_chunk(stackChunkOop chunk) { // in a fresh chunk, we freeze *with* the bottom-most frame's stack arguments. // They'll then be stored twice: in the chunk and in the parent chunk's top frame - const int chunk_start_sp = cont_size() + frame::metadata_words; + const int chunk_start_sp = cont_size() + frame::metadata_words + _monitors_in_lockstack; assert(chunk_start_sp == chunk->stack_size(), ""); DEBUG_ONLY(_orig_chunk_sp = chunk->start_address() + chunk_start_sp;) @@ -697,7 +724,7 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J assert(chunk_start_sp >= cont_size(), "no room in the chunk"); const int chunk_new_sp = chunk_start_sp - cont_size(); // the chunk's new sp, after freeze - assert(!(_fast_freeze_size > 0) || _orig_chunk_sp - (chunk->start_address() + chunk_new_sp) == _fast_freeze_size, ""); + assert(!(_fast_freeze_size > 0) || (_orig_chunk_sp - (chunk->start_address() + chunk_new_sp)) == (_fast_freeze_size - _monitors_in_lockstack), ""); intptr_t* chunk_top = chunk->start_address() + chunk_new_sp; #ifdef ASSERT @@ -734,9 +761,24 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J // We're always writing to a young chunk, so the GC can't see it until the next safepoint. chunk->set_sp(chunk_new_sp); + // set chunk->pc to the return address of the topmost frame in the chunk - chunk->set_pc(ContinuationHelper::return_address_at( + if (_preempt) { + // On aarch64/riscv64, the return pc of the top frame won't necessarily be at sp[-1]. + // Also, on x64, if the top frame is the native wrapper frame, sp[-1] will not + // be the pc we used when creating the oopmap. Get the top's frame last pc from + // the anchor instead. + address last_pc = _last_frame.pc(); + ContinuationHelper::patch_return_address_at(chunk_top - frame::sender_sp_ret_address_offset(), last_pc); + chunk->set_pc(last_pc); + } else { + chunk->set_pc(ContinuationHelper::return_address_at( _cont_stack_top - frame::sender_sp_ret_address_offset())); + } + + if (_monitors_in_lockstack > 0) { + freeze_lockstack(chunk); + } _cont.write(); @@ -802,43 +844,32 @@ NOINLINE freeze_result FreezeBase::freeze_slow() { } frame FreezeBase::freeze_start_frame() { - frame f = _thread->last_frame(); if (LIKELY(!_preempt)) { - return freeze_start_frame_yield_stub(f); + return freeze_start_frame_yield_stub(); } else { - return freeze_start_frame_safepoint_stub(f); + return freeze_start_frame_on_preempt(); } } -frame FreezeBase::freeze_start_frame_yield_stub(frame f) { +frame FreezeBase::freeze_start_frame_yield_stub() { + frame f = _thread->last_frame(); assert(SharedRuntime::cont_doYield_stub()->contains(f.pc()), "must be"); f = sender(f); assert(Continuation::is_frame_in_continuation(_thread->last_continuation(), f), ""); return f; } -frame FreezeBase::freeze_start_frame_safepoint_stub(frame f) { -#if (defined(X86) || defined(AARCH64) || defined(RISCV64)) && !defined(ZERO) - f.set_fp(f.real_fp()); // f.set_fp(*Frame::callee_link_address(f)); // ???? -#else - Unimplemented(); -#endif - if (!Interpreter::contains(f.pc())) { - assert(ContinuationHelper::Frame::is_stub(f.cb()), "must be"); - assert(f.oop_map() != nullptr, "must be"); - - if (Interpreter::contains(ContinuationHelper::StubFrame::return_pc(f))) { - f = sender(f); // Safepoint stub in interpreter - } - } - assert(Continuation::is_frame_in_continuation(_thread->last_continuation(), f), ""); - return f; +frame FreezeBase::freeze_start_frame_on_preempt() { + assert(_last_frame.sp() == _thread->last_frame().sp(), "_last_frame should be already initialized"); + assert(Continuation::is_frame_in_continuation(_thread->last_continuation(), _last_frame), ""); + return _last_frame; } // The parameter callee_argsize includes metadata that has to be part of caller/callee overlap. NOINLINE freeze_result FreezeBase::recurse_freeze(frame& f, frame& caller, int callee_argsize, bool callee_interpreted, bool top) { assert(f.unextended_sp() < _bottom_address, ""); // see recurse_freeze_java_frame - assert(f.is_interpreted_frame() || ((top && _preempt) == ContinuationHelper::Frame::is_stub(f.cb())), ""); + assert(f.is_interpreted_frame() || ((top && _preempt) == ContinuationHelper::Frame::is_stub(f.cb())) + || ((top && _preempt) == f.is_native_frame()), ""); if (stack_overflow()) { return freeze_exception; @@ -851,16 +882,14 @@ NOINLINE freeze_result FreezeBase::recurse_freeze(frame& f, frame& caller, int c } return recurse_freeze_compiled_frame(f, caller, callee_argsize, callee_interpreted); } else if (f.is_interpreted_frame()) { - assert((_preempt && top) || !f.interpreter_frame_method()->is_native(), ""); - if (_preempt && top && f.interpreter_frame_method()->is_native()) { - // int native entry - return freeze_pinned_native; - } - + assert(!f.interpreter_frame_method()->is_native() || (top && _preempt), ""); return recurse_freeze_interpreted_frame(f, caller, callee_argsize, callee_interpreted); - } else if (_preempt && top && ContinuationHelper::Frame::is_stub(f.cb())) { - return recurse_freeze_stub_frame(f, caller); + } else if (top && _preempt) { + assert(f.is_native_frame() || f.is_runtime_frame(), ""); + return f.is_native_frame() ? recurse_freeze_native_frame(f, caller) : recurse_freeze_stub_frame(f, caller); } else { + // Frame can't be frozen. Most likely the call_stub or upcall_stub + // which indicates there are further natives frames up the stack. return freeze_pinned_native; } } @@ -920,6 +949,7 @@ inline void FreezeBase::after_freeze_java_frame(const frame& hf, bool is_bottom_ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, int argsize_md) { int argsize = argsize_md - frame::metadata_words_at_top; assert(callee.is_interpreted_frame() + || ContinuationHelper::Frame::is_stub(callee.cb()) || callee.cb()->as_nmethod()->is_osr_method() || argsize == _cont.argsize(), "argsize: %d cont.argsize: %d", argsize, _cont.argsize()); log_develop_trace(continuations)("bottom: " INTPTR_FORMAT " count %d size: %d argsize: %d", @@ -962,6 +992,8 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in || unextended_sp == chunk->to_offset(StackChunkFrameStream(chunk).unextended_sp()), ""); assert(chunk != nullptr || unextended_sp < _freeze_size, ""); + _freeze_size += _monitors_in_lockstack; + // _barriers can be set to true by an allocation in freeze_fast, in which case the chunk is available bool allocated_old_in_freeze_fast = _barriers; assert(!allocated_old_in_freeze_fast || (unextended_sp >= _freeze_size && chunk->is_empty()), @@ -1016,12 +1048,23 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in assert(!chunk->is_empty() || StackChunkFrameStream(chunk).is_done(), ""); assert(!chunk->is_empty() || StackChunkFrameStream(chunk).to_frame().is_empty(), ""); + if (_preempt) { + frame f = _thread->last_frame(); + if (f.is_interpreted_frame()) { + // Some platforms do not save the last_sp in the top interpreter frame on VM calls. + // We need it so that on resume we can restore the sp to the right place, since + // thawing might add an alignment word to the expression stack (see finish_thaw()). + // We do it now that we know freezing will be successful. + prepare_freeze_interpreted_top_frame(f); + } + } + // We unwind frames after the last safepoint so that the GC will have found the oops in the frames, but before // writing into the chunk. This is so that an asynchronous stack walk (not at a safepoint) that suspends us here // will either see no continuation or a consistent chunk. unwind_frames(); - chunk->set_max_thawing_size(chunk->max_thawing_size() + _freeze_size - frame::metadata_words); + chunk->set_max_thawing_size(chunk->max_thawing_size() + _freeze_size - _monitors_in_lockstack - frame::metadata_words); if (lt.develop_is_enabled()) { LogStream ls(lt); @@ -1029,6 +1072,10 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in chunk->print_on(&ls); } + if (_monitors_in_lockstack > 0) { + freeze_lockstack(chunk); + } + // The topmost existing frame in the chunk; or an empty frame if the chunk is empty caller = StackChunkFrameStream(chunk).to_frame(); @@ -1048,13 +1095,14 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in frame entry = sender(callee); - assert(Continuation::is_return_barrier_entry(entry.pc()) || Continuation::is_continuation_enterSpecial(entry), ""); + assert((!empty && Continuation::is_return_barrier_entry(entry.pc())) || (empty && Continuation::is_continuation_enterSpecial(entry)), ""); assert(callee.is_interpreted_frame() || entry.sp() == entry.unextended_sp(), ""); #endif return freeze_ok_bottom; } +// After freezing a frame we need to possibly adjust some values related to the caller frame. void FreezeBase::patch(const frame& f, frame& hf, const frame& caller, bool is_bottom_frame) { if (is_bottom_frame) { // If we're the bottom frame, we need to replace the return barrier with the real @@ -1113,8 +1161,8 @@ NOINLINE freeze_result FreezeBase::recurse_freeze_interpreted_frame(frame& f, fr // including metadata between f and its args const int argsize = ContinuationHelper::InterpretedFrame::stack_argsize(f) + frame::metadata_words_at_top; - log_develop_trace(continuations)("recurse_freeze_interpreted_frame %s _size: %d fsize: %d argsize: %d", - frame_method->name_and_sig_as_C_string(), _freeze_size, fsize, argsize); + log_develop_trace(continuations)("recurse_freeze_interpreted_frame %s _size: %d fsize: %d argsize: %d callee_interpreted: %d", + frame_method->name_and_sig_as_C_string(), _freeze_size, fsize, argsize, callee_interpreted); // we'd rather not yield inside methods annotated with @JvmtiMountTransition assert(!ContinuationHelper::Frame::frame_method(f)->jvmti_mount_transition(), ""); @@ -1191,7 +1239,8 @@ freeze_result FreezeBase::recurse_freeze_compiled_frame(frame& f, frame& caller, assert(!is_bottom_frame || !caller.is_compiled_frame() || (heap_frame_top + fsize) == (caller.unextended_sp() + argsize), ""); if (caller.is_interpreted_frame()) { - _total_align_size += frame::align_wiggle; // See Thaw::align + // When thawing the frame we might need to add alignment (see Thaw::align) + _total_align_size += frame::align_wiggle; } patch(f, hf, caller, is_bottom_frame); @@ -1204,44 +1253,77 @@ freeze_result FreezeBase::recurse_freeze_compiled_frame(frame& f, frame& caller, } NOINLINE freeze_result FreezeBase::recurse_freeze_stub_frame(frame& f, frame& caller) { - intptr_t* const stack_frame_top = ContinuationHelper::StubFrame::frame_top(f, 0, 0); + DEBUG_ONLY(frame fsender = sender(f);) + assert(fsender.is_compiled_frame(), "sender should be compiled frame"); + + intptr_t* const stack_frame_top = ContinuationHelper::StubFrame::frame_top(f); const int fsize = f.cb()->frame_size(); log_develop_trace(continuations)("recurse_freeze_stub_frame %s _size: %d fsize: %d :: " INTPTR_FORMAT " - " INTPTR_FORMAT, f.cb()->name(), _freeze_size, fsize, p2i(stack_frame_top), p2i(stack_frame_top+fsize)); - // recurse_freeze_java_frame and freeze inlined here because we need to use a full RegisterMap for lock ownership - NOT_PRODUCT(_frames++;) - _freeze_size += fsize; + freeze_result result = recurse_freeze_java_frame(f, caller, fsize, 0); + if (UNLIKELY(result > freeze_ok_bottom)) { + return result; + } - RegisterMap map(_cont.thread(), - RegisterMap::UpdateMap::include, - RegisterMap::ProcessFrames::skip, - RegisterMap::WalkContinuation::skip); - map.set_include_argument_oops(false); - ContinuationHelper::update_register_map(f, &map); - f.oop_map()->update_register_map(&f, &map); // we have callee-save registers in this case - frame senderf = sender(f); - assert(senderf.unextended_sp() < _bottom_address - 1, ""); - assert(senderf.is_compiled_frame(), ""); - - if (UNLIKELY(senderf.oop_map() == nullptr)) { - // native frame + assert(result == freeze_ok, "should have caller"); + DEBUG_ONLY(before_freeze_java_frame(f, caller, fsize, 0, false /*is_bottom_frame*/);) + + frame hf = new_heap_frame(f, caller); + intptr_t* heap_frame_top = ContinuationHelper::StubFrame::frame_top(hf); + + copy_to_chunk(stack_frame_top, heap_frame_top, fsize); + + patch(f, hf, caller, false /*is_bottom_frame*/); + + DEBUG_ONLY(after_freeze_java_frame(hf, false /*is_bottom_frame*/);) + + caller = hf; + return freeze_ok; +} + +NOINLINE freeze_result FreezeBase::recurse_freeze_native_frame(frame& f, frame& caller) { + if (!f.cb()->as_nmethod()->method()->is_object_wait0()) { + assert(f.cb()->as_nmethod()->method()->is_synchronized(), ""); + // Synchronized native method case. Unlike the interpreter native wrapper, the compiled + // native wrapper tries to acquire the monitor after marshalling the arguments from the + // caller into the native convention. This is so that we have a valid oopMap in case of + // having to block in the slow path. But that would require freezing those registers too + // and then fixing them back on thaw in case of oops. To avoid complicating things and + // given that this would be a rare case anyways just pin the vthread to the carrier. return freeze_pinned_native; } - freeze_result result = recurse_freeze_compiled_frame(senderf, caller, 0, 0); // This might be deoptimized + intptr_t* const stack_frame_top = ContinuationHelper::NativeFrame::frame_top(f); + // There are no stackargs but argsize must include the metadata + const int argsize = frame::metadata_words_at_top; + const int fsize = f.cb()->frame_size() + argsize; + + log_develop_trace(continuations)("recurse_freeze_native_frame %s _size: %d fsize: %d :: " INTPTR_FORMAT " - " INTPTR_FORMAT, + f.cb()->name(), _freeze_size, fsize, p2i(stack_frame_top), p2i(stack_frame_top+fsize)); + + freeze_result result = recurse_freeze_java_frame(f, caller, fsize, argsize); if (UNLIKELY(result > freeze_ok_bottom)) { return result; } - assert(result != freeze_ok_bottom, ""); - assert(!caller.is_interpreted_frame(), ""); - DEBUG_ONLY(before_freeze_java_frame(f, caller, fsize, 0, false);) - frame hf = new_heap_frame(f, caller); - intptr_t* heap_frame_top = ContinuationHelper::StubFrame::frame_top(hf, 0, 0); + assert(result == freeze_ok, "should have caller frame"); + DEBUG_ONLY(before_freeze_java_frame(f, caller, fsize, argsize, false /* is_bottom_frame */);) + + frame hf = new_heap_frame(f, caller); + intptr_t* heap_frame_top = ContinuationHelper::NativeFrame::frame_top(hf); + copy_to_chunk(stack_frame_top, heap_frame_top, fsize); - DEBUG_ONLY(after_freeze_java_frame(hf, false);) + + if (caller.is_interpreted_frame()) { + // When thawing the frame we might need to add alignment (see Thaw::align) + _total_align_size += frame::align_wiggle; + } + + patch(f, hf, caller, false /* is_bottom_frame */); + + DEBUG_ONLY(after_freeze_java_frame(hf, false /* is_bottom_frame */);) caller = hf; return freeze_ok; @@ -1264,6 +1346,8 @@ NOINLINE void FreezeBase::finish_freeze(const frame& f, const frame& top) { chunk->set_max_thawing_size(chunk->max_thawing_size() + _total_align_size); + assert(chunk->sp_address() - chunk->start_address() >= _monitors_in_lockstack, "clash with lockstack"); + // At this point the chunk is consistent if (UNLIKELY(_barriers)) { @@ -1293,6 +1377,7 @@ NOINLINE void FreezeBase::finish_freeze(const frame& f, const frame& top) { ls.print_cr("top hframe after (freeze):"); assert(_cont.last_frame().is_heap_frame(), "should be"); _cont.last_frame().print_on(&ls); + DEBUG_ONLY(print_frame_layout(top, false, &ls);) } assert(_cont.chunk_invariant(), ""); @@ -1327,6 +1412,9 @@ class StackChunkAllocator : public MemAllocator { // zero out fields (but not the stack) const size_t hs = oopDesc::header_size(); + if (oopDesc::has_klass_gap()) { + oopDesc::set_klass_gap(mem, 0); + } Copy::fill_to_aligned_words(mem + hs, vmClasses::StackChunk_klass()->size_helper() - hs); int bottom = (int)_stack_size - _argsize_md; @@ -1435,6 +1523,8 @@ stackChunkOop Freeze::allocate_chunk(size_t stack_size, int argsize_md) assert(chunk->is_empty(), ""); assert(chunk->flags() == 0, ""); assert(chunk->is_gc_mode() == false, ""); + assert(chunk->lockstack_size() == 0, ""); + assert(chunk->object_waiter() == nullptr, ""); // fields are uninitialized chunk->set_parent_access(_cont.last_nonempty_chunk()); @@ -1505,6 +1595,29 @@ static void jvmti_yield_cleanup(JavaThread* thread, ContinuationWrapper& cont) { } invalidate_jvmti_stack(thread); } + +static void jvmti_mount_end(JavaThread* current, ContinuationWrapper& cont, frame top) { + assert(current->vthread() != nullptr, "must be"); + + HandleMarkCleaner hm(current); + Handle vth(current, current->vthread()); + + ContinuationWrapper::SafepointOp so(current, cont); + + // Since we might safepoint set the anchor so that the stack can be walked. + set_anchor(current, top.sp()); + + JRT_BLOCK + JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)vth.raw_value(), false); + + if (current->pending_contended_entered_event()) { + JvmtiExport::post_monitor_contended_entered(current, current->contended_entered_monitor()); + current->set_contended_entered_monitor(nullptr); + } + JRT_BLOCK_END + + clear_anchor(current); +} #endif // INCLUDE_JVMTI #ifdef ASSERT @@ -1517,7 +1630,8 @@ static bool monitors_on_stack(JavaThread* thread) { map.set_include_argument_oops(false); for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) || - (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f))) { + (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f)) || + (f.is_native_frame() && ContinuationHelper::NativeFrame::is_owning_locks(map.thread(), f))) { return true; } } @@ -1534,8 +1648,9 @@ bool FreezeBase::check_valid_fast_path() { RegisterMap::ProcessFrames::skip, RegisterMap::WalkContinuation::skip); map.set_include_argument_oops(false); - for (frame f = freeze_start_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { - if (!f.is_compiled_frame() || f.is_deoptimized_frame()) { + bool is_top_frame = true; + for (frame f = freeze_start_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map), is_top_frame = false) { + if (!((f.is_compiled_frame() && !f.is_deoptimized_frame()) || (is_top_frame && (f.is_runtime_frame() || f.is_native_frame())))) { return false; } } @@ -1543,34 +1658,45 @@ bool FreezeBase::check_valid_fast_path() { } #endif // ASSERT -static inline int freeze_epilog(JavaThread* thread, ContinuationWrapper& cont) { +static inline freeze_result freeze_epilog(ContinuationWrapper& cont) { verify_continuation(cont.continuation()); assert(!cont.is_empty(), ""); - // This is done for the sake of the enterSpecial frame - StackWatermarkSet::after_unwind(thread); log_develop_debug(continuations)("=== End of freeze cont ### #" INTPTR_FORMAT, cont.hash()); - - return 0; + return freeze_ok; } -static int freeze_epilog(JavaThread* thread, ContinuationWrapper& cont, freeze_result res) { +static freeze_result freeze_epilog(JavaThread* thread, ContinuationWrapper& cont, freeze_result res) { if (UNLIKELY(res != freeze_ok)) { + JFR_ONLY(thread->set_last_freeze_fail_result(res);) verify_continuation(cont.continuation()); log_develop_trace(continuations)("=== end of freeze (fail %d)", res); return res; } JVMTI_ONLY(jvmti_yield_cleanup(thread, cont)); // can safepoint - return freeze_epilog(thread, cont); + return freeze_epilog(cont); } -template -static inline int freeze_internal(JavaThread* current, intptr_t* const sp) { +static freeze_result preempt_epilog(ContinuationWrapper& cont, freeze_result res, frame& old_last_frame) { + if (UNLIKELY(res != freeze_ok)) { + verify_continuation(cont.continuation()); + log_develop_trace(continuations)("=== end of freeze (fail %d)", res); + return res; + } + + patch_return_pc_with_preempt_stub(old_last_frame); + cont.tail()->set_preempted(true); + + return freeze_epilog(cont); +} + +template +static inline freeze_result freeze_internal(JavaThread* current, intptr_t* const sp) { assert(!current->has_pending_exception(), ""); #ifdef ASSERT - log_trace(continuations)("~~~~ freeze sp: " INTPTR_FORMAT, p2i(current->last_continuation()->entry_sp())); + log_trace(continuations)("~~~~ freeze sp: " INTPTR_FORMAT "JavaThread: " INTPTR_FORMAT, p2i(current->last_continuation()->entry_sp()), p2i(current)); log_frames(current); #endif @@ -1588,8 +1714,10 @@ static inline int freeze_internal(JavaThread* current, intptr_t* const sp) { assert(entry->is_virtual_thread() == (entry->scope(current) == java_lang_VirtualThread::vthread_scope()), ""); - assert(monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0), + assert(LockingMode != LM_LEGACY || (monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0)), "Held monitor count and locks on stack invariant: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); + assert(LockingMode == LM_LEGACY || (current->held_monitor_count() == 0 && current->jni_monitor_count() == 0), + "Held monitor count should only be used for LM_LEGACY: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); if (entry->is_pinned() || current->held_monitor_count() > 0) { log_develop_debug(continuations)("PINNED due to critical section/hold monitor"); @@ -1597,26 +1725,36 @@ static inline int freeze_internal(JavaThread* current, intptr_t* const sp) { freeze_result res = entry->is_pinned() ? freeze_pinned_cs : freeze_pinned_monitor; log_develop_trace(continuations)("=== end of freeze (fail %d)", res); // Avoid Thread.yield() loops without safepoint polls. - if (SafepointMechanism::should_process(current)) { + if (SafepointMechanism::should_process(current) && !preempt) { cont.done(); // allow safepoint ThreadInVMfromJava tivmfj(current); } return res; } - Freeze freeze(current, cont, sp); + Freeze freeze(current, cont, sp, preempt); assert(!current->cont_fastpath() || freeze.check_valid_fast_path(), ""); bool fast = UseContinuationFastPath && current->cont_fastpath(); if (fast && freeze.size_if_fast_freeze_available() > 0) { freeze.freeze_fast_existing_chunk(); CONT_JFR_ONLY(freeze.jfr_info().post_jfr_event(&event, oopCont, current);) - freeze_epilog(current, cont); - return 0; + return !preempt ? freeze_epilog(cont) : preempt_epilog(cont, freeze_ok, freeze.last_frame()); + } + + if (preempt) { + JvmtiSampledObjectAllocEventCollector jsoaec(false); + freeze.set_jvmti_event_collector(&jsoaec); + + freeze_result res = fast ? freeze.try_freeze_fast() : freeze.freeze_slow(); + + CONT_JFR_ONLY(freeze.jfr_info().post_jfr_event(&event, oopCont, current);) + preempt_epilog(cont, res, freeze.last_frame()); + return res; } log_develop_trace(continuations)("chunk unavailable; transitioning to VM"); - assert(current == JavaThread::current(), "must be current thread except for preempt"); + assert(current == JavaThread::current(), "must be current thread"); JRT_BLOCK // delays a possible JvmtiSampledObjectAllocEventCollector in alloc_chunk JvmtiSampledObjectAllocEventCollector jsoaec(false); @@ -1751,6 +1889,7 @@ class ThawBase : public StackObj { intptr_t* _fastpath; bool _barriers; + bool _preempted_case; intptr_t* _top_unextended_sp_before_thaw; int _align_size; DEBUG_ONLY(intptr_t* _top_stack_address); @@ -1769,18 +1908,24 @@ class ThawBase : public StackObj { } void clear_chunk(stackChunkOop chunk); + template int remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &argsize); void copy_from_chunk(intptr_t* from, intptr_t* to, int size); + void thaw_lockstack(stackChunkOop chunk); + // fast path inline void prefetch_chunk_pd(void* start, int size_words); void patch_return(intptr_t* sp, bool is_last); - // slow path - NOINLINE intptr_t* thaw_slow(stackChunkOop chunk, bool return_barrier); + intptr_t* handle_preempted_continuation(intptr_t* sp, Continuation::preempt_kind preempt_kind, bool fast_case); + inline intptr_t* push_cleanup_continuation(); + void throw_interrupted_exception(JavaThread* current, frame& top); + + void recurse_thaw(const frame& heap_frame, frame& caller, int num_frames, bool top_on_preempt_case); + void finish_thaw(frame& f); private: - void recurse_thaw(const frame& heap_frame, frame& caller, int num_frames, bool top); template bool recurse_thaw_java_frame(frame& caller, int num_frames); void finalize_thaw(frame& entry, int argsize); @@ -1794,12 +1939,13 @@ class ThawBase : public StackObj { NOINLINE void recurse_thaw_interpreted_frame(const frame& hf, frame& caller, int num_frames); void recurse_thaw_compiled_frame(const frame& hf, frame& caller, int num_frames, bool stub_caller); void recurse_thaw_stub_frame(const frame& hf, frame& caller, int num_frames); - void finish_thaw(frame& f); + void recurse_thaw_native_frame(const frame& hf, frame& caller, int num_frames); void push_return_frame(frame& f); inline frame new_entry_frame(); template frame new_stack_frame(const frame& hf, frame& caller, bool bottom); inline void patch_pd(frame& f, const frame& sender); + inline void patch_pd(frame& f, intptr_t* caller_sp); inline intptr_t* align(const frame& hf, intptr_t* frame_sp, frame& caller, bool bottom); void maybe_set_fastpath(intptr_t* sp) { if (sp > _fastpath) _fastpath = sp; } @@ -1823,7 +1969,9 @@ class Thaw : public ThawBase { } inline intptr_t* thaw(Continuation::thaw_kind kind); + template NOINLINE intptr_t* thaw_fast(stackChunkOop chunk); + NOINLINE intptr_t* thaw_slow(stackChunkOop chunk, Continuation::thaw_kind kind); inline void patch_caller_links(intptr_t* sp, intptr_t* bottom); }; @@ -1839,7 +1987,7 @@ inline intptr_t* Thaw::thaw(Continuation::thaw_kind kind) { _barriers = chunk->requires_barriers(); return (LIKELY(can_thaw_fast(chunk))) ? thaw_fast(chunk) - : thaw_slow(chunk, kind != Continuation::thaw_top); + : thaw_slow(chunk, kind); } class ReconstructedStack : public StackObj { @@ -1872,6 +2020,7 @@ inline void ThawBase::clear_chunk(stackChunkOop chunk) { chunk->set_max_thawing_size(0); } +template int ThawBase::remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &argsize) { bool empty = false; StackChunkFrameStream f(chunk); @@ -1879,9 +2028,30 @@ int ThawBase::remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &arg assert(chunk_sp == f.sp(), ""); assert(chunk_sp == f.unextended_sp(), ""); - const int frame_size = f.cb()->frame_size(); + int frame_size = f.cb()->frame_size(); argsize = f.stack_argsize(); + assert(!f.is_stub() || check_stub, ""); + if (check_stub && f.is_stub()) { + // If we don't thaw the top compiled frame too, after restoring the saved + // registers back in Java, we would hit the return barrier to thaw one more + // frame effectively overwriting the restored registers during that call. + f.next(SmallRegisterMap::instance(), true /* stop */); + assert(!f.is_done(), ""); + + f.get_cb(); + assert(f.is_compiled(), ""); + frame_size += f.cb()->frame_size(); + argsize = f.stack_argsize(); + + if (f.cb()->as_nmethod()->is_marked_for_deoptimization()) { + // The caller of the runtime stub when the continuation is preempted is not at a + // Java call instruction, and so cannot rely on nmethod patching for deopt. + log_develop_trace(continuations)("Deoptimizing runtime stub caller"); + f.to_frame().deoptimize(nullptr); // the null thread simply avoids the assertion in deoptimize which we're not set up for + } + } + f.next(SmallRegisterMap::instance(), true /* stop */); empty = f.is_done(); assert(!empty || argsize == chunk->argsize(), ""); @@ -1909,6 +2079,18 @@ int ThawBase::remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &arg return frame_size + argsize + frame::metadata_words_at_top; } +void ThawBase::thaw_lockstack(stackChunkOop chunk) { + int lockStackSize = chunk->lockstack_size(); + assert(lockStackSize > 0 && lockStackSize <= LockStack::CAPACITY, ""); + + oop tmp_lockstack[LockStack::CAPACITY]; + chunk->transfer_lockstack(tmp_lockstack, _barriers); + _thread->lock_stack().move_from_address(tmp_lockstack, lockStackSize); + + chunk->set_lockstack_size(0); + chunk->set_has_lockstack(false); +} + void ThawBase::copy_from_chunk(intptr_t* from, intptr_t* to, int size) { assert(to >= _top_stack_address, "overwrote past thawing space" " to: " INTPTR_FORMAT " top_address: " INTPTR_FORMAT, p2i(to), p2i(_top_stack_address)); @@ -1927,6 +2109,7 @@ void ThawBase::patch_return(intptr_t* sp, bool is_last) { } template +template NOINLINE intptr_t* Thaw::thaw_fast(stackChunkOop chunk) { assert(chunk == _cont.tail(), ""); assert(!chunk->has_mixed_frames(), ""); @@ -1960,7 +2143,7 @@ NOINLINE intptr_t* Thaw::thaw_fast(stackChunkOop chunk) { empty = true; } else { // thaw a single frame partial = true; - thaw_size = remove_top_compiled_frame_from_chunk(chunk, argsize); + thaw_size = remove_top_compiled_frame_from_chunk(chunk, argsize); empty = chunk->is_empty(); } @@ -2016,11 +2199,70 @@ inline bool ThawBase::seen_by_gc() { return _barriers || _cont.tail()->is_gc_mode(); } -NOINLINE intptr_t* ThawBase::thaw_slow(stackChunkOop chunk, bool return_barrier) { +static inline void relativize_chunk_concurrently(stackChunkOop chunk) { +#if INCLUDE_ZGC || INCLUDE_SHENANDOAHGC + if (UseZGC || UseShenandoahGC) { + chunk->relativize_derived_pointers_concurrently(); + } +#endif +} + +template +NOINLINE intptr_t* Thaw::thaw_slow(stackChunkOop chunk, Continuation::thaw_kind kind) { + Continuation::preempt_kind preempt_kind; + bool retry_fast_path = false; + + _preempted_case = chunk->preempted(); + if (_preempted_case) { + if (chunk->object_waiter() != nullptr) { + // Mounted again after preemption. Resume the pending monitor operation, + // which will be either a monitorenter or Object.wait() call. + assert(chunk->current_pending_monitor() != nullptr || chunk->current_waiting_monitor() != nullptr, ""); + ObjectWaiter* waiter = chunk->object_waiter(); + ObjectMonitor* mon = waiter->monitor(); + preempt_kind = waiter->is_wait() ? Continuation::freeze_on_wait : Continuation::freeze_on_monitorenter; + + bool mon_acquired = mon->resume_operation(_thread, waiter, _cont); + assert(!mon_acquired || mon->has_owner(_thread), "invariant"); + if (!mon_acquired) { + // Failed to acquire monitor. Return to enterSpecial to unmount again. + return push_cleanup_continuation(); + } + chunk = _cont.tail(); // reload oop in case of safepoint in resume_operation (if posting JVMTI events). + } else { + // Preemption cancelled in moniterenter case. We actually acquired + // the monitor after freezing all frames so nothing to do. + preempt_kind = Continuation::freeze_on_monitorenter; + } + // Call this first to avoid racing with GC threads later when modifying the chunk flags. + relativize_chunk_concurrently(chunk); + chunk->set_preempted(false); + retry_fast_path = true; + } else { + relativize_chunk_concurrently(chunk); + } + + // On first thaw after freeze restore oops to the lockstack if any. + assert(chunk->lockstack_size() == 0 || kind == Continuation::thaw_top, ""); + if (kind == Continuation::thaw_top && chunk->lockstack_size() > 0) { + thaw_lockstack(chunk); + retry_fast_path = true; + } + + // Retry the fast path now that we possibly cleared the FLAG_HAS_LOCKSTACK + // and FLAG_PREEMPTED flags from the stackChunk. + if (retry_fast_path && can_thaw_fast(chunk)) { + intptr_t* sp = thaw_fast(chunk); + if (_preempted_case) { + return handle_preempted_continuation(sp, preempt_kind, true /* fast_case */); + } + return sp; + } + LogTarget(Trace, continuations) lt; if (lt.develop_is_enabled()) { LogStream ls(lt); - ls.print_cr("thaw slow return_barrier: %d " INTPTR_FORMAT, return_barrier, p2i(chunk)); + ls.print_cr("thaw slow return_barrier: %d " INTPTR_FORMAT, kind, p2i(chunk)); chunk->print_on(true, &ls); } @@ -2034,7 +2276,7 @@ NOINLINE intptr_t* ThawBase::thaw_slow(stackChunkOop chunk, bool return_barrier) DEBUG_ONLY(_frames = 0;) _align_size = 0; - int num_frames = (return_barrier ? 1 : 2); + int num_frames = kind == Continuation::thaw_top ? 2 : 1; _stream = StackChunkFrameStream(chunk); _top_unextended_sp_before_thaw = _stream.unextended_sp(); @@ -2047,14 +2289,8 @@ NOINLINE intptr_t* ThawBase::thaw_slow(stackChunkOop chunk, bool return_barrier) heap_frame.print_value_on(&ls); } -#if INCLUDE_ZGC || INCLUDE_SHENANDOAHGC - if (UseZGC || UseShenandoahGC) { - _cont.tail()->relativize_derived_pointers_concurrently(); - } -#endif - frame caller; // the thawed caller on the stack - recurse_thaw(heap_frame, caller, num_frames, true); + recurse_thaw(heap_frame, caller, num_frames, _preempted_case); finish_thaw(caller); // caller is now the topmost thawed frame _cont.write(); @@ -2065,18 +2301,21 @@ NOINLINE intptr_t* ThawBase::thaw_slow(stackChunkOop chunk, bool return_barrier) _thread->set_cont_fastpath(_fastpath); intptr_t* sp = caller.sp(); + + if (_preempted_case) { + return handle_preempted_continuation(sp, preempt_kind, false /* fast_case */); + } return sp; } -void ThawBase::recurse_thaw(const frame& heap_frame, frame& caller, int num_frames, bool top) { +void ThawBase::recurse_thaw(const frame& heap_frame, frame& caller, int num_frames, bool top_on_preempt_case) { log_develop_debug(continuations)("thaw num_frames: %d", num_frames); assert(!_cont.is_empty(), "no more frames"); assert(num_frames > 0, ""); assert(!heap_frame.is_empty(), ""); - if (top && heap_frame.is_safepoint_blob_frame()) { - assert(ContinuationHelper::Frame::is_stub(heap_frame.cb()), "cb: %s", heap_frame.cb()->name()); - recurse_thaw_stub_frame(heap_frame, caller, num_frames); + if (top_on_preempt_case && (heap_frame.is_native_frame() || heap_frame.is_runtime_frame())) { + heap_frame.is_native_frame() ? recurse_thaw_native_frame(heap_frame, caller, 2) : recurse_thaw_stub_frame(heap_frame, caller, 2); } else if (!heap_frame.is_interpreted_frame()) { recurse_thaw_compiled_frame(heap_frame, caller, num_frames, false); } else { @@ -2106,7 +2345,7 @@ bool ThawBase::recurse_thaw_java_frame(frame& caller, int num_frames) { finalize_thaw(caller, FKind::interpreted ? 0 : argsize); return true; // bottom } else { // recurse - recurse_thaw(_stream.to_frame(), caller, num_frames - 1, false); + recurse_thaw(_stream.to_frame(), caller, num_frames - 1, false /* top_on_preempt_case */); return false; } } @@ -2196,6 +2435,59 @@ void ThawBase::clear_bitmap_bits(address start, address end) { assert(effective_end == end || !chunk->bitmap().at(chunk->bit_index_for(effective_end)), "bit should not be set"); } +intptr_t* ThawBase::handle_preempted_continuation(intptr_t* sp, Continuation::preempt_kind preempt_kind, bool fast_case) { + assert(preempt_kind == Continuation::freeze_on_wait || preempt_kind == Continuation::freeze_on_monitorenter, ""); + frame top(sp); + assert(top.pc() == *(address*)(sp - frame::sender_sp_ret_address_offset()), ""); + +#if INCLUDE_JVMTI + // Finish the VTMS transition. + assert(_thread->is_in_VTMS_transition(), "must be"); + bool is_vthread = Continuation::continuation_scope(_cont.continuation()) == java_lang_VirtualThread::vthread_scope(); + if (is_vthread) { + if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { + jvmti_mount_end(_thread, _cont, top); + } else { + _thread->set_is_in_VTMS_transition(false); + java_lang_Thread::set_is_in_VTMS_transition(_thread->vthread(), false); + } + } +#endif + + if (fast_case) { + // If we thawed in the slow path the runtime stub/native wrapper frame already + // has the correct fp (see ThawBase::new_stack_frame). On the fast path though, + // we copied the original fp at the time of freeze which now will have to be fixed. + assert(top.is_runtime_frame() || top.is_native_frame(), ""); + int fsize = top.cb()->frame_size(); + patch_pd(top, sp + fsize); + } + + if (preempt_kind == Continuation::freeze_on_wait) { + // Check now if we need to throw IE exception. + if (_thread->pending_interrupted_exception()) { + throw_interrupted_exception(_thread, top); + _thread->set_pending_interrupted_exception(false); + } + } else if (top.is_runtime_frame()) { + // The continuation might now run on a different platform thread than the previous time so + // we need to adjust the current thread saved in the stub frame before restoring registers. + JavaThread** thread_addr = frame::saved_thread_address(top); + if (thread_addr != nullptr) *thread_addr = _thread; + } + return sp; +} + +void ThawBase::throw_interrupted_exception(JavaThread* current, frame& top) { + ContinuationWrapper::SafepointOp so(current, _cont); + // Since we might safepoint set the anchor so that the stack can be walked. + set_anchor(current, top.sp()); + JRT_BLOCK + THROW(vmSymbols::java_lang_InterruptedException()); + JRT_BLOCK_END + clear_anchor(current); +} + NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& caller, int num_frames) { assert(hf.is_interpreted_frame(), ""); @@ -2239,7 +2531,9 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c maybe_set_fastpath(f.sp()); - const int locals = hf.interpreter_frame_method()->max_locals(); + Method* m = hf.interpreter_frame_method(); + assert(!m->is_native() || !is_bottom_frame, "should be top frame of thaw_top case; missing caller frame"); + const int locals = m->max_locals(); if (!is_bottom_frame) { // can only fix caller once this frame is thawed (due to callee saved regs) @@ -2256,8 +2550,8 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c } void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int num_frames, bool stub_caller) { - assert(!hf.is_interpreted_frame(), ""); - assert(_cont.is_preempted() || !stub_caller, "stub caller not at preemption"); + assert(hf.is_compiled_frame(), ""); + assert(_preempted_case || !stub_caller, "stub caller not at preemption"); if (!stub_caller && UNLIKELY(seen_by_gc())) { // recurse_thaw_stub_frame already invoked our barriers with a full regmap _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance()); @@ -2270,7 +2564,7 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n assert(caller.sp() == caller.unextended_sp(), ""); if ((!is_bottom_frame && caller.is_interpreted_frame()) || (is_bottom_frame && Interpreter::contains(_cont.tail()->pc()))) { - _align_size += frame::align_wiggle; // we add one whether or not we've aligned because we add it in freeze_interpreted_frame + _align_size += frame::align_wiggle; // we add one whether or not we've aligned because we add it in recurse_freeze_compiled_frame } // new_stack_frame must construct the resulting frame using hf.pc() rather than hf.raw_pc() because the frame is not @@ -2303,10 +2597,9 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n if (hf.is_deoptimized_frame()) { maybe_set_fastpath(f.sp()); } else if (_thread->is_interp_only_mode() - || (_cont.is_preempted() && f.cb()->as_nmethod()->is_marked_for_deoptimization())) { + || (stub_caller && f.cb()->as_nmethod()->is_marked_for_deoptimization())) { // The caller of the safepoint stub when the continuation is preempted is not at a call instruction, and so // cannot rely on nmethod patching for deopt. - assert(_thread->is_interp_only_mode() || stub_caller, "expected a stub-caller"); log_develop_trace(continuations)("Deoptimizing thawed frame"); DEBUG_ONLY(ContinuationHelper::Frame::patch_pc(f, nullptr)); @@ -2334,7 +2627,8 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n void ThawBase::recurse_thaw_stub_frame(const frame& hf, frame& caller, int num_frames) { DEBUG_ONLY(_frames++;) - { + if (UNLIKELY(seen_by_gc())) { + // Process the stub's caller here since we might need the full map. RegisterMap map(nullptr, RegisterMap::UpdateMap::include, RegisterMap::ProcessFrames::skip, @@ -2342,41 +2636,89 @@ void ThawBase::recurse_thaw_stub_frame(const frame& hf, frame& caller, int num_f map.set_include_argument_oops(false); _stream.next(&map); assert(!_stream.is_done(), ""); - if (UNLIKELY(seen_by_gc())) { // we're now doing this on the stub's caller - _cont.tail()->do_barriers(_stream, &map); - } + _cont.tail()->do_barriers(_stream, &map); + } else { + _stream.next(SmallRegisterMap::instance()); assert(!_stream.is_done(), ""); } - recurse_thaw_compiled_frame(_stream.to_frame(), caller, num_frames, true); // this could be deoptimized + recurse_thaw_compiled_frame(_stream.to_frame(), caller, num_frames, true); - DEBUG_ONLY(before_thaw_java_frame(hf, caller, false, num_frames);) - - assert(ContinuationHelper::Frame::is_stub(hf.cb()), ""); + assert(caller.is_compiled_frame(), ""); assert(caller.sp() == caller.unextended_sp(), ""); - assert(!caller.is_interpreted_frame(), ""); - int fsize = ContinuationHelper::StubFrame::size(hf); + DEBUG_ONLY(before_thaw_java_frame(hf, caller, false /*is_bottom_frame*/, num_frames);) frame f = new_stack_frame(hf, caller, false); intptr_t* stack_frame_top = f.sp(); intptr_t* heap_frame_top = hf.sp(); + int fsize = ContinuationHelper::StubFrame::size(hf); copy_from_chunk(heap_frame_top - frame::metadata_words, stack_frame_top - frame::metadata_words, fsize + frame::metadata_words); - { // can only fix caller once this frame is thawed (due to callee saved regs) - RegisterMap map(nullptr, - RegisterMap::UpdateMap::include, - RegisterMap::ProcessFrames::skip, - RegisterMap::WalkContinuation::skip); // map.clear(); - map.set_include_argument_oops(false); - f.oop_map()->update_register_map(&f, &map); - ContinuationHelper::update_register_map_with_callee(caller, &map); - _cont.tail()->fix_thawed_frame(caller, &map); + patch(f, caller, false /*is_bottom_frame*/); + + // can only fix caller once this frame is thawed (due to callee saved regs) + RegisterMap map(nullptr, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + map.set_include_argument_oops(false); + f.oop_map()->update_register_map(&f, &map); + ContinuationHelper::update_register_map_with_callee(caller, &map); + _cont.tail()->fix_thawed_frame(caller, &map); + + DEBUG_ONLY(after_thaw_java_frame(f, false /*is_bottom_frame*/);) + caller = f; +} + +void ThawBase::recurse_thaw_native_frame(const frame& hf, frame& caller, int num_frames) { + assert(hf.is_native_frame(), ""); + assert(_preempted_case && hf.cb()->as_nmethod()->method()->is_object_wait0(), ""); + + if (UNLIKELY(seen_by_gc())) { // recurse_thaw_stub_frame already invoked our barriers with a full regmap + _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance()); } - DEBUG_ONLY(after_thaw_java_frame(f, false);) + const bool is_bottom_frame = recurse_thaw_java_frame(caller, num_frames); + assert(!is_bottom_frame, ""); + + DEBUG_ONLY(before_thaw_java_frame(hf, caller, is_bottom_frame, num_frames);) + + assert(caller.sp() == caller.unextended_sp(), ""); + + if (caller.is_interpreted_frame()) { + _align_size += frame::align_wiggle; // we add one whether or not we've aligned because we add it in recurse_freeze_native_frame + } + + // new_stack_frame must construct the resulting frame using hf.pc() rather than hf.raw_pc() because the frame is not + // yet laid out in the stack, and so the original_pc is not stored in it. + // As a result, f.is_deoptimized_frame() is always false and we must test hf to know if the frame is deoptimized. + frame f = new_stack_frame(hf, caller, false /* bottom */); + intptr_t* const stack_frame_top = f.sp(); + intptr_t* const heap_frame_top = hf.unextended_sp(); + + int fsize = ContinuationHelper::NativeFrame::size(hf); + assert(fsize <= (int)(caller.unextended_sp() - f.unextended_sp()), ""); + + intptr_t* from = heap_frame_top - frame::metadata_words_at_bottom; + intptr_t* to = stack_frame_top - frame::metadata_words_at_bottom; + int sz = fsize + frame::metadata_words_at_bottom; + + copy_from_chunk(from, to, sz); // copying good oops because we invoked barriers above + + patch(f, caller, false /* bottom */); + + // f.is_deoptimized_frame() is always false and we must test hf.is_deoptimized_frame() (see comment above) + assert(!f.is_deoptimized_frame(), ""); + assert(!hf.is_deoptimized_frame(), ""); + assert(!f.cb()->as_nmethod()->is_marked_for_deoptimization(), ""); + + // can only fix caller once this frame is thawed (due to callee saved regs); this happens on the stack + _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance()); + + DEBUG_ONLY(after_thaw_java_frame(f, false /* bottom */);) caller = f; } @@ -2463,30 +2805,11 @@ static inline intptr_t* thaw_internal(JavaThread* thread, const Continuation::th clear_anchor(thread); #endif + DEBUG_ONLY(bool preempted = cont.tail()->preempted();) Thaw thw(thread, cont); intptr_t* const sp = thw.thaw(kind); assert(is_aligned(sp, frame::frame_alignment), ""); - - // All or part of the frames have been thawed so we know they don't hold any monitors except JNI monitors. - assert(thread->held_monitor_count() == thread->jni_monitor_count(), "Must be"); - -#ifdef ASSERT - intptr_t* sp0 = sp; - set_anchor(thread, sp0); - log_frames(thread); - if (LoomVerifyAfterThaw) { - assert(do_verify_after_thaw(thread, cont.tail(), tty), ""); - } - assert(ContinuationEntry::assert_entry_frame_laid_out(thread), ""); - clear_anchor(thread); - - LogTarget(Trace, continuations) lt; - if (lt.develop_is_enabled()) { - LogStream ls(lt); - ls.print_cr("Jumping to frame (thaw):"); - frame(sp).print_value_on(&ls); - } -#endif + DEBUG_ONLY(log_frames_after_thaw(thread, cont, sp, preempted);) CONT_JFR_ONLY(thw.jfr_info().post_jfr_event(&event, cont.continuation(), thread);) @@ -2591,7 +2914,7 @@ static void log_frames(JavaThread* thread) { } LogStream ls(lt); - ls.print_cr("------- frames ---------"); + ls.print_cr("------- frames --------- for thread " INTPTR_FORMAT, p2i(thread)); if (!thread->has_last_Java_frame()) { ls.print_cr("NO ANCHOR!"); } @@ -2615,8 +2938,8 @@ static void log_frames(JavaThread* thread) { int i = 0; int post_entry = -1; - for (frame f = thread->last_frame(); !f.is_entry_frame(); f = f.sender(&map)) { - f.describe(values, i++, &map); + for (frame f = thread->last_frame(); !f.is_first_frame(); f = f.sender(&map), i++) { + f.describe(values, i, &map, i == 0); if (post_entry >= 0 || Continuation::is_continuation_enterSpecial(f)) post_entry++; if (post_entry >= show_entry_callers) @@ -2627,6 +2950,33 @@ static void log_frames(JavaThread* thread) { ls.print_cr("======= end frames ========="); } + +static void log_frames_after_thaw(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp, bool preempted) { + intptr_t* sp0 = sp; + address pc0 = *(address*)(sp - frame::sender_sp_ret_address_offset()); + + if (preempted && sp0 == cont.entrySP()) { + // Still preempted (monitor not acquired) so no frames were thawed. + assert(cont.tail()->preempted(), ""); + set_anchor(thread, cont.entrySP(), cont.entryPC()); + } else { + set_anchor(thread, sp0); + } + + log_frames(thread); + if (LoomVerifyAfterThaw) { + assert(do_verify_after_thaw(thread, cont.tail(), tty), ""); + } + assert(ContinuationEntry::assert_entry_frame_laid_out(thread), ""); + clear_anchor(thread); + + LogTarget(Trace, continuations) lt; + if (lt.develop_is_enabled()) { + LogStream ls(lt); + ls.print_cr("Jumping to frame (thaw):"); + frame(sp).print_value_on(&ls); + } +} #endif // ASSERT #include CPU_HEADER_INLINE(continuationFreezeThaw) @@ -2647,13 +2997,14 @@ static void print_frame_layout(const frame& f, bool callee_complete, outputStrea if (callee_complete) { frame::update_map_with_saved_link(&map, ContinuationHelper::Frame::callee_link_address(f)); } - const_cast(f).describe(values, 0, &map); + const_cast(f).describe(values, 0, &map, true); values.print_on(static_cast(nullptr), st); } #endif static address thaw_entry = nullptr; static address freeze_entry = nullptr; +static address freeze_preempt_entry = nullptr; address Continuation::thaw_entry() { return ::thaw_entry; @@ -2663,6 +3014,10 @@ address Continuation::freeze_entry() { return ::freeze_entry; } +address Continuation::freeze_preempt_entry() { + return ::freeze_preempt_entry; +} + class ConfigResolve { public: static void resolve() { resolve_compressed(); } @@ -2696,6 +3051,7 @@ class ConfigResolve { typedef Config SelectedConfigT; freeze_entry = (address)freeze; + freeze_preempt_entry = (address)SelectedConfigT::freeze_preempt; // If we wanted, we could templatize by kind and have three different thaw entries thaw_entry = (address)thaw; diff --git a/src/hotspot/share/runtime/continuationHelper.hpp b/src/hotspot/share/runtime/continuationHelper.hpp index 6d8474e1844e0..0b3e6c7245246 100644 --- a/src/hotspot/share/runtime/continuationHelper.hpp +++ b/src/hotspot/share/runtime/continuationHelper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,7 @@ class ContinuationHelper { class InterpretedFrame; class NonInterpretedUnknownFrame; class CompiledFrame; + class NativeFrame; class StubFrame; }; @@ -65,6 +66,7 @@ class ContinuationHelper::Frame : public AllStatic { public: static const bool interpreted = false; static const bool stub = false; + static const bool native = false; static inline intptr_t** callee_link_address(const frame& f); static Method* frame_method(const frame& f); @@ -133,11 +135,25 @@ class ContinuationHelper::CompiledFrame : public ContinuationHelper::NonInterpre #endif }; +class ContinuationHelper::NativeFrame : public ContinuationHelper::NonInterpretedFrame { +public: + static const bool native = true; + + static bool is_instance(const frame& f); + +#ifdef ASSERT + static bool is_owning_locks(JavaThread* current, const frame& f); +#endif + + static int stack_argsize(const frame& f) { return 0; } +}; + class ContinuationHelper::StubFrame : public ContinuationHelper::NonInterpretedFrame { public: static const bool stub = true; static bool is_instance(const frame& f); + static int stack_argsize(const frame& f) { return 0; } }; #endif // SHARE_VM_RUNTIME_CONTINUATIONHELPER_HPP diff --git a/src/hotspot/share/runtime/continuationHelper.inline.hpp b/src/hotspot/share/runtime/continuationHelper.inline.hpp index d6b18a758156d..279e2a5ffd52d 100644 --- a/src/hotspot/share/runtime/continuationHelper.inline.hpp +++ b/src/hotspot/share/runtime/continuationHelper.inline.hpp @@ -32,7 +32,9 @@ #include "compiler/oopMap.inline.hpp" #include "interpreter/oopMapCache.hpp" #include "runtime/frame.inline.hpp" +#include "runtime/objectMonitor.hpp" #include "runtime/stackValue.hpp" +#include "runtime/synchronizer.hpp" #include "utilities/macros.hpp" #include CPU_HEADER_INLINE(continuationHelper) @@ -53,7 +55,7 @@ inline bool ContinuationHelper::NonInterpretedUnknownFrame::is_instance(const fr } inline bool ContinuationHelper::Frame::is_stub(CodeBlob* cb) { - return cb != nullptr && (cb->is_safepoint_stub() || cb->is_runtime_stub()); + return cb != nullptr && cb->is_runtime_stub(); } inline Method* ContinuationHelper::Frame::frame_method(const frame& f) { @@ -195,6 +197,26 @@ bool ContinuationHelper::CompiledFrame::is_owning_locks(JavaThread* thread, Regi } #endif +inline bool ContinuationHelper::NativeFrame::is_instance(const frame& f) { + return f.is_native_frame(); +} + +#ifdef ASSERT +inline bool ContinuationHelper::NativeFrame::is_owning_locks(JavaThread* thread, const frame& f) { + assert(NativeFrame::is_instance(f), ""); + + Method* method = f.cb()->as_nmethod()->method(); + if (!method->is_synchronized()) { + return false; + } else { + // Just verify we are actually the owner + oop synced_obj = f.get_native_receiver(); + assert(ObjectSynchronizer::current_thread_holds_lock(thread, Handle(thread, synced_obj)), "must be owner"); + return true; + } +} +#endif + inline bool ContinuationHelper::StubFrame::is_instance(const frame& f) { return !f.is_interpreted_frame() && is_stub(f.cb()); } diff --git a/src/hotspot/share/runtime/continuationJavaClasses.cpp b/src/hotspot/share/runtime/continuationJavaClasses.cpp index d9f2d05b7e9c9..5ab96f9cf4f85 100644 --- a/src/hotspot/share/runtime/continuationJavaClasses.cpp +++ b/src/hotspot/share/runtime/continuationJavaClasses.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,6 +87,8 @@ int jdk_internal_vm_StackChunk::_pc_offset; int jdk_internal_vm_StackChunk::_bottom_offset; int jdk_internal_vm_StackChunk::_flags_offset; int jdk_internal_vm_StackChunk::_maxThawingSize_offset; +int jdk_internal_vm_StackChunk::_lockStackSize_offset; +int jdk_internal_vm_StackChunk::_objectWaiter_offset; int jdk_internal_vm_StackChunk::_cont_offset; #define STACKCHUNK_FIELDS_DO(macro) \ diff --git a/src/hotspot/share/runtime/continuationJavaClasses.hpp b/src/hotspot/share/runtime/continuationJavaClasses.hpp index 05cba90a0f5ef..d100ef17871cf 100644 --- a/src/hotspot/share/runtime/continuationJavaClasses.hpp +++ b/src/hotspot/share/runtime/continuationJavaClasses.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,10 +71,12 @@ class jdk_internal_vm_Continuation: AllStatic { // Interface to jdk.internal.vm.StackChunk objects #define STACKCHUNK_INJECTED_FIELDS(macro) \ - macro(jdk_internal_vm_StackChunk, cont, continuation_signature, false) \ - macro(jdk_internal_vm_StackChunk, flags, byte_signature, false) \ - macro(jdk_internal_vm_StackChunk, pc, intptr_signature, false) \ - macro(jdk_internal_vm_StackChunk, maxThawingSize, int_signature, false) \ + macro(jdk_internal_vm_StackChunk, cont, continuation_signature, false) \ + macro(jdk_internal_vm_StackChunk, flags, byte_signature, false) \ + macro(jdk_internal_vm_StackChunk, pc, intptr_signature, false) \ + macro(jdk_internal_vm_StackChunk, maxThawingSize, int_signature, false) \ + macro(jdk_internal_vm_StackChunk, lockStackSize, byte_signature, false) \ + macro(jdk_internal_vm_StackChunk, objectWaiter, intptr_signature, false) \ class jdk_internal_vm_StackChunk: AllStatic { friend class JavaClasses; @@ -86,6 +88,8 @@ class jdk_internal_vm_StackChunk: AllStatic { static int _bottom_offset; static int _flags_offset; static int _maxThawingSize_offset; + static int _lockStackSize_offset; + static int _objectWaiter_offset; static int _cont_offset; @@ -124,6 +128,12 @@ class jdk_internal_vm_StackChunk: AllStatic { static inline int maxThawingSize(oop chunk); static inline void set_maxThawingSize(oop chunk, int value); + static inline uint8_t lockStackSize(oop chunk); + static inline void set_lockStackSize(oop chunk, uint8_t value); + + static inline address objectWaiter(oop chunk); + static inline void set_objectWaiter(oop chunk, address mon); + // cont oop's processing is essential for the chunk's GC protocol static inline oop cont(oop chunk); template diff --git a/src/hotspot/share/runtime/continuationJavaClasses.inline.hpp b/src/hotspot/share/runtime/continuationJavaClasses.inline.hpp index b2dea2d704acb..b4400c59c68f9 100644 --- a/src/hotspot/share/runtime/continuationJavaClasses.inline.hpp +++ b/src/hotspot/share/runtime/continuationJavaClasses.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -185,4 +185,21 @@ inline void jdk_internal_vm_StackChunk::set_maxThawingSize(oop chunk, int value) chunk->int_field_put(_maxThawingSize_offset, value); } +// lockStackSize is read concurrently by GC threads so we use Atomic. +inline uint8_t jdk_internal_vm_StackChunk::lockStackSize(oop chunk) { + return Atomic::load(chunk->field_addr(_lockStackSize_offset)); +} + +inline void jdk_internal_vm_StackChunk::set_lockStackSize(oop chunk, uint8_t value) { + Atomic::store(chunk->field_addr(_lockStackSize_offset), value); +} + +inline address jdk_internal_vm_StackChunk::objectWaiter(oop chunk) { + return chunk->address_field(_objectWaiter_offset); +} + +inline void jdk_internal_vm_StackChunk::set_objectWaiter(oop chunk, address value) { + chunk->address_field_put(_objectWaiter_offset, value); +} + #endif // SHARE_RUNTIME_CONTINUATIONJAVACLASSES_INLINE_HPP diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 2006f340450e5..9992d42622f65 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -122,6 +122,10 @@ DeoptimizationScope::~DeoptimizationScope() { } void DeoptimizationScope::mark(nmethod* nm, bool inc_recompile_counts) { + if (!nm->can_be_deoptimized()) { + return; + } + ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag); // If it's already marked but we still need it to be deopted. @@ -1666,7 +1670,7 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArrayowner()->is_locked(), "object must be locked now"); assert(obj->mark().has_monitor(), "must be"); assert(!deoptee_thread->lock_stack().contains(obj()), "must be"); - assert(ObjectSynchronizer::read_monitor(thread, obj(), obj->mark())->owner() == deoptee_thread, "must be"); + assert(ObjectSynchronizer::read_monitor(thread, obj(), obj->mark())->has_owner(deoptee_thread), "must be"); } else { ObjectSynchronizer::enter_for(obj, lock, deoptee_thread); assert(mon_info->owner()->is_locked(), "object must be locked now"); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 17078a69ab991..738b89e77bd47 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -497,7 +497,6 @@ jint frame::interpreter_frame_expression_stack_size() const { return (jint)stack_size; } - // (frame::interpreter_frame_sender_sp accessor is in frame_.cpp) const char* frame::print_name() const { @@ -577,10 +576,21 @@ void frame::interpreter_frame_print_on(outputStream* st) const { current < interpreter_frame_monitor_begin(); current = next_monitor_in_interpreter_frame(current)) { st->print(" - obj [%s", current->obj() == nullptr ? "null" : ""); - if (current->obj() != nullptr) current->obj()->print_value_on(st); + oop obj = current->obj(); + if (obj != nullptr) { + if (!is_heap_frame()) { + obj->print_value_on(st); + } else { + // Might be an invalid oop. We don't have the + // stackChunk to correct it so just print address. + st->print(INTPTR_FORMAT, p2i(obj)); + } + } st->print_cr("]"); st->print(" - lock ["); - current->lock()->print_on(st, current->obj()); + if (!is_heap_frame()) { + current->lock()->print_on(st, obj); + } st->print_cr("]"); } // monitor @@ -1085,7 +1095,7 @@ oop frame::retrieve_receiver(RegisterMap* reg_map) { } -BasicLock* frame::get_native_monitor() { +BasicLock* frame::get_native_monitor() const { nmethod* nm = (nmethod*)_cb; assert(_cb != nullptr && _cb->is_nmethod() && nm->method()->is_native(), "Should not call this unless it's a native nmethod"); @@ -1094,7 +1104,7 @@ BasicLock* frame::get_native_monitor() { return (BasicLock*) &sp()[byte_offset / wordSize]; } -oop frame::get_native_receiver() { +oop frame::get_native_receiver() const { nmethod* nm = (nmethod*)_cb; assert(_cb != nullptr && _cb->is_nmethod() && nm->method()->is_native(), "Should not call this unless it's a native nmethod"); @@ -1340,9 +1350,14 @@ class FrameValuesOopMapClosure: public OopMapClosure { // callers need a ResourceMark because of name_and_sig_as_C_string() usage, // RA allocated string is returned to the caller -void frame::describe(FrameValues& values, int frame_no, const RegisterMap* reg_map) { +void frame::describe(FrameValues& values, int frame_no, const RegisterMap* reg_map, bool top) { // boundaries: sp and the 'real' frame pointer values.describe(-1, sp(), err_msg("sp for #%d", frame_no), 0); + if (top) { + values.describe(-1, sp() - 1, err_msg("sp[-1] for #%d", frame_no), 0); + values.describe(-1, sp() - 2, err_msg("sp[-2] for #%d", frame_no), 0); + } + intptr_t* frame_pointer = real_fp(); // Note: may differ from fp() // print frame info at the highest boundary @@ -1414,7 +1429,7 @@ void frame::describe(FrameValues& values, int frame_no, const RegisterMap* reg_m } else if (is_entry_frame()) { // For now just label the frame values.describe(-1, info_address, err_msg("#%d entry frame", frame_no), 2); - } else if (cb()->is_nmethod()) { + } else if (is_compiled_frame()) { // For now just label the frame nmethod* nm = cb()->as_nmethod(); values.describe(-1, info_address, @@ -1517,17 +1532,16 @@ void frame::describe(FrameValues& values, int frame_no, const RegisterMap* reg_m oop_map()->all_type_do(this, OopMapValue::callee_saved_value, &valuesFn); } } - - if (nm->method()->is_continuation_enter_intrinsic()) { - ContinuationEntry* ce = Continuation::get_continuation_entry_for_entry_frame(reg_map->thread(), *this); // (ContinuationEntry*)unextended_sp(); - ce->describe(values, frame_no); - } } else if (is_native_frame()) { // For now just label the frame nmethod* nm = cb()->as_nmethod_or_null(); values.describe(-1, info_address, FormatBuffer<1024>("#%d nmethod " INTPTR_FORMAT " for native method %s", frame_no, p2i(nm), nm->method()->name_and_sig_as_C_string()), 2); + if (nm->method()->is_continuation_enter_intrinsic()) { + ContinuationEntry* ce = Continuation::get_continuation_entry_for_entry_frame(reg_map->thread(), *this); // (ContinuationEntry*)unextended_sp(); + ce->describe(values, frame_no); + } } else { // provide default info if not handled before char *info = (char *) "special frame"; diff --git a/src/hotspot/share/runtime/frame.hpp b/src/hotspot/share/runtime/frame.hpp index 50aafce3837ac..fe531cff8a646 100644 --- a/src/hotspot/share/runtime/frame.hpp +++ b/src/hotspot/share/runtime/frame.hpp @@ -337,8 +337,8 @@ class frame { // Return the monitor owner and BasicLock for compiled synchronized // native methods. Used by JVMTI's GetLocalInstance method // (via VM_GetReceiver) to retrieve the receiver from a native wrapper frame. - BasicLock* get_native_monitor(); - oop get_native_receiver(); + BasicLock* get_native_monitor() const; + oop get_native_receiver() const; // Find receiver for an invoke when arguments are just pushed on stack (i.e., callee stack-frame is // not setup) @@ -426,6 +426,8 @@ class frame { oop saved_oop_result(RegisterMap* map) const; void set_saved_oop_result(RegisterMap* map, oop obj); + static JavaThread** saved_thread_address(const frame& f); + // For debugging private: const char* print_name() const; @@ -442,7 +444,7 @@ class frame { #ifndef PRODUCT // Add annotated descriptions of memory locations belonging to this frame to values - void describe(FrameValues& values, int frame_no, const RegisterMap* reg_map=nullptr); + void describe(FrameValues& values, int frame_no, const RegisterMap* reg_map=nullptr, bool top = false); #endif // Conversion from a VMReg to physical stack location diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 285c2de17fa26..fd9f75c41b424 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -83,6 +83,7 @@ #include "runtime/stackWatermarkSet.hpp" #include "runtime/synchronizer.hpp" #include "runtime/threadCritical.hpp" +#include "runtime/threadIdentifier.hpp" #include "runtime/threadSMR.inline.hpp" #include "runtime/threadStatisticalInfo.hpp" #include "runtime/threadWXSetters.inline.hpp" @@ -235,6 +236,8 @@ void JavaThread::allocate_threadObj(Handle thread_group, const char* thread_name // constructor calls Thread.current(), which must be set here. java_lang_Thread::set_thread(thread_oop(), this); set_threadOopHandles(thread_oop()); + // Set the lock_id to the next thread_id temporarily while initialization runs. + set_lock_id(ThreadIdentifier::next()); JavaValue result(T_VOID); if (thread_name != nullptr) { @@ -260,6 +263,9 @@ void JavaThread::allocate_threadObj(Handle thread_group, const char* thread_name Handle(), CHECK); } + // Update the lock_id with the tid value. + set_lock_id(java_lang_Thread::thread_id(thread_oop())); + os::set_priority(this, NormPriority); if (daemon) { @@ -429,6 +435,7 @@ JavaThread::JavaThread(MemTag mem_tag) : _current_waiting_monitor(nullptr), _active_handles(nullptr), _free_handle_block(nullptr), + _lock_id(0), _suspend_flags(0), @@ -448,6 +455,8 @@ JavaThread::JavaThread(MemTag mem_tag) : _is_in_VTMS_transition(false), _is_disable_suspend(false), _VTMS_transition_mark(false), + _on_monitor_waited_event(false), + _contended_entered_monitor(nullptr), #ifdef ASSERT _is_VTMS_transition_disabler(false), #endif @@ -488,6 +497,10 @@ JavaThread::JavaThread(MemTag mem_tag) : _jni_monitor_count(0), _unlocked_inflated_monitor(nullptr), + _preempt_alternate_return(nullptr), + _preemption_cancelled(false), + _pending_interrupted_exception(false), + _handshake(this), _popframe_preserved_args(nullptr), @@ -501,6 +514,7 @@ JavaThread::JavaThread(MemTag mem_tag) : _parker(), _class_to_be_initialized(nullptr), + _class_being_initialized(nullptr), _SleepEvent(ParkEvent::Allocate(this)), @@ -1162,6 +1176,7 @@ void JavaThread::send_async_exception(JavaThread* target, oop java_throwable) { #if INCLUDE_JVMTI void JavaThread::set_is_in_VTMS_transition(bool val) { + assert(is_in_VTMS_transition() != val, "already %s transition", val ? "inside" : "outside"); _is_in_VTMS_transition = val; } @@ -1525,9 +1540,8 @@ void JavaThread::print_on(outputStream *st, bool print_extended_info) const { st->print_cr("[" INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12)); if (thread_oop != nullptr) { if (is_vthread_mounted()) { - oop vt = vthread(); - assert(vt != nullptr, ""); - st->print_cr(" Carrying virtual thread #" INT64_FORMAT, (int64_t)java_lang_Thread::thread_id(vt)); + // _lock_id is the thread ID of the mounted virtual thread + st->print_cr(" Carrying virtual thread #" INT64_FORMAT, lock_id()); } else { st->print_cr(" java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop)); } @@ -1711,6 +1725,7 @@ void JavaThread::prepare(jobject jni_thread, ThreadPriority prio) { assert(InstanceKlass::cast(thread_oop->klass())->is_linked(), "must be initialized"); set_threadOopHandles(thread_oop()); + set_lock_id(java_lang_Thread::thread_id(thread_oop())); if (prio == NoPriority) { prio = java_lang_Thread::priority(thread_oop()); @@ -1990,6 +2005,14 @@ void JavaThread::trace_stack() { // this slow-path. void JavaThread::inc_held_monitor_count(intx i, bool jni) { #ifdef SUPPORT_MONITOR_COUNT + + if (LockingMode != LM_LEGACY) { + // Nothing to do. Just do some sanity check. + assert(_held_monitor_count == 0, "counter should not be used"); + assert(_jni_monitor_count == 0, "counter should not be used"); + return; + } + assert(_held_monitor_count >= 0, "Must always be non-negative: " INTX_FORMAT, _held_monitor_count); _held_monitor_count += i; if (jni) { @@ -1998,18 +2021,26 @@ void JavaThread::inc_held_monitor_count(intx i, bool jni) { } assert(_held_monitor_count >= _jni_monitor_count, "Monitor count discrepancy detected - held count " INTX_FORMAT " is less than JNI count " INTX_FORMAT, _held_monitor_count, _jni_monitor_count); -#endif +#endif // SUPPORT_MONITOR_COUNT } // Slow-path decrement of the held monitor counts. JNI unlocking is always // this slow-path. void JavaThread::dec_held_monitor_count(intx i, bool jni) { #ifdef SUPPORT_MONITOR_COUNT + + if (LockingMode != LM_LEGACY) { + // Nothing to do. Just do some sanity check. + assert(_held_monitor_count == 0, "counter should not be used"); + assert(_jni_monitor_count == 0, "counter should not be used"); + return; + } + _held_monitor_count -= i; - assert(_held_monitor_count >= 0, "Must always be greater than 0: " INTX_FORMAT, _held_monitor_count); + assert(_held_monitor_count >= 0, "Must always be non-negative: " INTX_FORMAT, _held_monitor_count); if (jni) { _jni_monitor_count -= i; - assert(_jni_monitor_count >= 0, "Must always be greater than 0: " INTX_FORMAT, _jni_monitor_count); + assert(_jni_monitor_count >= 0, "Must always be non-negative: " INTX_FORMAT, _jni_monitor_count); } // When a thread is detaching with still owned JNI monitors, the logic that releases // the monitors doesn't know to set the "jni" flag and so the counts can get out of sync. @@ -2017,7 +2048,7 @@ void JavaThread::dec_held_monitor_count(intx i, bool jni) { // JNI count is directly set to zero. assert(_held_monitor_count >= _jni_monitor_count || is_exiting(), "Monitor count discrepancy detected - held count " INTX_FORMAT " is less than JNI count " INTX_FORMAT, _held_monitor_count, _jni_monitor_count); -#endif +#endif // SUPPORT_MONITOR_COUNT } frame JavaThread::vthread_last_frame() { @@ -2199,6 +2230,7 @@ void JavaThread::start_internal_daemon(JavaThread* current, JavaThread* target, // Now bind the thread_oop to the target JavaThread. target->set_threadOopHandles(thread_oop()); + target->set_lock_id(java_lang_Thread::thread_id(thread_oop())); Threads::add(target); // target is now visible for safepoint/handshake // Publish the JavaThread* in java.lang.Thread after the JavaThread* is @@ -2295,3 +2327,38 @@ void JavaThread::add_oop_handles_for_release() { _oop_handle_list = new_head; Service_lock->notify_all(); } + +#if INCLUDE_JFR +void JavaThread::set_last_freeze_fail_result(freeze_result result) { + assert(result != freeze_ok, "sanity check"); + _last_freeze_fail_result = result; + _last_freeze_fail_time = Ticks::now(); +} + +// Post jdk.VirtualThreadPinned event +void JavaThread::post_vthread_pinned_event(EventVirtualThreadPinned* event, const char* op, freeze_result result) { + assert(result != freeze_ok, "sanity check"); + if (event->should_commit()) { + char reason[256]; + if (class_to_be_initialized() != nullptr) { + ResourceMark rm(this); + jio_snprintf(reason, sizeof reason, "Waited for initialization of %s by another thread", + class_to_be_initialized()->external_name()); + event->set_pinnedReason(reason); + } else if (class_being_initialized() != nullptr) { + ResourceMark rm(this); + jio_snprintf(reason, sizeof(reason), "VM call to %s. on stack", + class_being_initialized()->external_name()); + event->set_pinnedReason(reason); + } else if (result == freeze_pinned_native) { + event->set_pinnedReason("Native or VM frame on stack"); + } else { + jio_snprintf(reason, sizeof(reason), "Freeze or preempt failed (%d)", result); + event->set_pinnedReason(reason); + } + event->set_blockingOperation(op); + event->set_carrierThread(JFR_JVM_THREAD_ID(this)); + event->commit(); + } +} +#endif \ No newline at end of file diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 249a1e4dc8739..e24138583b894 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -30,6 +30,7 @@ #include "memory/allocation.hpp" #include "oops/oop.hpp" #include "oops/oopHandle.hpp" +#include "runtime/continuationEntry.hpp" #include "runtime/frame.hpp" #include "runtime/globals.hpp" #include "runtime/handshake.hpp" @@ -41,16 +42,17 @@ #include "runtime/stackOverflow.hpp" #include "runtime/thread.hpp" #include "runtime/threadHeapSampler.hpp" +#include "runtime/threadIdentifier.hpp" #include "runtime/threadStatisticalInfo.hpp" #include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #if INCLUDE_JFR #include "jfr/support/jfrThreadExtension.hpp" +#include "utilities/ticks.hpp" #endif class AsyncExceptionHandshake; -class ContinuationEntry; class DeoptResourceMark; class InternalOOMEMark; class JNIHandleBlock; @@ -77,6 +79,8 @@ class javaVFrame; class JavaThread; typedef void (*ThreadFunction)(JavaThread*, TRAPS); +class EventVirtualThreadPinned; + class JavaThread: public Thread { friend class VMStructs; friend class JVMCIVMStructs; @@ -159,7 +163,18 @@ class JavaThread: public Thread { // One-element thread local free list JNIHandleBlock* _free_handle_block; + // ID used as owner for inflated monitors. Same as the j.l.Thread.tid of the + // current _vthread object, except during creation of the primordial and JNI + // attached thread cases where this field can have a temporary value. + int64_t _lock_id; + public: + void set_lock_id(int64_t tid) { + assert(tid >= ThreadIdentifier::initial() && tid < ThreadIdentifier::current(), "invalid tid"); + _lock_id = tid; + } + int64_t lock_id() const { return _lock_id; } + // For tracking the heavyweight monitor the thread is pending on. ObjectMonitor* current_pending_monitor() { // Use Atomic::load() to prevent data race between concurrent modification and @@ -313,6 +328,8 @@ class JavaThread: public Thread { bool _is_in_VTMS_transition; // thread is in virtual thread mount state transition bool _is_disable_suspend; // JVMTI suspend is temporarily disabled; used on current thread only bool _VTMS_transition_mark; // used for sync between VTMS transitions and disablers + bool _on_monitor_waited_event; // Avoid callee arg processing for enterSpecial when posting waited event + ObjectMonitor* _contended_entered_monitor; // Monitor for pending monitor_contended_entered callback #ifdef ASSERT bool _is_VTMS_transition_disabler; // thread currently disabled VTMS transitions #endif @@ -454,8 +471,8 @@ class JavaThread: public Thread { int _frames_to_pop_failed_realloc; ContinuationEntry* _cont_entry; - intptr_t* _cont_fastpath; // the sp of the oldest known interpreted/call_stub frame inside the - // continuation that we know about + intptr_t* _cont_fastpath; // the sp of the oldest known interpreted/call_stub/upcall_stub/native_wrapper + // frame inside the continuation that we know about int _cont_fastpath_thread_state; // whether global thread state allows continuation fastpath (JVMTI) // It's signed for error detection. @@ -463,6 +480,28 @@ class JavaThread: public Thread { intx _jni_monitor_count; ObjectMonitor* _unlocked_inflated_monitor; + // This is the field we poke in the interpreter and native + // wrapper (Object.wait) to check for preemption. + address _preempt_alternate_return; + // When preempting on monitorenter we could have acquired the + // monitor after freezing all vthread frames. In that case we + // set this field so that in the preempt stub we call thaw again + // instead of unmounting. + bool _preemption_cancelled; + // For Object.wait() we set this field to know if we need to + // throw IE at the end of thawing before returning to Java. + bool _pending_interrupted_exception; + + public: + bool preemption_cancelled() { return _preemption_cancelled; } + void set_preemption_cancelled(bool b) { _preemption_cancelled = b; } + + bool pending_interrupted_exception() { return _pending_interrupted_exception; } + void set_pending_interrupted_exception(bool b) { _pending_interrupted_exception = b; } + + bool preempting() { return _preempt_alternate_return != nullptr; } + void set_preempt_alternate_return(address val) { _preempt_alternate_return = val; } + private: friend class VMThread; @@ -627,7 +666,7 @@ class JavaThread: public Thread { intx held_monitor_count() { return _held_monitor_count; } intx jni_monitor_count() { return _jni_monitor_count; } - void clear_jni_monitor_count() { _jni_monitor_count = 0; } + void clear_jni_monitor_count() { _jni_monitor_count = 0; } // Support for SharedRuntime::monitor_exit_helper() ObjectMonitor* unlocked_inflated_monitor() const { return _unlocked_inflated_monitor; } @@ -682,12 +721,19 @@ class JavaThread: public Thread { bool VTMS_transition_mark() const { return Atomic::load(&_VTMS_transition_mark); } void set_VTMS_transition_mark(bool val) { Atomic::store(&_VTMS_transition_mark, val); } + bool on_monitor_waited_event() { return _on_monitor_waited_event; } + void set_on_monitor_waited_event(bool val) { _on_monitor_waited_event = val; } + + bool pending_contended_entered_event() { return _contended_entered_monitor != nullptr; } + ObjectMonitor* contended_entered_monitor() { return _contended_entered_monitor; } #ifdef ASSERT bool is_VTMS_transition_disabler() const { return _is_VTMS_transition_disabler; } void set_is_VTMS_transition_disabler(bool val); #endif #endif + void set_contended_entered_monitor(ObjectMonitor* val) NOT_JVMTI_RETURN JVMTI_ONLY({ _contended_entered_monitor = val; }) + // Support for object deoptimization and JFR suspension void handle_special_runtime_exit_condition(); bool has_special_runtime_exit_condition() { @@ -838,10 +884,14 @@ class JavaThread: public Thread { static ByteSize doing_unsafe_access_offset() { return byte_offset_of(JavaThread, _doing_unsafe_access); } NOT_PRODUCT(static ByteSize requires_cross_modify_fence_offset() { return byte_offset_of(JavaThread, _requires_cross_modify_fence); }) + static ByteSize lock_id_offset() { return byte_offset_of(JavaThread, _lock_id); } + static ByteSize cont_entry_offset() { return byte_offset_of(JavaThread, _cont_entry); } static ByteSize cont_fastpath_offset() { return byte_offset_of(JavaThread, _cont_fastpath); } static ByteSize held_monitor_count_offset() { return byte_offset_of(JavaThread, _held_monitor_count); } static ByteSize jni_monitor_count_offset() { return byte_offset_of(JavaThread, _jni_monitor_count); } + static ByteSize preemption_cancelled_offset() { return byte_offset_of(JavaThread, _preemption_cancelled); } + static ByteSize preempt_alternate_return_offset() { return byte_offset_of(JavaThread, _preempt_alternate_return); } static ByteSize unlocked_inflated_monitor_offset() { return byte_offset_of(JavaThread, _unlocked_inflated_monitor); } #if INCLUDE_JVMTI @@ -1161,11 +1211,23 @@ class JavaThread: public Thread { void set_class_to_be_initialized(InstanceKlass* k); InstanceKlass* class_to_be_initialized() const; + // Track executing class initializer, see ThreadInClassInitializer + void set_class_being_initialized(InstanceKlass* k); + InstanceKlass* class_being_initialized() const; + private: InstanceKlass* _class_to_be_initialized; + InstanceKlass* _class_being_initialized; // java.lang.Thread.sleep support ParkEvent * _SleepEvent; + +#if INCLUDE_JFR + // Support for jdk.VirtualThreadPinned event + freeze_result _last_freeze_fail_result; + Ticks _last_freeze_fail_time; +#endif + public: bool sleep(jlong millis); bool sleep_nanos(jlong nanos); @@ -1174,6 +1236,15 @@ class JavaThread: public Thread { void interrupt(); bool is_interrupted(bool clear_interrupted); +#if INCLUDE_JFR + // Support for jdk.VirtualThreadPinned event + freeze_result last_freeze_fail_result() { return _last_freeze_fail_result; } + Ticks& last_freeze_fail_time() { return _last_freeze_fail_time; } + void set_last_freeze_fail_result(freeze_result result); +#endif + void post_vthread_pinned_event(EventVirtualThreadPinned* event, const char* op, freeze_result result) NOT_JFR_RETURN(); + + // This is only for use by JVMTI RawMonitorWait. It emulates the actions of // the Java code in Object::wait which are not present in RawMonitorWait. bool get_and_clear_interrupted(); @@ -1261,4 +1332,36 @@ class JNIHandleMark : public StackObj { ~JNIHandleMark() { _thread->pop_jni_handle_block(); } }; +class NoPreemptMark { + ContinuationEntry* _ce; + bool _unpin; + public: + NoPreemptMark(JavaThread* thread) : _ce(thread->last_continuation()), _unpin(false) { + if (_ce != nullptr) _unpin = _ce->pin(); + } + ~NoPreemptMark() { if (_unpin) _ce->unpin(); } +}; + +class ThreadOnMonitorWaitedEvent { + JavaThread* _thread; + public: + ThreadOnMonitorWaitedEvent(JavaThread* thread) : _thread(thread) { + JVMTI_ONLY(_thread->set_on_monitor_waited_event(true);) + } + ~ThreadOnMonitorWaitedEvent() { JVMTI_ONLY(_thread->set_on_monitor_waited_event(false);) } +}; + +class ThreadInClassInitializer : public StackObj { + JavaThread* _thread; + InstanceKlass* _previous; + public: + ThreadInClassInitializer(JavaThread* thread, InstanceKlass* ik) : _thread(thread) { + _previous = _thread->class_being_initialized(); + _thread->set_class_being_initialized(ik); + } + ~ThreadInClassInitializer() { + _thread->set_class_being_initialized(_previous); + } +}; + #endif // SHARE_RUNTIME_JAVATHREAD_HPP diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp index fabb589c2b5ac..f434fc161bb6c 100644 --- a/src/hotspot/share/runtime/javaThread.inline.hpp +++ b/src/hotspot/share/runtime/javaThread.inline.hpp @@ -241,10 +241,20 @@ inline InstanceKlass* JavaThread::class_to_be_initialized() const { return _class_to_be_initialized; } +inline void JavaThread::set_class_being_initialized(InstanceKlass* k) { + assert(k != nullptr || _class_being_initialized != nullptr, "incorrect usage"); + assert(this == Thread::current(), "Only the current thread can set this field"); + _class_being_initialized = k; +} + +inline InstanceKlass* JavaThread::class_being_initialized() const { + return _class_being_initialized; +} + inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) { assert(UseObjectMonitorTable, "must be"); assert(monitor != nullptr, "use om_clear_monitor_cache to clear"); - assert(this == current() || monitor->owner_raw() == this, "only add owned monitors for other threads"); + assert(this == current() || monitor->has_owner(this), "only add owned monitors for other threads"); assert(this == current() || is_obj_deopt_suspend(), "thread must not run concurrently"); _om_cache.set_monitor(monitor); diff --git a/src/hotspot/share/runtime/lightweightSynchronizer.cpp b/src/hotspot/share/runtime/lightweightSynchronizer.cpp index 7cf7c201faf74..b3bcd5f702905 100644 --- a/src/hotspot/share/runtime/lightweightSynchronizer.cpp +++ b/src/hotspot/share/runtime/lightweightSynchronizer.cpp @@ -344,7 +344,7 @@ ObjectMonitor* LightweightSynchronizer::get_or_insert_monitor_from_table(oop obj } ObjectMonitor* alloced_monitor = new ObjectMonitor(object); - alloced_monitor->set_owner_anonymous(); + alloced_monitor->set_anonymous_owner(); // Try insert monitor monitor = add_monitor(current, alloced_monitor, object); @@ -623,8 +623,6 @@ void LightweightSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* ObjectSynchronizer::handle_sync_on_value_based_class(obj, locking_thread); } - locking_thread->inc_held_monitor_count(); - CacheSetter cache_setter(locking_thread, lock); LockStack& lock_stack = locking_thread->lock_stack(); @@ -654,8 +652,6 @@ void LightweightSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* cur ObjectSynchronizer::handle_sync_on_value_based_class(obj, current); } - current->inc_held_monitor_count(); - CacheSetter cache_setter(current, lock); // Used when deflation is observed. Progress here requires progress @@ -745,7 +741,7 @@ void LightweightSynchronizer::exit(oop object, JavaThread* current) { assert(mark.has_monitor(), "must be"); // The monitor exists ObjectMonitor* monitor = ObjectSynchronizer::read_monitor(current, object, mark); - if (monitor->is_owner_anonymous()) { + if (monitor->has_anonymous_owner()) { assert(current->lock_stack().contains(object), "current must have object on its lock stack"); monitor->set_owner_from_anonymous(current); monitor->set_recursions(current->lock_stack().remove(object) - 1); @@ -790,7 +786,7 @@ ObjectMonitor* LightweightSynchronizer::inflate_locked_or_imse(oop obj, ObjectSy assert(mark.has_monitor(), "must be"); ObjectMonitor* monitor = ObjectSynchronizer::read_monitor(current, obj, mark); if (monitor != nullptr) { - if (monitor->is_owner_anonymous()) { + if (monitor->has_anonymous_owner()) { LockStack& lock_stack = current->lock_stack(); if (lock_stack.contains(obj)) { // Current thread owns the lock but someone else inflated it. @@ -808,10 +804,10 @@ ObjectMonitor* LightweightSynchronizer::inflate_locked_or_imse(oop obj, ObjectSy } } -ObjectMonitor* LightweightSynchronizer::inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* inflating_thread, Thread* current) { +ObjectMonitor* LightweightSynchronizer::inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, Thread* current) { - // The JavaThread* inflating_thread parameter is only used by LM_LIGHTWEIGHT and requires - // that the inflating_thread == Thread::current() or is suspended throughout the call by + // The JavaThread* locking_thread parameter is only used by LM_LIGHTWEIGHT and requires + // that the locking_thread == Thread::current() or is suspended throughout the call by // some other mechanism. // Even with LM_LIGHTWEIGHT the thread might be nullptr when called from a non // JavaThread. (As may still be the case from FastHashCode). However it is only @@ -826,10 +822,10 @@ ObjectMonitor* LightweightSynchronizer::inflate_into_object_header(oop object, O // The mark can be in one of the following states: // * inflated - Just return if using stack-locking. // If using fast-locking and the ObjectMonitor owner - // is anonymous and the inflating_thread owns the - // object lock, then we make the inflating_thread + // is anonymous and the locking_thread owns the + // object lock, then we make the locking_thread // the ObjectMonitor owner and remove the lock from - // the inflating_thread's lock stack. + // the locking_thread's lock stack. // * fast-locked - Coerce it to inflated from fast-locked. // * unlocked - Aggressively inflate the object. @@ -838,42 +834,42 @@ ObjectMonitor* LightweightSynchronizer::inflate_into_object_header(oop object, O ObjectMonitor* inf = mark.monitor(); markWord dmw = inf->header(); assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value()); - if (inf->is_owner_anonymous() && - inflating_thread != nullptr && inflating_thread->lock_stack().contains(object)) { - inf->set_owner_from_anonymous(inflating_thread); - size_t removed = inflating_thread->lock_stack().remove(object); + if (inf->has_anonymous_owner() && + locking_thread != nullptr && locking_thread->lock_stack().contains(object)) { + inf->set_owner_from_anonymous(locking_thread); + size_t removed = locking_thread->lock_stack().remove(object); inf->set_recursions(removed - 1); } return inf; } // CASE: fast-locked - // Could be fast-locked either by the inflating_thread or by some other thread. + // Could be fast-locked either by the locking_thread or by some other thread. // // Note that we allocate the ObjectMonitor speculatively, _before_ // attempting to set the object's mark to the new ObjectMonitor. If - // the inflating_thread owns the monitor, then we set the ObjectMonitor's - // owner to the inflating_thread. Otherwise, we set the ObjectMonitor's owner + // the locking_thread owns the monitor, then we set the ObjectMonitor's + // owner to the locking_thread. Otherwise, we set the ObjectMonitor's owner // to anonymous. If we lose the race to set the object's mark to the // new ObjectMonitor, then we just delete it and loop around again. // if (mark.is_fast_locked()) { ObjectMonitor* monitor = new ObjectMonitor(object); monitor->set_header(mark.set_unlocked()); - bool own = inflating_thread != nullptr && inflating_thread->lock_stack().contains(object); + bool own = locking_thread != nullptr && locking_thread->lock_stack().contains(object); if (own) { - // Owned by inflating_thread. - monitor->set_owner_from(nullptr, inflating_thread); + // Owned by locking_thread. + monitor->set_owner(locking_thread); } else { // Owned by somebody else. - monitor->set_owner_anonymous(); + monitor->set_anonymous_owner(); } markWord monitor_mark = markWord::encode(monitor); markWord old_mark = object->cas_set_mark(monitor_mark, mark); if (old_mark == mark) { // Success! Return inflated monitor. if (own) { - size_t removed = inflating_thread->lock_stack().remove(object); + size_t removed = locking_thread->lock_stack().remove(object); monitor->set_recursions(removed - 1); } // Once the ObjectMonitor is configured and object is associated @@ -956,7 +952,7 @@ ObjectMonitor* LightweightSynchronizer::inflate_fast_locked_object(oop object, O // ObjectMonitors are always inserted as anonymously owned, this thread is // the current holder of the monitor. So unless the entry is stale and // contains a deflating monitor it must be anonymously owned. - if (monitor->is_owner_anonymous()) { + if (monitor->has_anonymous_owner()) { // The monitor must be anonymously owned if it was added assert(monitor == get_monitor_from_table(current, object), "The monitor must be found"); // New fresh monitor @@ -1077,7 +1073,7 @@ ObjectMonitor* LightweightSynchronizer::inflate_and_enter(oop object, ObjectSync // CASE: inflated if (mark.has_monitor()) { LockStack& lock_stack = locking_thread->lock_stack(); - if (monitor->is_owner_anonymous() && lock_stack.contains(object)) { + if (monitor->has_anonymous_owner() && lock_stack.contains(object)) { // The lock is fast-locked by the locking thread, // convert it to a held monitor with a known owner. monitor->set_owner_from_anonymous(locking_thread); @@ -1189,7 +1185,6 @@ bool LightweightSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread* // Only for 32bit which has limited support for fast locking outside the runtime. if (lock_stack.try_recursive_enter(obj)) { // Recursive lock successful. - current->inc_held_monitor_count(); return true; } @@ -1198,7 +1193,6 @@ bool LightweightSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread* if (obj->cas_set_mark(locked_mark, mark) == mark) { // Successfully fast-locked, push object to lock-stack and return. lock_stack.push(obj); - current->inc_held_monitor_count(); return true; } } @@ -1216,7 +1210,6 @@ bool LightweightSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread* if (monitor->try_enter(current)) { // ObjectMonitor enter successful. cache_setter.set_monitor(monitor); - current->inc_held_monitor_count(); return true; } } diff --git a/src/hotspot/share/runtime/lightweightSynchronizer.hpp b/src/hotspot/share/runtime/lightweightSynchronizer.hpp index c546988f7782e..fdd753e9b9c3f 100644 --- a/src/hotspot/share/runtime/lightweightSynchronizer.hpp +++ b/src/hotspot/share/runtime/lightweightSynchronizer.hpp @@ -63,7 +63,7 @@ class LightweightSynchronizer : AllStatic { static void enter(Handle obj, BasicLock* lock, JavaThread* current); static void exit(oop object, JavaThread* current); - static ObjectMonitor* inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* inflating_thread, Thread* current); + static ObjectMonitor* inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, Thread* current); static ObjectMonitor* inflate_locked_or_imse(oop object, ObjectSynchronizer::InflateCause cause, TRAPS); static ObjectMonitor* inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current); static ObjectMonitor* inflate_and_enter(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current); diff --git a/src/hotspot/share/runtime/lockStack.hpp b/src/hotspot/share/runtime/lockStack.hpp index de32eb4125975..dbc958a71e2b1 100644 --- a/src/hotspot/share/runtime/lockStack.hpp +++ b/src/hotspot/share/runtime/lockStack.hpp @@ -118,6 +118,10 @@ class LockStack { // Tests whether the oop is on this lock-stack. inline bool contains(oop o) const; + inline int monitor_count() const; + inline void move_to_address(oop* start); + inline void move_from_address(oop* start, int count); + // GC support inline void oops_do(OopClosure* cl); diff --git a/src/hotspot/share/runtime/lockStack.inline.hpp b/src/hotspot/share/runtime/lockStack.inline.hpp index 6c25a2a3a9871..0516a85356d1f 100644 --- a/src/hotspot/share/runtime/lockStack.inline.hpp +++ b/src/hotspot/share/runtime/lockStack.inline.hpp @@ -215,6 +215,29 @@ inline bool LockStack::contains(oop o) const { return false; } +inline int LockStack::monitor_count() const { + int end = to_index(_top); + assert(end <= CAPACITY, "invariant"); + return end; +} + +inline void LockStack::move_to_address(oop* start) { + int end = to_index(_top); + for (int i = 0; i < end; i++) { + start[i] = _base[i]; + DEBUG_ONLY(_base[i] = nullptr;) + } + _top = lock_stack_base_offset; +} + +inline void LockStack::move_from_address(oop* start, int count) { + assert(to_index(_top) == 0, "lockstack should be empty"); + for (int i = 0; i < count; i++) { + _base[i] = start[i]; + _top += oopSize; + } +} + inline void LockStack::oops_do(OopClosure* cl) { // We don't perform pre oops_do verify here because this function // is used by the GC to fix the oops. diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 3ea987801db6a..7d1998ca849c0 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -39,6 +39,7 @@ #include "prims/jvmtiDeferredUpdates.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" +#include "runtime/continuationWrapper.inline.hpp" #include "runtime/globals.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" @@ -53,6 +54,7 @@ #include "runtime/safefetch.hpp" #include "runtime/safepointMechanism.inline.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/threads.hpp" #include "services/threadService.hpp" #include "utilities/debug.hpp" #include "utilities/dtrace.hpp" @@ -114,27 +116,41 @@ DEBUG_ONLY(static volatile bool InitDone = false;) OopStorage* ObjectMonitor::_oop_storage = nullptr; +OopHandle ObjectMonitor::_vthread_cxq_head; +ParkEvent* ObjectMonitor::_vthread_unparker_ParkEvent = nullptr; + // ----------------------------------------------------------------------------- // Theory of operations -- Monitors lists, thread residency, etc: // // * A thread acquires ownership of a monitor by successfully -// CAS()ing the _owner field from null to non-null. +// CAS()ing the _owner field from NO_OWNER/DEFLATER_MARKER to +// its tid (return value from owner_from()). // // * Invariant: A thread appears on at most one monitor list -- // cxq, EntryList or WaitSet -- at any one time. // // * Contending threads "push" themselves onto the cxq with CAS // and then spin/park. +// If the thread is a virtual thread it will first attempt to +// unmount itself. The virtual thread will first try to freeze +// all frames in the heap. If the operation fails it will just +// follow the regular path for platform threads. If the operation +// succeeds, it will push itself onto the cxq with CAS and then +// return back to Java to continue the unmount logic. // // * After a contending thread eventually acquires the lock it must // dequeue itself from either the EntryList or the cxq. // // * The exiting thread identifies and unparks an "heir presumptive" -// tentative successor thread on the EntryList. Critically, the -// exiting thread doesn't unlink the successor thread from the EntryList. -// After having been unparked, the wakee will recontend for ownership of -// the monitor. The successor (wakee) will either acquire the lock or -// re-park itself. +// tentative successor thread on the EntryList. In case the successor +// is an unmounted virtual thread, the exiting thread will first try +// to add it to the list of vthreads waiting to be unblocked, and on +// success it will unpark the special unblocker thread instead, which +// will be in charge of submitting the vthread back to the scheduler +// queue. Critically, the exiting thread doesn't unlink the successor +// thread from the EntryList. After having been unparked/re-scheduled, +// the wakee will recontend for ownership of the monitor. The successor +// (wakee) will either acquire the lock or re-park/unmount itself. // // Succession is provided for by a policy of competitive handoff. // The exiting thread does _not_ grant or pass ownership to the @@ -196,20 +212,18 @@ OopStorage* ObjectMonitor::_oop_storage = nullptr; // // * The monitor synchronization subsystem avoids the use of native // synchronization primitives except for the narrow platform-specific -// park-unpark abstraction. See the comments in os_solaris.cpp regarding +// park-unpark abstraction. See the comments in os_posix.cpp regarding // the semantics of park-unpark. Put another way, this monitor implementation -// depends only on atomic operations and park-unpark. The monitor subsystem -// manages all RUNNING->BLOCKED and BLOCKED->READY transitions while the -// underlying OS manages the READY<->RUN transitions. +// depends only on atomic operations and park-unpark. // // * Waiting threads reside on the WaitSet list -- wait() puts // the caller onto the WaitSet. // // * notify() or notifyAll() simply transfers threads from the WaitSet to // either the EntryList or cxq. Subsequent exit() operations will -// unpark the notifyee. Unparking a notifee in notify() is inefficient - -// it's likely the notifyee would simply impale itself on the lock held -// by the notifier. +// unpark/re-schedule the notifyee. Unparking/re-scheduling a notifyee in +// notify() is inefficient - it's likely the notifyee would simply impale +// itself on the lock held by the notifier. // Check that object() and set_object() are called from the right context: static void check_object_context() { @@ -237,18 +251,19 @@ static void check_object_context() { ObjectMonitor::ObjectMonitor(oop object) : _metadata(0), _object(_oop_storage, object), - _owner(nullptr), + _owner(NO_OWNER), _previous_owner_tid(0), _next_om(nullptr), _recursions(0), _EntryList(nullptr), _cxq(nullptr), - _succ(nullptr), + _succ(NO_OWNER), _SpinDuration(ObjectMonitor::Knob_SpinLimit), _contentions(0), _WaitSet(nullptr), _waiters(0), - _WaitSetLock(0) + _WaitSetLock(0), + _stack_locker(nullptr) { } ObjectMonitor::~ObjectMonitor() { @@ -263,7 +278,7 @@ oop ObjectMonitor::object() const { void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) { if (current->is_suspended()) { _om->_recursions = 0; - _om->_succ = nullptr; + _om->clear_successor(); // Don't need a full fence after clearing successor here because of the call to exit(). _om->exit(current, false /* not_suspended */); _om_exited = true; @@ -274,8 +289,8 @@ void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) { void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) { if (current->is_suspended()) { - if (_om->_succ == current) { - _om->_succ = nullptr; + if (_om->has_successor(current)) { + _om->clear_successor(); OrderAccess::fence(); // always do a full fence when successor is cleared } } @@ -310,13 +325,13 @@ bool ObjectMonitor::TryLockWithContentionMark(JavaThread* locking_thread, Object assert(contention_mark._monitor == this, "must be"); assert(!is_being_async_deflated(), "must be"); - void* prev_owner = try_set_owner_from(nullptr, locking_thread); + int64_t prev_owner = try_set_owner_from(NO_OWNER, locking_thread); bool success = false; - if (prev_owner == nullptr) { + if (prev_owner == NO_OWNER) { assert(_recursions == 0, "invariant"); success = true; - } else if (prev_owner == locking_thread) { + } else if (prev_owner == owner_from(locking_thread)) { _recursions++; success = true; } else if (prev_owner == DEFLATER_MARKER) { @@ -334,21 +349,16 @@ bool ObjectMonitor::TryLockWithContentionMark(JavaThread* locking_thread, Object // recognizes that the async deflation was cancelled. contention_mark.extend(); success = true; - } else if (prev_owner == nullptr) { + } else if (prev_owner == NO_OWNER) { // At this point we cannot race with deflation as we have both incremented // contentions, seen contention > 0 and seen a DEFLATER_MARKER. // success will only be false if this races with something other than // deflation. - prev_owner = try_set_owner_from(nullptr, locking_thread); - success = prev_owner == nullptr; + prev_owner = try_set_owner_from(NO_OWNER, locking_thread); + success = prev_owner == NO_OWNER; } - } else if (LockingMode == LM_LEGACY && locking_thread->is_lock_owned((address)prev_owner)) { - assert(_recursions == 0, "must be"); - _recursions = 1; - set_owner_from_BasicLock(prev_owner, locking_thread); - success = true; } - assert(!success || owner_raw() == locking_thread, "must be"); + assert(!success || has_owner(locking_thread), "must be"); return success; } @@ -361,8 +371,8 @@ void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, O bool success = TryLockWithContentionMark(locking_thread, contention_mark); assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT - ", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}", - p2i(locking_thread), p2i(this), p2i(owner_raw())); + ", this=" INTPTR_FORMAT "{owner=" INT64_FORMAT "}", + p2i(locking_thread), p2i(this), owner_raw()); } bool ObjectMonitor::enter_for(JavaThread* locking_thread) { @@ -382,9 +392,9 @@ bool ObjectMonitor::enter_for(JavaThread* locking_thread) { bool success = TryLockWithContentionMark(locking_thread, contention_mark); assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT - ", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}", - p2i(locking_thread), p2i(this), p2i(owner_raw())); - assert(owner_raw() == locking_thread, "must be"); + ", this=" INTPTR_FORMAT "{owner=" INT64_FORMAT "}", + p2i(locking_thread), p2i(this), owner_raw()); + assert(has_owner(locking_thread), "must be"); return true; } @@ -402,18 +412,11 @@ bool ObjectMonitor::try_enter(JavaThread* current, bool check_for_recursion) { return false; } - if (r == TryLockResult::HasOwner && owner() == current) { + if (r == TryLockResult::HasOwner && has_owner(current)) { _recursions++; return true; } - void* cur = owner_raw(); - if (LockingMode == LM_LEGACY && current->is_lock_owned((address)cur)) { - assert(_recursions == 0, "internal state error"); - _recursions = 1; - set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*. - return true; - } return false; } @@ -436,7 +439,7 @@ bool ObjectMonitor::spin_enter(JavaThread* current) { // Note that if we acquire the monitor from an initial spin // we forgo posting JVMTI events and firing DTRACE probes. if (TrySpin(current)) { - assert(owner_raw() == current, "must be current: owner=" INTPTR_FORMAT, p2i(owner_raw())); + assert(has_owner(current), "must be current: owner=" INT64_FORMAT, owner_raw()); assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions); assert_mark_word_consistency(); return true; @@ -452,8 +455,8 @@ bool ObjectMonitor::enter(JavaThread* current) { return true; } - assert(owner_raw() != current, "invariant"); - assert(_succ != current, "invariant"); + assert(!has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); assert(current->thread_state() != _thread_blocked, "invariant"); @@ -472,19 +475,22 @@ bool ObjectMonitor::enter(JavaThread* current) { void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonitorContentionMark &cm) { assert(current == JavaThread::current(), "must be"); - assert(owner_raw() != current, "must be"); + assert(!has_owner(current), "must be"); assert(cm._monitor == this, "must be"); assert(!is_being_async_deflated(), "must be"); JFR_ONLY(JfrConditionalFlush flush(current);) - EventJavaMonitorEnter event; - if (event.is_started()) { - event.set_monitorClass(object()->klass()); + EventJavaMonitorEnter enter_event; + if (enter_event.is_started()) { + enter_event.set_monitorClass(object()->klass()); // Set an address that is 'unique enough', such that events close in // time and with the same address are likely (but not guaranteed) to // belong to the same object. - event.set_address((uintptr_t)this); + enter_event.set_address((uintptr_t)this); } + EventVirtualThreadPinned vthread_pinned_event; + + freeze_result result; { // Change java thread status to indicate blocked on monitor enter. JavaThreadBlockedOnMonitorEnterState jtbmes(current, this); @@ -503,6 +509,30 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito // ParkEvent associated with this ObjectMonitor. } + ContinuationEntry* ce = current->last_continuation(); + if (ce != nullptr && ce->is_virtual_thread()) { + result = Continuation::try_preempt(current, ce->cont_oop(current)); + if (result == freeze_ok) { + bool acquired = VThreadMonitorEnter(current); + if (acquired) { + // We actually acquired the monitor while trying to add the vthread to the + // _cxq so cancel preemption. We will still go through the preempt stub + // but instead of unmounting we will call thaw to continue execution. + current->set_preemption_cancelled(true); + if (JvmtiExport::should_post_monitor_contended_entered()) { + // We are going to call thaw again after this and finish the VMTS + // transition so no need to do it here. We will post the event there. + current->set_contended_entered_monitor(this); + } + } + current->set_current_pending_monitor(nullptr); + DEBUG_ONLY(int state = java_lang_VirtualThread::state(current->vthread())); + assert((acquired && current->preemption_cancelled() && state == java_lang_VirtualThread::RUNNING) || + (!acquired && !current->preemption_cancelled() && state == java_lang_VirtualThread::BLOCKING), "invariant"); + return; + } + } + OSThreadContendState osts(current->osthread()); assert(current->thread_state() == _thread_in_vm, "invariant"); @@ -523,7 +553,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito } if (!eos.exited()) { // ExitOnSuspend did not exit the OM - assert(owner_raw() == current, "invariant"); + assert(has_owner(current), "invariant"); break; } } @@ -536,8 +566,8 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito // Must either set _recursions = 0 or ASSERT _recursions == 0. assert(_recursions == 0, "invariant"); - assert(owner_raw() == current, "invariant"); - assert(_succ != current, "invariant"); + assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); assert_mark_word_consistency(); // The thread -- now the owner -- is back in vm mode. @@ -562,10 +592,17 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito // event handler consumed an unpark() issued by the thread that // just exited the monitor. } - if (event.should_commit()) { - event.set_previousOwner(_previous_owner_tid); - event.commit(); + if (enter_event.should_commit()) { + enter_event.set_previousOwner(_previous_owner_tid); + enter_event.commit(); } + + ContinuationEntry* ce = current->last_continuation(); + if (ce != nullptr && ce->is_virtual_thread()) { + assert(result != freeze_ok, "sanity check"); + current->post_vthread_pinned_event(&vthread_pinned_event, "Contended monitor enter", result); + } + OM_PERFDATA_OP(ContendedLockAttempts, inc()); } @@ -573,8 +610,8 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito // Callers must compensate as needed. ObjectMonitor::TryLockResult ObjectMonitor::TryLock(JavaThread* current) { - void* own = owner_raw(); - void* first_own = own; + int64_t own = owner_raw(); + int64_t first_own = own; for (;;) { if (own == DEFLATER_MARKER) { @@ -593,9 +630,9 @@ ObjectMonitor::TryLockResult ObjectMonitor::TryLock(JavaThread* current) { // Deflation won or change of owner; dont spin break; } - } else if (own == nullptr) { - void* prev_own = try_set_owner_from(nullptr, current); - if (prev_own == nullptr) { + } else if (own == NO_OWNER) { + int64_t prev_own = try_set_owner_from(NO_OWNER, current); + if (prev_own == NO_OWNER) { assert(_recursions == 0, "invariant"); return TryLockResult::Success; } else { @@ -634,7 +671,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) { if (obj == nullptr) { // If the object died, we can recycle the monitor without racing with // Java threads. The GC already broke the association with the object. - set_owner_from(nullptr, DEFLATER_MARKER); + set_owner_from_raw(NO_OWNER, DEFLATER_MARKER); assert(contentions() >= 0, "must be non-negative: contentions=%d", contentions()); _contentions = INT_MIN; // minimum negative int } else { @@ -643,7 +680,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) { // Set a null owner to DEFLATER_MARKER to force any contending thread // through the slow path. This is just the first part of the async // deflation dance. - if (try_set_owner_from(nullptr, DEFLATER_MARKER) != nullptr) { + if (try_set_owner_from_raw(NO_OWNER, DEFLATER_MARKER) != NO_OWNER) { // The owner field is no longer null so we lost the race since the // ObjectMonitor is now busy. return false; @@ -654,7 +691,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) { // is_busy() above or has already entered and waited on // it which makes it busy so no deflation. Restore owner to // null if it is still DEFLATER_MARKER. - if (try_set_owner_from(DEFLATER_MARKER, nullptr) != DEFLATER_MARKER) { + if (try_set_owner_from_raw(DEFLATER_MARKER, NO_OWNER) != DEFLATER_MARKER) { // Deferred decrement for the JT EnterI() that cancelled the async deflation. add_to_contentions(-1); } @@ -667,7 +704,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) { // Contentions was no longer 0 so we lost the race since the // ObjectMonitor is now busy. Restore owner to null if it is // still DEFLATER_MARKER: - if (try_set_owner_from(DEFLATER_MARKER, nullptr) != DEFLATER_MARKER) { + if (try_set_owner_from_raw(DEFLATER_MARKER, NO_OWNER) != DEFLATER_MARKER) { // Deferred decrement for the JT EnterI() that cancelled the async deflation. add_to_contentions(-1); } @@ -768,7 +805,7 @@ void ObjectMonitor::install_displaced_markword_in_object(const oop obj) { const char* ObjectMonitor::is_busy_to_string(stringStream* ss) { ss->print("is_busy: waiters=%d" ", contentions=%d" - ", owner=" PTR_FORMAT + ", owner=" INT64_FORMAT ", cxq=" PTR_FORMAT ", EntryList=" PTR_FORMAT, _waiters, @@ -776,8 +813,8 @@ const char* ObjectMonitor::is_busy_to_string(stringStream* ss) { owner_is_DEFLATER_MARKER() // We report null instead of DEFLATER_MARKER here because is_busy() // ignores DEFLATER_MARKER values. - ? p2i(nullptr) - : p2i(owner_raw()), + ? NO_OWNER + : owner_raw(), p2i(_cxq), p2i(_EntryList)); return ss->base(); @@ -788,8 +825,8 @@ void ObjectMonitor::EnterI(JavaThread* current) { // Try the lock - TATAS if (TryLock(current) == TryLockResult::Success) { - assert(_succ != current, "invariant"); - assert(owner_raw() == current, "invariant"); + assert(!has_successor(current), "invariant"); + assert(has_owner(current), "invariant"); return; } @@ -803,14 +840,14 @@ void ObjectMonitor::EnterI(JavaThread* current) { // effects. if (TrySpin(current)) { - assert(owner_raw() == current, "invariant"); - assert(_succ != current, "invariant"); + assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); return; } // The Spin failed -- Enqueue and park the thread ... - assert(_succ != current, "invariant"); - assert(owner_raw() != current, "invariant"); + assert(!has_successor(current), "invariant"); + assert(!has_owner(current), "invariant"); // Enqueue "current" on ObjectMonitor's _cxq. // @@ -838,8 +875,8 @@ void ObjectMonitor::EnterI(JavaThread* current) { // Interference - the CAS failed because _cxq changed. Just retry. // As an optional optimization we retry the lock. if (TryLock(current) == TryLockResult::Success) { - assert(_succ != current, "invariant"); - assert(owner_raw() == current, "invariant"); + assert(!has_successor(current), "invariant"); + assert(has_owner(current), "invariant"); return; } } @@ -855,15 +892,38 @@ void ObjectMonitor::EnterI(JavaThread* current) { // to defer the state transitions until absolutely necessary, // and in doing so avoid some transitions ... + // For virtual threads that are pinned, do a timed-park instead to + // alleviate some deadlocks cases where the succesor is an unmounted + // virtual thread that cannot run. This can happen in particular when + // this virtual thread is currently loading/initializing a class, and + // all other carriers have a vthread pinned to it waiting for said class + // to be loaded/initialized. + static int MAX_RECHECK_INTERVAL = 1000; + int recheck_interval = 1; + bool do_timed_parked = false; + ContinuationEntry* ce = current->last_continuation(); + if (ce != nullptr && ce->is_virtual_thread()) { + do_timed_parked = true; + } + for (;;) { if (TryLock(current) == TryLockResult::Success) { break; } - assert(owner_raw() != current, "invariant"); + assert(!has_owner(current), "invariant"); // park self - current->_ParkEvent->park(); + if (do_timed_parked) { + current->_ParkEvent->park((jlong) recheck_interval); + // Increase the recheck_interval, but clamp the value. + recheck_interval *= 8; + if (recheck_interval > MAX_RECHECK_INTERVAL) { + recheck_interval = MAX_RECHECK_INTERVAL; + } + } else { + current->_ParkEvent->park(); + } if (TryLock(current) == TryLockResult::Success) { break; @@ -893,7 +953,7 @@ void ObjectMonitor::EnterI(JavaThread* current) { // just spin again. This pattern can repeat, leaving _succ to simply // spin on a CPU. - if (_succ == current) _succ = nullptr; + if (has_successor(current)) clear_successor(); // Invariant: after clearing _succ a thread *must* retry _owner before parking. OrderAccess::fence(); @@ -907,11 +967,11 @@ void ObjectMonitor::EnterI(JavaThread* current) { // The head of cxq is volatile but the interior is stable. // In addition, current.TState is stable. - assert(owner_raw() == current, "invariant"); + assert(has_owner(current), "invariant"); UnlinkAfterAcquire(current, &node); - if (_succ == current) { - _succ = nullptr; + if (has_successor(current)) { + clear_successor(); // Note that we don't need to do OrderAccess::fence() after clearing // _succ here, since we own the lock. } @@ -958,7 +1018,7 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) { for (;;) { ObjectWaiter::TStates v = currentNode->TState; guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant"); - assert(owner_raw() != current, "invariant"); + assert(!has_owner(current), "invariant"); // This thread has been notified so try to reacquire the lock. if (TryLock(current) == TryLockResult::Success) { @@ -994,7 +1054,7 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) { // Assuming this is not a spurious wakeup we'll normally // find that _succ == current. - if (_succ == current) _succ = nullptr; + if (has_successor(current)) clear_successor(); // Invariant: after clearing _succ a contending thread // *must* retry _owner before parking. @@ -1017,22 +1077,152 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) { // The head of cxq is volatile but the interior is stable. // In addition, current.TState is stable. - assert(owner_raw() == current, "invariant"); + assert(has_owner(current), "invariant"); assert_mark_word_consistency(); UnlinkAfterAcquire(current, currentNode); - if (_succ == current) _succ = nullptr; - assert(_succ != current, "invariant"); + if (has_successor(current)) clear_successor(); + assert(!has_successor(current), "invariant"); currentNode->TState = ObjectWaiter::TS_RUN; OrderAccess::fence(); // see comments at the end of EnterI() } +// This method is called from two places: +// - On monitorenter contention with a null waiter. +// - After Object.wait() times out or the target is interrupted to reenter the +// monitor, with the existing waiter. +// For the Object.wait() case we do not delete the ObjectWaiter in case we +// succesfully acquire the monitor since we are going to need it on return. +bool ObjectMonitor::VThreadMonitorEnter(JavaThread* current, ObjectWaiter* waiter) { + if (TryLock(current) == TryLockResult::Success) { + assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); + return true; + } + + oop vthread = current->vthread(); + ObjectWaiter* node = waiter != nullptr ? waiter : new ObjectWaiter(vthread, this); + node->_prev = (ObjectWaiter*) 0xBAD; + node->TState = ObjectWaiter::TS_CXQ; + + // Push node associated with vthread onto the front of the _cxq. + ObjectWaiter* nxt; + for (;;) { + node->_next = nxt = _cxq; + if (Atomic::cmpxchg(&_cxq, nxt, node) == nxt) break; + + // Interference - the CAS failed because _cxq changed. Just retry. + // As an optional optimization we retry the lock. + if (TryLock(current) == TryLockResult::Success) { + assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); + if (waiter == nullptr) delete node; // for Object.wait() don't delete yet + return true; + } + } + + // We have to try once more since owner could have exited monitor and checked + // _cxq before we added the node to the queue. + if (TryLock(current) == TryLockResult::Success) { + assert(has_owner(current), "invariant"); + UnlinkAfterAcquire(current, node); + if (has_successor(current)) clear_successor(); + if (waiter == nullptr) delete node; // for Object.wait() don't delete yet + return true; + } + + assert(java_lang_VirtualThread::state(vthread) == java_lang_VirtualThread::RUNNING, "wrong state for vthread"); + java_lang_VirtualThread::set_state(vthread, java_lang_VirtualThread::BLOCKING); + + // We didn't succeed in acquiring the monitor so increment _contentions and + // save ObjectWaiter* in the chunk since we will need it when resuming execution. + add_to_contentions(1); + oop cont = java_lang_VirtualThread::continuation(vthread); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(cont); + chunk->set_object_waiter(node); + return false; +} + +// Called from thaw code to resume the monitor operation that caused the vthread +// to be unmounted. Method returns true if the monitor is successfully acquired, +// which marks the end of the monitor operation, otherwise it returns false. +bool ObjectMonitor::resume_operation(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont) { + assert(java_lang_VirtualThread::state(current->vthread()) == java_lang_VirtualThread::RUNNING, "wrong state for vthread"); + assert(!has_owner(current), ""); + + if (node->is_wait() && !node->at_reenter()) { + bool acquired_monitor = VThreadWaitReenter(current, node, cont); + if (acquired_monitor) return true; + } + + // Retry acquiring monitor... + + int state = node->TState; + guarantee(state == ObjectWaiter::TS_ENTER || state == ObjectWaiter::TS_CXQ, "invariant"); + + if (TryLock(current) == TryLockResult::Success) { + VThreadEpilog(current, node); + return true; + } + + oop vthread = current->vthread(); + if (has_successor(current)) clear_successor(); + + // Invariant: after clearing _succ a thread *must* retry acquiring the monitor. + OrderAccess::fence(); + + if (TryLock(current) == TryLockResult::Success) { + VThreadEpilog(current, node); + return true; + } + + // We will return to Continuation.run() and unmount so set the right state. + java_lang_VirtualThread::set_state(vthread, java_lang_VirtualThread::BLOCKING); + + return false; +} + +void ObjectMonitor::VThreadEpilog(JavaThread* current, ObjectWaiter* node) { + assert(has_owner(current), "invariant"); + add_to_contentions(-1); + + if (has_successor(current)) clear_successor(); + + guarantee(_recursions == 0, "invariant"); + + if (node->is_wait()) { + _recursions = node->_recursions; // restore the old recursion count + _waiters--; // decrement the number of waiters + + if (node->_interrupted) { + // We will throw at thaw end after finishing the mount transition. + current->set_pending_interrupted_exception(true); + } + } + + UnlinkAfterAcquire(current, node); + delete node; + + // Remove the ObjectWaiter* from the stackChunk. + oop vthread = current->vthread(); + oop cont = java_lang_VirtualThread::continuation(vthread); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(cont); + chunk->set_object_waiter(nullptr); + + if (JvmtiExport::should_post_monitor_contended_entered()) { + // We are going to call thaw again after this and finish the VMTS + // transition so no need to do it here. We will post the event there. + current->set_contended_entered_monitor(this); + } +} + // By convention we unlink a contending thread from EntryList|cxq immediately // after the thread acquires the lock in ::enter(). Equally, we could defer // unlinking the thread until ::exit()-time. void ObjectMonitor::UnlinkAfterAcquire(JavaThread* current, ObjectWaiter* currentNode) { - assert(owner_raw() == current, "invariant"); - assert(currentNode->_thread == current, "invariant"); + assert(has_owner(current), "invariant"); + assert((!currentNode->is_vthread() && currentNode->thread() == current) || + (currentNode->is_vthread() && currentNode->vthread() == current->vthread()), "invariant"); if (currentNode->TState == ObjectWaiter::TS_ENTER) { // Normal case: remove current from the DLL EntryList . @@ -1151,32 +1341,25 @@ void ObjectMonitor::UnlinkAfterAcquire(JavaThread* current, ObjectWaiter* curren // of such futile wakups is low. void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { - void* cur = owner_raw(); - if (current != cur) { - if (LockingMode != LM_LIGHTWEIGHT && current->is_lock_owned((address)cur)) { - assert(_recursions == 0, "invariant"); - set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*. - _recursions = 0; - } else { - // Apparent unbalanced locking ... - // Naively we'd like to throw IllegalMonitorStateException. - // As a practical matter we can neither allocate nor throw an - // exception as ::exit() can be called from leaf routines. - // see x86_32.ad Fast_Unlock() and the I1 and I2 properties. - // Upon deeper reflection, however, in a properly run JVM the only - // way we should encounter this situation is in the presence of - // unbalanced JNI locking. TODO: CheckJNICalls. - // See also: CR4414101 + if (!has_owner(current)) { + // Apparent unbalanced locking ... + // Naively we'd like to throw IllegalMonitorStateException. + // As a practical matter we can neither allocate nor throw an + // exception as ::exit() can be called from leaf routines. + // see x86_32.ad Fast_Unlock() and the I1 and I2 properties. + // Upon deeper reflection, however, in a properly run JVM the only + // way we should encounter this situation is in the presence of + // unbalanced JNI locking. TODO: CheckJNICalls. + // See also: CR4414101 #ifdef ASSERT - LogStreamHandle(Error, monitorinflation) lsh; - lsh.print_cr("ERROR: ObjectMonitor::exit(): thread=" INTPTR_FORMAT - " is exiting an ObjectMonitor it does not own.", p2i(current)); - lsh.print_cr("The imbalance is possibly caused by JNI locking."); - print_debug_style_on(&lsh); - assert(false, "Non-balanced monitor enter/exit!"); + LogStreamHandle(Error, monitorinflation) lsh; + lsh.print_cr("ERROR: ObjectMonitor::exit(): thread=" INTPTR_FORMAT + " is exiting an ObjectMonitor it does not own.", p2i(current)); + lsh.print_cr("The imbalance is possibly caused by JNI locking."); + print_debug_style_on(&lsh); + assert(false, "Non-balanced monitor enter/exit!"); #endif - return; - } + return; } if (_recursions != 0) { @@ -1193,18 +1376,18 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { #endif for (;;) { - assert(current == owner_raw(), "invariant"); + assert(has_owner(current), "invariant"); // Drop the lock. // release semantics: prior loads and stores from within the critical section // must not float (reorder) past the following store that drops the lock. // Uses a storeload to separate release_store(owner) from the - // successor check. The try_set_owner() below uses cmpxchg() so + // successor check. The try_set_owner_from() below uses cmpxchg() so // we get the fence down there. release_clear_owner(current); OrderAccess::storeload(); - if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != nullptr) { + if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || has_successor()) { return; } // Other threads are blocked trying to acquire the lock. @@ -1246,7 +1429,7 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { return; } - guarantee(owner_raw() == current, "invariant"); + guarantee(has_owner(current), "invariant"); ObjectWaiter* w = nullptr; @@ -1311,7 +1494,7 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { // See if we can abdicate to a spinner instead of waking a thread. // A primary goal of the implementation is to reduce the // context-switch rate. - if (_succ != nullptr) continue; + if (has_successor()) continue; w = _EntryList; if (w != nullptr) { @@ -1323,7 +1506,7 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { } void ObjectMonitor::ExitEpilog(JavaThread* current, ObjectWaiter* Wakee) { - assert(owner_raw() == current, "invariant"); + assert(has_owner(current), "invariant"); // Exit protocol: // 1. ST _succ = wakee @@ -1331,8 +1514,19 @@ void ObjectMonitor::ExitEpilog(JavaThread* current, ObjectWaiter* Wakee) { // 2. ST _owner = nullptr // 3. unpark(wakee) - _succ = Wakee->_thread; - ParkEvent * Trigger = Wakee->_event; + oop vthread = nullptr; + ParkEvent * Trigger; + if (!Wakee->is_vthread()) { + JavaThread* t = Wakee->thread(); + assert(t != nullptr, ""); + Trigger = t->_ParkEvent; + set_successor(t); + } else { + vthread = Wakee->vthread(); + assert(vthread != nullptr, ""); + Trigger = ObjectMonitor::vthread_unparker_ParkEvent(); + set_successor(vthread); + } // Hygiene -- once we've set _owner = nullptr we can't safely dereference Wakee again. // The thread associated with Wakee may have grabbed the lock and "Wakee" may be @@ -1345,34 +1539,29 @@ void ObjectMonitor::ExitEpilog(JavaThread* current, ObjectWaiter* Wakee) { OrderAccess::fence(); DTRACE_MONITOR_PROBE(contended__exit, this, object(), current); - Trigger->unpark(); + + if (vthread == nullptr) { + // Platform thread case. + Trigger->unpark(); + } else if (java_lang_VirtualThread::set_onWaitingList(vthread, vthread_cxq_head())) { + // Virtual thread case. + Trigger->unpark(); + } // Maintain stats and report events to JVMTI OM_PERFDATA_OP(Parks, inc()); } -// complete_exit exits a lock returning recursion count -// complete_exit requires an inflated monitor -// The _owner field is not always the Thread addr even with an -// inflated monitor, e.g. the monitor can be inflated by a non-owning -// thread due to contention. +// Exits the monitor returning recursion count. _owner should +// be set to current's tid, i.e. no ANONYMOUS_OWNER allowed. intx ObjectMonitor::complete_exit(JavaThread* current) { assert(InitDone, "Unexpectedly not initialized"); + guarantee(has_owner(current), "complete_exit not owner"); - void* cur = owner_raw(); - if (current != cur) { - if (LockingMode != LM_LIGHTWEIGHT && current->is_lock_owned((address)cur)) { - assert(_recursions == 0, "internal state error"); - set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*. - _recursions = 0; - } - } - - guarantee(current == owner_raw(), "complete_exit not owner"); intx save = _recursions; // record the old recursion count _recursions = 0; // set the recursion level to be 0 exit(current); // exit the monitor - guarantee(owner_raw() != current, "invariant"); + guarantee(!has_owner(current), "invariant"); return save; } @@ -1395,14 +1584,8 @@ intx ObjectMonitor::complete_exit(JavaThread* current) { // is not the owner, that exception will be replaced by the IMSE. bool ObjectMonitor::check_owner(TRAPS) { JavaThread* current = THREAD; - void* cur = owner_raw(); - assert(cur != anon_owner_ptr(), "no anon owner here"); - if (cur == current) { - return true; - } - if (LockingMode != LM_LIGHTWEIGHT && current->is_lock_owned((address)cur)) { - set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*. - _recursions = 0; + int64_t cur = owner_raw(); + if (cur == owner_from(current)) { return true; } THROW_MSG_(vmSymbols::java_lang_IllegalMonitorStateException(), @@ -1437,6 +1620,32 @@ static void post_monitor_wait_event(EventJavaMonitorWait* event, event->commit(); } +static void vthread_monitor_waited_event(JavaThread *current, ObjectWaiter* node, ContinuationWrapper& cont, EventJavaMonitorWait* event, jboolean timed_out) { + // Since we might safepoint set the anchor so that the stack can we walked. + assert(current->last_continuation() != nullptr, ""); + JavaFrameAnchor* anchor = current->frame_anchor(); + anchor->set_last_Java_sp(current->last_continuation()->entry_sp()); + anchor->set_last_Java_pc(current->last_continuation()->entry_pc()); + + ContinuationWrapper::SafepointOp so(current, cont); + + JRT_BLOCK + if (event->should_commit()) { + long timeout = java_lang_VirtualThread::timeout(current->vthread()); + post_monitor_wait_event(event, node->_monitor, node->_notifier_tid, timeout, timed_out); + } + if (JvmtiExport::should_post_monitor_waited()) { + // We mark this call in case of an upcall to Java while posting the event. + // If somebody walks the stack in that case, processing the enterSpecial + // frame should not include processing callee arguments since there is no + // actual callee (see nmethod::preserve_callee_argument_oops()). + ThreadOnMonitorWaitedEvent tmwe(current); + JvmtiExport::vthread_post_monitor_waited(current, node->_monitor, timed_out); + } + JRT_BLOCK_END + current->frame_anchor()->clear(); +} + // ----------------------------------------------------------------------------- // Wait/Notify/NotifyAll // @@ -1449,7 +1658,8 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { CHECK_OWNER(); // Throws IMSE if not owner. - EventJavaMonitorWait event; + EventJavaMonitorWait wait_event; + EventVirtualThreadPinned vthread_pinned_event; // check for a pending interrupt if (interruptible && current->is_interrupted(true) && !HAS_PENDING_EXCEPTION) { @@ -1467,8 +1677,8 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // consume an unpark() meant for the ParkEvent associated with // this ObjectMonitor. } - if (event.should_commit()) { - post_monitor_wait_event(&event, this, 0, millis, false); + if (wait_event.should_commit()) { + post_monitor_wait_event(&wait_event, this, 0, millis, false); } THROW(vmSymbols::java_lang_InterruptedException()); return; @@ -1476,6 +1686,17 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { current->set_current_waiting_monitor(this); + freeze_result result; + ContinuationEntry* ce = current->last_continuation(); + if (ce != nullptr && ce->is_virtual_thread()) { + result = Continuation::try_preempt(current, ce->cont_oop(current)); + if (result == freeze_ok) { + VThreadWait(current, millis); + current->set_current_waiting_monitor(nullptr); + return; + } + } + // create a node to be put into the queue // Critically, after we reset() the event but prior to park(), we must check // for a pending interrupt. @@ -1499,7 +1720,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { _waiters++; // increment the number of waiters _recursions = 0; // set the recursion level to be 1 exit(current); // exit the monitor - guarantee(owner_raw() != current, "invariant"); + guarantee(!has_owner(current), "invariant"); // The thread is on the WaitSet list - now park() it. // On MP systems it's conceivable that a brief spin before we park @@ -1525,7 +1746,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); if (interrupted || HAS_PENDING_EXCEPTION) { // Intentionally empty - } else if (node._notified == 0) { + } else if (!node._notified) { if (millis <= 0) { current->_ParkEvent->park(); } else { @@ -1553,7 +1774,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { Thread::SpinAcquire(&_WaitSetLock, "WaitSet - unlink"); if (node.TState == ObjectWaiter::TS_WAIT) { DequeueSpecificWaiter(&node); // unlink from WaitSet - assert(node._notified == 0, "invariant"); + assert(!node._notified, "invariant"); node.TState = ObjectWaiter::TS_RUN; } Thread::SpinRelease(&_WaitSetLock); @@ -1565,7 +1786,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // No other threads will asynchronously modify TState. guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant"); OrderAccess::loadload(); - if (_succ == current) _succ = nullptr; + if (has_successor(current)) clear_successor(); WasNotified = node._notified; // Reentry phase -- reacquire the monitor. @@ -1579,7 +1800,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { if (JvmtiExport::should_post_monitor_waited()) { JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT); - if (node._notified != 0 && _succ == current) { + if (node._notified && has_successor(current)) { // In this part of the monitor wait-notify-reenter protocol it // is possible (and normal) for another thread to do a fastpath // monitor enter-exit while this thread is still trying to get @@ -1595,19 +1816,28 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // We redo the unpark() to ensure forward progress, i.e., we // don't want all pending threads hanging (parked) with none // entering the unlocked monitor. - node._event->unpark(); + current->_ParkEvent->unpark(); } } - if (event.should_commit()) { - post_monitor_wait_event(&event, this, node._notifier_tid, millis, ret == OS_TIMEOUT); + if (wait_event.should_commit()) { + post_monitor_wait_event(&wait_event, this, node._notifier_tid, millis, ret == OS_TIMEOUT); + } + + if (ce != nullptr && ce->is_virtual_thread()) { + assert(result != freeze_ok, "sanity check"); + current->post_vthread_pinned_event(&vthread_pinned_event, "Object.wait", result); } OrderAccess::fence(); - assert(owner_raw() != current, "invariant"); + assert(!has_owner(current), "invariant"); ObjectWaiter::TStates v = node.TState; if (v == ObjectWaiter::TS_RUN) { + // We use the NoPreemptMark for the very rare case where the previous + // preempt attempt failed due to OOM. The preempt on monitor contention + // could succeed but we can't unmount now. + NoPreemptMark npm(current); enter(current); } else { guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant"); @@ -1620,8 +1850,8 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // Node is about to go out-of-scope, but even if it were immortal we wouldn't // want residual elements associated with this thread left on any lists. guarantee(node.TState == ObjectWaiter::TS_RUN, "invariant"); - assert(owner_raw() == current, "invariant"); - assert(_succ != current, "invariant"); + assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); } // OSThreadWaitState() current->set_current_waiting_monitor(nullptr); @@ -1634,8 +1864,8 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { _waiters--; // decrement the number of waiters // Verify a few postconditions - assert(owner_raw() == current, "invariant"); - assert(_succ != current, "invariant"); + assert(has_owner(current), "invariant"); + assert(!has_successor(current), "invariant"); assert_mark_word_consistency(); // check if the notification happened @@ -1651,7 +1881,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // Monitor notify has precedence over thread interrupt. } - // Consider: // If the lock is cool (cxq == null && succ == null) and we're on an MP system // then instead of transferring a thread from the WaitSet to the EntryList @@ -1662,16 +1891,32 @@ void ObjectMonitor::INotify(JavaThread* current) { ObjectWaiter* iterator = DequeueWaiter(); if (iterator != nullptr) { guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); - guarantee(iterator->_notified == 0, "invariant"); + guarantee(!iterator->_notified, "invariant"); // Disposition - what might we do with iterator ? // a. add it directly to the EntryList - either tail (policy == 1) // or head (policy == 0). // b. push it onto the front of the _cxq (policy == 2). // For now we use (b). + if (iterator->is_vthread()) { + oop vthread = iterator->vthread(); + java_lang_VirtualThread::set_notified(vthread, true); + int old_state = java_lang_VirtualThread::state(vthread); + // If state is not WAIT/TIMED_WAIT then target could still be on + // unmount transition, or wait could have already timed-out or target + // could have been interrupted. In the first case, the target itself + // will set the state to BLOCKED at the end of the unmount transition. + // In the other cases the target would have been already unblocked so + // there is nothing to do. + if (old_state == java_lang_VirtualThread::WAIT || + old_state == java_lang_VirtualThread::TIMED_WAIT) { + java_lang_VirtualThread::cmpxchg_state(vthread, old_state, java_lang_VirtualThread::BLOCKED); + } + } + iterator->TState = ObjectWaiter::TS_ENTER; - iterator->_notified = 1; + iterator->_notified = true; iterator->_notifier_tid = JFR_THREAD_ID(current); ObjectWaiter* list = _EntryList; @@ -1704,7 +1949,9 @@ void ObjectMonitor::INotify(JavaThread* current) { // on _WaitSetLock so it's not profitable to reduce the length of the // critical section. - iterator->wait_reenter_begin(this); + if (!iterator->is_vthread()) { + iterator->wait_reenter_begin(this); + } } Thread::SpinRelease(&_WaitSetLock); } @@ -1755,6 +2002,94 @@ void ObjectMonitor::notifyAll(TRAPS) { OM_PERFDATA_OP(Notifications, inc(tally)); } +void ObjectMonitor::VThreadWait(JavaThread* current, jlong millis) { + oop vthread = current->vthread(); + ObjectWaiter* node = new ObjectWaiter(vthread, this); + node->_is_wait = true; + node->TState = ObjectWaiter::TS_WAIT; + java_lang_VirtualThread::set_notified(vthread, false); // Reset notified flag + + // Enter the waiting queue, which is a circular doubly linked list in this case + // but it could be a priority queue or any data structure. + // _WaitSetLock protects the wait queue. Normally the wait queue is accessed only + // by the owner of the monitor *except* in the case where park() + // returns because of a timeout or interrupt. Contention is exceptionally rare + // so we use a simple spin-lock instead of a heavier-weight blocking lock. + + Thread::SpinAcquire(&_WaitSetLock, "WaitSet - add"); + AddWaiter(node); + Thread::SpinRelease(&_WaitSetLock); + + node->_recursions = _recursions; // record the old recursion count + _recursions = 0; // set the recursion level to be 0 + _waiters++; // increment the number of waiters + exit(current); // exit the monitor + guarantee(!has_owner(current), "invariant"); + + assert(java_lang_VirtualThread::state(vthread) == java_lang_VirtualThread::RUNNING, "wrong state for vthread"); + java_lang_VirtualThread::set_state(vthread, millis == 0 ? java_lang_VirtualThread::WAITING : java_lang_VirtualThread::TIMED_WAITING); + java_lang_VirtualThread::set_timeout(vthread, millis); + + // Save the ObjectWaiter* in the chunk since we will need it when resuming execution. + oop cont = java_lang_VirtualThread::continuation(vthread); + stackChunkOop chunk = jdk_internal_vm_Continuation::tail(cont); + chunk->set_object_waiter(node); +} + +bool ObjectMonitor::VThreadWaitReenter(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont) { + // The first time we run after being preempted on Object.wait() we + // need to check if we were interrupted or the wait timed-out, and + // in that case remove ourselves from the _WaitSet queue. + if (node->TState == ObjectWaiter::TS_WAIT) { + Thread::SpinAcquire(&_WaitSetLock, "WaitSet - unlink"); + if (node->TState == ObjectWaiter::TS_WAIT) { + DequeueSpecificWaiter(node); // unlink from WaitSet + assert(!node->_notified, "invariant"); + node->TState = ObjectWaiter::TS_RUN; + } + Thread::SpinRelease(&_WaitSetLock); + } + + // If this was an interrupted case, set the _interrupted boolean so that + // once we re-acquire the monitor we know if we need to throw IE or not. + ObjectWaiter::TStates state = node->TState; + bool was_notified = state == ObjectWaiter::TS_ENTER || state == ObjectWaiter::TS_CXQ; + assert(was_notified || state == ObjectWaiter::TS_RUN, ""); + node->_interrupted = !was_notified && current->is_interrupted(false); + + // Post JFR and JVMTI events. + EventJavaMonitorWait wait_event; + if (wait_event.should_commit() || JvmtiExport::should_post_monitor_waited()) { + vthread_monitor_waited_event(current, node, cont, &wait_event, !was_notified && !node->_interrupted); + } + + // Mark that we are at reenter so that we don't call this method again. + node->_at_reenter = true; + + if (!was_notified) { + bool acquired = VThreadMonitorEnter(current, node); + if (acquired) { + guarantee(_recursions == 0, "invariant"); + _recursions = node->_recursions; // restore the old recursion count + _waiters--; // decrement the number of waiters + + if (node->_interrupted) { + // We will throw at thaw end after finishing the mount transition. + current->set_pending_interrupted_exception(true); + } + + delete node; + stackChunkOop chunk = cont.tail(); + chunk->set_object_waiter(nullptr); + return true; + } + } else { + // Already moved to _cxq or _EntryList by notifier, so just add to contentions. + add_to_contentions(1); + } + return false; +} + // ----------------------------------------------------------------------------- // Adaptive Spinning Support // @@ -1908,10 +2243,10 @@ bool ObjectMonitor::TrySpin(JavaThread* current) { // CONSIDER: use Prefetch::write() to avoid RTS->RTO upgrades // when preparing to LD...CAS _owner, etc and the CAS is likely // to succeed. - if (_succ == nullptr) { - _succ = current; + if (!has_successor()) { + set_successor(current); } - Thread* prv = nullptr; + int64_t prv = NO_OWNER; // There are three ways to exit the following loop: // 1. A successful spin where this thread has acquired the lock. @@ -1949,14 +2284,14 @@ bool ObjectMonitor::TrySpin(JavaThread* current) { // the spin without prejudice or apply a "penalty" to the // spin count-down variable "ctr", reducing it by 100, say. - JavaThread* ox = static_cast(owner_raw()); - if (ox == nullptr) { - ox = static_cast(try_set_owner_from(nullptr, current)); - if (ox == nullptr) { + int64_t ox = owner_raw(); + if (ox == NO_OWNER) { + ox = try_set_owner_from(NO_OWNER, current); + if (ox == NO_OWNER) { // The CAS succeeded -- this thread acquired ownership // Take care of some bookkeeping to exit spin state. - if (_succ == current) { - _succ = nullptr; + if (has_successor(current)) { + clear_successor(); } // Increase _SpinDuration : @@ -1979,13 +2314,13 @@ bool ObjectMonitor::TrySpin(JavaThread* current) { } // Did lock ownership change hands ? - if (ox != prv && prv != nullptr) { + if (ox != prv && prv != NO_OWNER) { break; } prv = ox; - if (_succ == nullptr) { - _succ = current; + if (!has_successor()) { + set_successor(current); } } @@ -1994,8 +2329,8 @@ bool ObjectMonitor::TrySpin(JavaThread* current) { _SpinDuration = adjust_down(_SpinDuration); } - if (_succ == current) { - _succ = nullptr; + if (has_successor(current)) { + clear_successor(); // Invariant: after setting succ=null a contending thread // must recheck-retry _owner before parking. This usually happens // in the normal usage of TrySpin(), but it's safest @@ -2016,13 +2351,33 @@ bool ObjectMonitor::TrySpin(JavaThread* current) { ObjectWaiter::ObjectWaiter(JavaThread* current) { _next = nullptr; _prev = nullptr; - _notified = 0; + _thread = current; + _monitor = nullptr; _notifier_tid = 0; + _recursions = 0; TState = TS_RUN; - _thread = current; - _event = _thread->_ParkEvent; + _notified = false; + _is_wait = false; + _at_reenter = false; + _interrupted = false; _active = false; - assert(_event != nullptr, "invariant"); +} + +ObjectWaiter::ObjectWaiter(oop vthread, ObjectMonitor* mon) : ObjectWaiter(nullptr) { + assert(oopDesc::is_oop(vthread), ""); + _vthread = OopHandle(JavaThread::thread_oop_storage(), vthread); + _monitor = mon; +} + +ObjectWaiter::~ObjectWaiter() { + if (is_vthread()) { + assert(vthread() != nullptr, ""); + _vthread.release(JavaThread::thread_oop_storage()); + } +} + +oop ObjectWaiter::vthread() const { + return _vthread.resolve(); } void ObjectWaiter::wait_reenter_begin(ObjectMonitor * const mon) { @@ -2140,12 +2495,18 @@ void ObjectMonitor::Initialize() { DEBUG_ONLY(InitDone = true;) } +// We can't call this during Initialize() because BarrierSet needs to be set. +void ObjectMonitor::Initialize2() { + _vthread_cxq_head = OopHandle(JavaThread::thread_oop_storage(), nullptr); + _vthread_unparker_ParkEvent = ParkEvent::Allocate(nullptr); +} + void ObjectMonitor::print_on(outputStream* st) const { // The minimal things to print for markWord printing, more can be added for debugging and logging. st->print("{contentions=0x%08x,waiters=0x%08x" - ",recursions=" INTX_FORMAT ",owner=" INTPTR_FORMAT "}", + ",recursions=" INTX_FORMAT ",owner=" INT64_FORMAT "}", contentions(), waiters(), recursions(), - p2i(owner())); + owner()); } void ObjectMonitor::print() const { print_on(tty); } @@ -2188,7 +2549,7 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr(" ..."); st->print_cr(" [%d] = '\\0'", (int)sizeof(_pad_buf0) - 1); st->print_cr(" }"); - st->print_cr(" _owner = " INTPTR_FORMAT, p2i(owner_raw())); + st->print_cr(" _owner = " INT64_FORMAT, owner_raw()); st->print_cr(" _previous_owner_tid = " UINT64_FORMAT, _previous_owner_tid); st->print_cr(" _pad_buf1 = {"); st->print_cr(" [0] = '\\0'"); @@ -2199,7 +2560,7 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr(" _recursions = " INTX_FORMAT, _recursions); st->print_cr(" _EntryList = " INTPTR_FORMAT, p2i(_EntryList)); st->print_cr(" _cxq = " INTPTR_FORMAT, p2i(_cxq)); - st->print_cr(" _succ = " INTPTR_FORMAT, p2i(_succ)); + st->print_cr(" _succ = " INT64_FORMAT, successor()); st->print_cr(" _SpinDuration = %d", _SpinDuration); st->print_cr(" _contentions = %d", contentions()); st->print_cr(" _WaitSet = " INTPTR_FORMAT, p2i(_WaitSet)); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 30d2e5094162d..c205eedf68468 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -28,34 +28,48 @@ #include "memory/allocation.hpp" #include "memory/padded.hpp" #include "oops/markWord.hpp" +#include "oops/oopHandle.hpp" #include "oops/weakHandle.hpp" +#include "runtime/javaThread.hpp" #include "runtime/perfDataTypes.hpp" #include "utilities/checkedCast.hpp" class ObjectMonitor; class ObjectMonitorContentionMark; class ParkEvent; +class BasicLock; +class ContinuationWrapper; -// ObjectWaiter serves as a "proxy" or surrogate thread. -// TODO-FIXME: Eliminate ObjectWaiter and use the thread-specific -// ParkEvent instead. Beware, however, that the JVMTI code -// knows about ObjectWaiters, so we'll have to reconcile that code. -// See next_waiter(), first_waiter(), etc. -class ObjectWaiter : public StackObj { +class ObjectWaiter : public CHeapObj { public: - enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ }; + enum TStates : uint8_t { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ }; ObjectWaiter* volatile _next; ObjectWaiter* volatile _prev; - JavaThread* _thread; - uint64_t _notifier_tid; - ParkEvent * _event; - volatile int _notified; + JavaThread* _thread; + OopHandle _vthread; + ObjectMonitor* _monitor; + uint64_t _notifier_tid; + int _recursions; volatile TStates TState; - bool _active; // Contention monitoring is enabled + volatile bool _notified; + bool _is_wait; + bool _at_reenter; + bool _interrupted; + bool _active; // Contention monitoring is enabled public: ObjectWaiter(JavaThread* current); - + ObjectWaiter(oop vthread, ObjectMonitor* mon); + ~ObjectWaiter(); + JavaThread* thread() const { return _thread; } + bool is_vthread() const { return _thread == nullptr; } + uint8_t state() const { return TState; } + ObjectMonitor* monitor() const { return _monitor; } + bool is_wait() const { return _is_wait; } + bool notified() const { return _notified; } + bool at_reenter() const { return _at_reenter; } + bool at_monitorenter() const { return !_is_wait || _at_reenter || _notified; } + oop vthread() const; void wait_reenter_begin(ObjectMonitor *mon); void wait_reenter_end(ObjectMonitor *mon); }; @@ -132,6 +146,11 @@ class ObjectMonitor : public CHeapObj { static OopStorage* _oop_storage; + // List of j.l.VirtualThread waiting to be unblocked by unblocker thread. + static OopHandle _vthread_cxq_head; + // ParkEvent of unblocker thread. + static ParkEvent* _vthread_unparker_ParkEvent; + // The sync code expects the metadata field to be at offset zero (0). // Enforced by the assert() in metadata_addr(). // * LM_LIGHTWEIGHT with UseObjectMonitorTable: @@ -146,24 +165,12 @@ class ObjectMonitor : public CHeapObj { // its cache line with _metadata. DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(_metadata) + sizeof(WeakHandle)); - // Used by async deflation as a marker in the _owner field. - // Note that the choice of the two markers is peculiar: - // - They need to represent values that cannot be pointers. In particular, - // we achieve this by using the lowest two bits. - // - ANONYMOUS_OWNER should be a small value, it is used in generated code - // and small values encode much better. - // - We test for anonymous owner by testing for the lowest bit, therefore - // DEFLATER_MARKER must *not* have that bit set. - static const uintptr_t DEFLATER_MARKER_VALUE = 2; - #define DEFLATER_MARKER reinterpret_cast(DEFLATER_MARKER_VALUE) - public: - // NOTE: Typed as uintptr_t so that we can pick it up in SA, via vmStructs. - static const uintptr_t ANONYMOUS_OWNER = 1; - private: - static void* anon_owner_ptr() { return reinterpret_cast(ANONYMOUS_OWNER); } + static const int64_t NO_OWNER = 0; + static const int64_t ANONYMOUS_OWNER = 1; + static const int64_t DEFLATER_MARKER = 2; - void* volatile _owner; // pointer to owning thread OR BasicLock + int64_t volatile _owner; // Either tid of owner, NO_OWNER, ANONYMOUS_OWNER or DEFLATER_MARKER. volatile uint64_t _previous_owner_tid; // thread id of the previous owner of the monitor // Separate _owner and _next_om on different cache lines since // both can have busy multi-threaded access. _previous_owner_tid is only @@ -178,7 +185,7 @@ class ObjectMonitor : public CHeapObj { // acting as proxies for Threads. ObjectWaiter* volatile _cxq; // LL of recently-arrived threads blocked on entry. - JavaThread* volatile _succ; // Heir presumptive thread - used for futile wakeup throttling + int64_t volatile _succ; // Heir presumptive thread - used for futile wakeup throttling volatile int _SpinDuration; @@ -191,9 +198,16 @@ class ObjectMonitor : public CHeapObj { volatile int _waiters; // number of waiting threads volatile int _WaitSetLock; // protects Wait Queue - simple spinlock + // Used in LM_LEGACY mode to store BasicLock* in case of inflation by contending thread. + BasicLock* volatile _stack_locker; + public: static void Initialize(); + static void Initialize2(); + + static OopHandle& vthread_cxq_head() { return _vthread_cxq_head; } + static ParkEvent* vthread_unparker_ParkEvent() { return _vthread_unparker_ParkEvent; } // Only perform a PerfData operation if the PerfData object has been // allocated and if the PerfDataManager has not freed the PerfData @@ -249,7 +263,7 @@ class ObjectMonitor : public CHeapObj { void set_hash(intptr_t hash); bool is_busy() const { - // TODO-FIXME: assert _owner == null implies _recursions = 0 + // TODO-FIXME: assert _owner == NO_OWNER implies _recursions = 0 intptr_t ret_code = intptr_t(_waiters) | intptr_t(_cxq) | intptr_t(_EntryList); int cnts = contentions(); // read once if (cnts > 0) { @@ -266,48 +280,75 @@ class ObjectMonitor : public CHeapObj { // Returns true if this OM has an owner, false otherwise. bool has_owner() const; - void* owner() const; // Returns null if DEFLATER_MARKER is observed. - void* owner_raw() const; + int64_t owner() const; // Returns NO_OWNER if DEFLATER_MARKER is observed. + int64_t owner_raw() const; + + // These methods return the value we set in _owner when acquiring + // the monitor with the given thread/vthread (tid). + static int64_t owner_from(JavaThread* thread); + static int64_t owner_from(oop vthread); + // Returns true if owner field == DEFLATER_MARKER and false otherwise. bool owner_is_DEFLATER_MARKER() const; // Returns true if 'this' is being async deflated and false otherwise. bool is_being_async_deflated(); - // Clear _owner field; current value must match old_value. - void release_clear_owner(void* old_value); + // Clear _owner field; current value must match thread's tid. + void release_clear_owner(JavaThread* thread); // Simply set _owner field to new_value; current value must match old_value. - void set_owner_from(void* old_value, void* new_value); - // Simply set _owner field to current; current value must match basic_lock_p. - void set_owner_from_BasicLock(void* basic_lock_p, JavaThread* current); + void set_owner_from_raw(int64_t old_value, int64_t new_value); + // Same as above but uses tid of current as new value. + void set_owner_from(int64_t old_value, JavaThread* current); // Try to set _owner field to new_value if the current value matches // old_value, using Atomic::cmpxchg(). Otherwise, does not change the // _owner field. Returns the prior value of the _owner field. - void* try_set_owner_from(void* old_value, void* new_value); - - void set_owner_anonymous() { - set_owner_from(nullptr, anon_owner_ptr()); + int64_t try_set_owner_from_raw(int64_t old_value, int64_t new_value); + // Same as above but uses tid of current as new_value. + int64_t try_set_owner_from(int64_t old_value, JavaThread* current); + + // Methods to check and set _succ. The successor is the thread selected + // from _cxq/_EntryList by the current owner when releasing the monitor, + // to run again and re-try acquiring the monitor. It is used to avoid + // unnecessary wake-ups if there is already a successor set. + bool has_successor() const; + bool has_successor(JavaThread* thread) const; + void set_successor(JavaThread* thread); + void set_successor(oop vthread); + void clear_successor(); + int64_t successor() const; + + // Returns true if _owner field == tid of thread, false otherwise. + bool has_owner(JavaThread* thread) const { return owner() == owner_from(thread); } + // Set _owner field to tid of thread; current value must be NO_OWNER. + void set_owner(JavaThread* thread) { set_owner_from(NO_OWNER, thread); } + // Try to set _owner field from NO_OWNER to tid of thread. + bool try_set_owner(JavaThread* thread) { + return try_set_owner_from(NO_OWNER, thread) == NO_OWNER; } - bool is_owner_anonymous() const { - return owner_raw() == anon_owner_ptr(); + bool has_anonymous_owner() const { return owner_raw() == ANONYMOUS_OWNER; } + void set_anonymous_owner() { + set_owner_from_raw(NO_OWNER, ANONYMOUS_OWNER); } - - void set_owner_from_anonymous(Thread* owner) { - set_owner_from(anon_owner_ptr(), owner); + void set_owner_from_anonymous(JavaThread* owner) { + set_owner_from(ANONYMOUS_OWNER, owner); } + // Get and set _stack_locker. + BasicLock* stack_locker() const; + void set_stack_locker(BasicLock* locker); + // Simply get _next_om field. ObjectMonitor* next_om() const; // Simply set _next_om field to new_value. void set_next_om(ObjectMonitor* new_value); - int waiters() const; - int contentions() const; void add_to_contentions(int value); intx recursions() const { return _recursions; } void set_recursions(size_t recursions); // JVM/TI GetObjectMonitorUsage() needs this: + int waiters() const; ObjectWaiter* first_waiter() { return _WaitSet; } ObjectWaiter* next_waiter(ObjectWaiter* o) { return o->_next; } JavaThread* thread_of_waiter(ObjectWaiter* o) { return o->_thread; } @@ -351,6 +392,7 @@ class ObjectMonitor : public CHeapObj { bool spin_enter(JavaThread* current); void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark); void exit(JavaThread* current, bool not_suspended = true); + bool resume_operation(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont); void wait(jlong millis, bool interruptible, TRAPS); void notify(TRAPS); void notifyAll(TRAPS); @@ -373,6 +415,10 @@ class ObjectMonitor : public CHeapObj { void ReenterI(JavaThread* current, ObjectWaiter* current_node); void UnlinkAfterAcquire(JavaThread* current, ObjectWaiter* current_node); + bool VThreadMonitorEnter(JavaThread* current, ObjectWaiter* node = nullptr); + void VThreadWait(JavaThread* current, jlong millis); + bool VThreadWaitReenter(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont); + void VThreadEpilog(JavaThread* current, ObjectWaiter* node); enum class TryLockResult { Interference = -1, HasOwner = 0, Success = 1 }; diff --git a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp index 6d3c6ff24c38b..64e233a1ff5f2 100644 --- a/src/hotspot/share/runtime/objectMonitor.inline.hpp +++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp @@ -32,23 +32,34 @@ #include "oops/markWord.hpp" #include "runtime/atomic.hpp" #include "runtime/globals.hpp" +#include "runtime/javaThread.inline.hpp" #include "runtime/lockStack.inline.hpp" #include "runtime/synchronizer.hpp" +#include "runtime/threadIdentifier.hpp" #include "utilities/checkedCast.hpp" #include "utilities/globalDefinitions.hpp" +inline int64_t ObjectMonitor::owner_from(JavaThread* thread) { + int64_t tid = thread->lock_id(); + assert(tid >= ThreadIdentifier::initial() && tid < ThreadIdentifier::current(), "must be reasonable"); + return tid; +} + +inline int64_t ObjectMonitor::owner_from(oop vthread) { + int64_t tid = java_lang_Thread::thread_id(vthread); + assert(tid >= ThreadIdentifier::initial() && tid < ThreadIdentifier::current(), "must be reasonable"); + return tid; +} + inline bool ObjectMonitor::is_entered(JavaThread* current) const { - if (LockingMode == LM_LIGHTWEIGHT) { - if (is_owner_anonymous()) { + if (has_anonymous_owner()) { + if (LockingMode == LM_LIGHTWEIGHT) { return current->lock_stack().contains(object()); } else { - return current == owner_raw(); + return current->is_lock_owned((address)stack_locker()); } } else { - void* owner = owner_raw(); - if (current == owner || current->is_lock_owned((address)owner)) { - return true; - } + return has_owner(current); } return false; } @@ -92,23 +103,29 @@ inline int ObjectMonitor::waiters() const { } inline bool ObjectMonitor::has_owner() const { - void* owner = owner_raw(); - return owner != nullptr && owner != DEFLATER_MARKER; + int64_t owner = owner_raw(); + return owner != NO_OWNER && owner != DEFLATER_MARKER; } -// Returns null if DEFLATER_MARKER is observed. -inline void* ObjectMonitor::owner() const { - void* owner = owner_raw(); - return owner != DEFLATER_MARKER ? owner : nullptr; +// Returns NO_OWNER if DEFLATER_MARKER is observed. +inline int64_t ObjectMonitor::owner() const { + int64_t owner = owner_raw(); + return owner != DEFLATER_MARKER ? owner : NO_OWNER; } -inline void* ObjectMonitor::owner_raw() const { +inline int64_t ObjectMonitor::owner_raw() const { return Atomic::load(&_owner); } +inline BasicLock* ObjectMonitor::stack_locker() const { + return Atomic::load(&_stack_locker); +} + +inline void ObjectMonitor::set_stack_locker(BasicLock* locker) { + Atomic::store(&_stack_locker, locker); +} + // Returns true if owner field == DEFLATER_MARKER and false otherwise. -// This accessor is called when we really need to know if the owner -// field == DEFLATER_MARKER and any non-null value won't do the trick. inline bool ObjectMonitor::owner_is_DEFLATER_MARKER() const { return owner_raw() == DEFLATER_MARKER; } @@ -135,63 +152,82 @@ inline void ObjectMonitor::set_recursions(size_t recursions) { } // Clear _owner field; current value must match old_value. -inline void ObjectMonitor::release_clear_owner(void* old_value) { +inline void ObjectMonitor::release_clear_owner(JavaThread* old_owner) { + int64_t old_value = owner_from(old_owner); #ifdef ASSERT - void* prev = Atomic::load(&_owner); - assert(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT - ", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value)); + int64_t prev = Atomic::load(&_owner); + assert(prev == old_value, "unexpected prev owner=" INT64_FORMAT + ", expected=" INT64_FORMAT, prev, old_value); #endif - Atomic::release_store(&_owner, (void*)nullptr); + Atomic::release_store(&_owner, NO_OWNER); log_trace(monitorinflation, owner)("release_clear_owner(): mid=" - INTPTR_FORMAT ", old_value=" INTPTR_FORMAT, - p2i(this), p2i(old_value)); + INTPTR_FORMAT ", old_value=" INT64_FORMAT, + p2i(this), old_value); } // Simply set _owner field to new_value; current value must match old_value. // (Simple means no memory sync needed.) -inline void ObjectMonitor::set_owner_from(void* old_value, void* new_value) { +inline void ObjectMonitor::set_owner_from_raw(int64_t old_value, int64_t new_value) { #ifdef ASSERT - void* prev = Atomic::load(&_owner); - assert(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT - ", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value)); + int64_t prev = Atomic::load(&_owner); + assert((int64_t)prev < ThreadIdentifier::current(), "must be reasonable"); + assert(prev == old_value, "unexpected prev owner=" INT64_FORMAT + ", expected=" INT64_FORMAT, prev, old_value); #endif Atomic::store(&_owner, new_value); log_trace(monitorinflation, owner)("set_owner_from(): mid=" - INTPTR_FORMAT ", old_value=" INTPTR_FORMAT - ", new_value=" INTPTR_FORMAT, p2i(this), - p2i(old_value), p2i(new_value)); + INTPTR_FORMAT ", old_value=" INT64_FORMAT + ", new_value=" INT64_FORMAT, p2i(this), + old_value, new_value); } -// Simply set _owner field to self; current value must match basic_lock_p. -inline void ObjectMonitor::set_owner_from_BasicLock(void* basic_lock_p, JavaThread* current) { -#ifdef ASSERT - void* prev = Atomic::load(&_owner); - assert(prev == basic_lock_p, "unexpected prev owner=" INTPTR_FORMAT - ", expected=" INTPTR_FORMAT, p2i(prev), p2i(basic_lock_p)); -#endif - // Non-null owner field to non-null owner field is safe without - // cmpxchg() as long as all readers can tolerate either flavor. - Atomic::store(&_owner, current); - log_trace(monitorinflation, owner)("set_owner_from_BasicLock(): mid=" - INTPTR_FORMAT ", basic_lock_p=" - INTPTR_FORMAT ", new_value=" INTPTR_FORMAT, - p2i(this), p2i(basic_lock_p), p2i(current)); +inline void ObjectMonitor::set_owner_from(int64_t old_value, JavaThread* current) { + set_owner_from_raw(old_value, owner_from(current)); } // Try to set _owner field to new_value if the current value matches // old_value. Otherwise, does not change the _owner field. Returns // the prior value of the _owner field. -inline void* ObjectMonitor::try_set_owner_from(void* old_value, void* new_value) { - void* prev = Atomic::cmpxchg(&_owner, old_value, new_value); +inline int64_t ObjectMonitor::try_set_owner_from_raw(int64_t old_value, int64_t new_value) { + assert((int64_t)new_value < ThreadIdentifier::current(), "must be reasonable"); + int64_t prev = Atomic::cmpxchg(&_owner, old_value, new_value); if (prev == old_value) { log_trace(monitorinflation, owner)("try_set_owner_from(): mid=" - INTPTR_FORMAT ", prev=" INTPTR_FORMAT - ", new=" INTPTR_FORMAT, p2i(this), - p2i(prev), p2i(new_value)); + INTPTR_FORMAT ", prev=" INT64_FORMAT + ", new=" INT64_FORMAT, p2i(this), + prev, new_value); } return prev; } +inline int64_t ObjectMonitor::try_set_owner_from(int64_t old_value, JavaThread* current) { + return try_set_owner_from_raw(old_value, owner_from(current)); +} + +inline bool ObjectMonitor::has_successor() const { + return Atomic::load(&_succ) != NO_OWNER; +} + +inline bool ObjectMonitor::has_successor(JavaThread* thread) const { + return owner_from(thread) == Atomic::load(&_succ); +} + +inline void ObjectMonitor::set_successor(JavaThread* thread) { + Atomic::store(&_succ, owner_from(thread)); +} + +inline void ObjectMonitor::set_successor(oop vthread) { + Atomic::store(&_succ, java_lang_Thread::thread_id(vthread)); +} + +inline void ObjectMonitor::clear_successor() { + Atomic::store(&_succ, NO_OWNER); +} + +inline int64_t ObjectMonitor::successor() const { + return Atomic::load(&_succ); +} + // The _next_om field can be concurrently read and modified so we // use Atomic operations to disable compiler optimizations that // might try to elide loading and/or storing this field. diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index e4d4e6aea0f8c..a9ad42ec5f228 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -1971,7 +1971,7 @@ void SharedRuntime::monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThrea // hasn't been deallocated. ObjectMonitor* m = current->unlocked_inflated_monitor(); if (m != nullptr) { - assert(m->owner_raw() != current, "must be"); + assert(!m->has_owner(current), "must be"); current->clear_unlocked_inflated_monitor(); // We need to reacquire the lock before we can call ObjectSynchronizer::exit(). diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index f530c0ad2a805..854d4dcf1ad52 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -512,6 +512,10 @@ class SharedRuntime: AllStatic { // On PowerPC it includes the 4 words holding the old TOC & LR glue. static uint in_preserve_stack_slots(); + static VMReg thread_register(); + + static void continuation_enter_cleanup(MacroAssembler* masm); + // Is vector's size (in bytes) bigger than a size saved by default? // For example, on x86 16 bytes XMM registers are saved by default. static bool is_wide_vector(int size); diff --git a/src/hotspot/share/runtime/stackChunkFrameStream.hpp b/src/hotspot/share/runtime/stackChunkFrameStream.hpp index 6937feb151753..207203dbb3509 100644 --- a/src/hotspot/share/runtime/stackChunkFrameStream.hpp +++ b/src/hotspot/share/runtime/stackChunkFrameStream.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,6 +75,7 @@ class StackChunkFrameStream : public StackObj { inline bool is_stub() const; inline bool is_compiled() const; CodeBlob* cb() const { return _cb; } + inline void get_cb(); const ImmutableOopMap* oopmap() const { if (_oopmap == nullptr) get_oopmap(); return _oopmap; } inline int frame_size() const; inline int stack_argsize() const; @@ -98,7 +99,6 @@ class StackChunkFrameStream : public StackObj { private: inline address get_pc() const; - inline void get_cb(); inline int interpreter_frame_size() const; inline int interpreter_frame_num_oops() const; diff --git a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp index c56034786c9d7..734d31d33907a 100644 --- a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp +++ b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp @@ -106,7 +106,7 @@ StackChunkFrameStream::StackChunkFrameStream(stackChunkOop chunk, co template inline bool StackChunkFrameStream::is_stub() const { - return cb() != nullptr && (_cb->is_safepoint_stub() || _cb->is_runtime_stub()); + return cb() != nullptr && _cb->is_runtime_stub(); } template @@ -196,7 +196,14 @@ inline int StackChunkFrameStream::stack_argsize() const { template inline int StackChunkFrameStream::num_oops() const { - return is_interpreted() ? interpreter_frame_num_oops() : oopmap()->num_oops(); + if (is_interpreted()) { + return interpreter_frame_num_oops(); + } else if (is_compiled()) { + return oopmap()->num_oops(); + } else { + assert(is_stub(), "invariant"); + return 0; + } } template @@ -208,7 +215,7 @@ template template inline void StackChunkFrameStream::next(RegisterMapT* map, bool stop) { update_reg_map(map); - bool safepoint = is_stub(); + bool is_runtime_stub = is_stub(); if (frame_kind == ChunkFrames::Mixed) { if (is_interpreted()) { next_for_interpreter_frame(); @@ -232,8 +239,9 @@ inline void StackChunkFrameStream::next(RegisterMapT* map, bool stop get_cb(); update_reg_map_pd(map); - if (safepoint && cb() != nullptr) { // there's no post-call nop and no fast oopmap lookup - _oopmap = cb()->oop_map_for_return_address(pc()); + if (is_runtime_stub && cb() != nullptr) { // there's no post-call nop and no fast oopmap lookup + // caller could have been deoptimized so use orig_pc() + _oopmap = cb()->oop_map_for_return_address(orig_pc()); } } @@ -300,9 +308,8 @@ inline void StackChunkFrameStream::update_reg_map(RegisterMa template<> template<> inline void StackChunkFrameStream::update_reg_map(RegisterMap* map) { - assert(map->in_cont(), ""); - assert(map->stack_chunk()() == _chunk, ""); - if (map->update_map()) { + assert(!map->in_cont() || map->stack_chunk() == _chunk, ""); + if (map->update_map() && is_stub()) { frame f = to_frame(); oopmap()->update_register_map(&f, map); // we have callee-save registers in this case } diff --git a/src/hotspot/share/runtime/stackValue.cpp b/src/hotspot/share/runtime/stackValue.cpp index c0b511ed53728..cc6e0bb92d6d4 100644 --- a/src/hotspot/share/runtime/stackValue.cpp +++ b/src/hotspot/share/runtime/stackValue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -293,7 +293,7 @@ address StackValue::stack_value_address(const frame* fr, const RegisterMapT* reg return value_addr; } -BasicLock* StackValue::resolve_monitor_lock(const frame* fr, Location location) { +BasicLock* StackValue::resolve_monitor_lock(const frame& fr, Location location) { assert(location.is_stack(), "for now we only look at the stack"); int word_offset = location.stack_offset() / wordSize; // (stack picture) @@ -304,7 +304,7 @@ BasicLock* StackValue::resolve_monitor_lock(const frame* fr, Location location) // the word_offset is the distance from the stack pointer to the lowest address // The frame's original stack pointer, before any extension by its callee // (due to Compiler1 linkage on SPARC), must be used. - return (BasicLock*) (fr->unextended_sp() + word_offset); + return (BasicLock*) (fr.unextended_sp() + word_offset); } diff --git a/src/hotspot/share/runtime/stackValue.hpp b/src/hotspot/share/runtime/stackValue.hpp index 517c4cf9e7780..5001675872c55 100644 --- a/src/hotspot/share/runtime/stackValue.hpp +++ b/src/hotspot/share/runtime/stackValue.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -123,7 +123,7 @@ class StackValue : public ResourceObj { static StackValue* create_stack_value_from_oop_location(stackChunkOop chunk, void* addr); static StackValue* create_stack_value_from_narrowOop_location(stackChunkOop chunk, void* addr, bool is_register); - static BasicLock* resolve_monitor_lock(const frame* fr, Location location); + static BasicLock* resolve_monitor_lock(const frame& fr, Location location); template static StackValue* create_stack_value(const frame* fr, const RegisterMapT* reg_map, ScopeValue* sv); diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index c881b64b59280..5253bcd05712a 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -186,6 +186,7 @@ address StubRoutines::_array_partition = nullptr; address StubRoutines::_cont_thaw = nullptr; address StubRoutines::_cont_returnBarrier = nullptr; address StubRoutines::_cont_returnBarrierExc = nullptr; +address StubRoutines::_cont_preempt_stub = nullptr; address StubRoutines::_upcall_stub_exception_handler = nullptr; address StubRoutines::_upcall_stub_load_target = nullptr; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index f025742b60585..4c96738ce4b1c 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -292,6 +292,7 @@ class StubRoutines: AllStatic { static address _cont_thaw; static address _cont_returnBarrier; static address _cont_returnBarrierExc; + static address _cont_preempt_stub; // Vector Math Routines static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; @@ -501,6 +502,7 @@ class StubRoutines: AllStatic { static address cont_thaw() { return _cont_thaw; } static address cont_returnBarrier() { return _cont_returnBarrier; } static address cont_returnBarrierExc(){return _cont_returnBarrierExc; } + static address cont_preempt_stub() { return _cont_preempt_stub; } static address upcall_stub_exception_handler() { assert(_upcall_stub_exception_handler != nullptr, "not implemented"); diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index d8fb7a4734abe..cba1918529cb8 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -361,7 +361,7 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al return false; } assert(mon->object() == oop(obj), "invariant"); - if (mon->owner() != current) return false; // slow-path for IMS exception + if (!mon->has_owner(current)) return false; // slow-path for IMS exception if (mon->first_waiter() != nullptr) { // We have one or more waiters. Since this is an inflated monitor @@ -424,14 +424,13 @@ bool ObjectSynchronizer::quick_enter_legacy(oop obj, BasicLock* lock, JavaThread if (m->object_peek() == nullptr) { return false; } - JavaThread* const owner = static_cast(m->owner_raw()); // Lock contention and Transactional Lock Elision (TLE) diagnostics // and observability // Case: light contention possibly amenable to TLE // Case: TLE inimical operations such as nested/recursive synchronization - if (owner == current) { + if (m->has_owner(current)) { m->_recursions++; current->inc_held_monitor_count(); return true; @@ -448,7 +447,7 @@ bool ObjectSynchronizer::quick_enter_legacy(oop obj, BasicLock* lock, JavaThread // and last are the inflated Java Monitor (ObjectMonitor) checks. lock->set_displaced_header(markWord::unused_mark()); - if (owner == nullptr && m->try_set_owner_from(nullptr, current) == nullptr) { + if (!m->has_owner() && m->try_set_owner(current)) { assert(m->_recursions == 0, "invariant"); current->inc_held_monitor_count(); return true; @@ -658,7 +657,7 @@ void ObjectSynchronizer::exit_legacy(oop object, BasicLock* lock, JavaThread* cu // The ObjectMonitor* can't be async deflated until ownership is // dropped inside exit() and the ObjectMonitor* must be !is_busy(). ObjectMonitor* monitor = inflate(current, object, inflate_cause_vm_internal); - assert(!monitor->is_owner_anonymous(), "must not be"); + assert(!monitor->has_anonymous_owner(), "must not be"); monitor->exit(current); } @@ -666,6 +665,10 @@ void ObjectSynchronizer::exit_legacy(oop object, BasicLock* lock, JavaThread* cu // JNI locks on java objects // NOTE: must use heavy weight monitor to handle jni monitor enter void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) { + // Top native frames in the stack will not be seen if we attempt + // preemption, since we start walking from the last Java anchor. + NoPreemptMark npm(current); + if (obj->klass()->is_value_based()) { handle_sync_on_value_based_class(obj, current); } @@ -717,7 +720,7 @@ void ObjectSynchronizer::jni_exit(oop obj, TRAPS) { // ----------------------------------------------------------------------------- // Internal VM locks on java objects // standard constructor, allows locking failures -ObjectLocker::ObjectLocker(Handle obj, JavaThread* thread) { +ObjectLocker::ObjectLocker(Handle obj, JavaThread* thread) : _npm(thread) { _thread = thread; _thread->check_for_valid_safepoint_state(); _obj = obj; @@ -1165,7 +1168,7 @@ JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_ob if (LockingMode == LM_LEGACY && mark.has_locker()) { // stack-locked so header points into owner's stack. // owning_thread_from_monitor_owner() may also return null here: - return Threads::owning_thread_from_monitor_owner(t_list, (address) mark.locker()); + return Threads::owning_thread_from_stacklock(t_list, (address) mark.locker()); } if (LockingMode == LM_LIGHTWEIGHT && mark.is_fast_locked()) { @@ -1229,7 +1232,7 @@ void ObjectSynchronizer::owned_monitors_iterate_filtered(MonitorClosure* closure // only interested in an owned ObjectMonitor and ownership // cannot be dropped under the calling contexts so the // ObjectMonitor cannot be async deflated. - if (monitor->has_owner() && filter(monitor->owner_raw())) { + if (monitor->has_owner() && filter(monitor)) { assert(!monitor->is_being_async_deflated(), "Owned monitors should not be deflating"); closure->do_monitor(monitor); @@ -1240,13 +1243,20 @@ void ObjectSynchronizer::owned_monitors_iterate_filtered(MonitorClosure* closure // Iterate ObjectMonitors where the owner == thread; this does NOT include // ObjectMonitors where owner is set to a stack-lock address in thread. void ObjectSynchronizer::owned_monitors_iterate(MonitorClosure* closure, JavaThread* thread) { - auto thread_filter = [&](void* owner) { return owner == thread; }; + int64_t key = ObjectMonitor::owner_from(thread); + auto thread_filter = [&](ObjectMonitor* monitor) { return monitor->owner() == key; }; + return owned_monitors_iterate_filtered(closure, thread_filter); +} + +void ObjectSynchronizer::owned_monitors_iterate(MonitorClosure* closure, oop vthread) { + int64_t key = ObjectMonitor::owner_from(vthread); + auto thread_filter = [&](ObjectMonitor* monitor) { return monitor->owner() == key; }; return owned_monitors_iterate_filtered(closure, thread_filter); } // Iterate ObjectMonitors owned by any thread. void ObjectSynchronizer::owned_monitors_iterate(MonitorClosure* closure) { - auto all_filter = [&](void* owner) { return true; }; + auto all_filter = [&](ObjectMonitor* monitor) { return true; }; return owned_monitors_iterate_filtered(closure, all_filter); } @@ -1418,16 +1428,22 @@ void ObjectSynchronizer::inflate_helper(oop obj) { ObjectMonitor* ObjectSynchronizer::inflate(Thread* current, oop obj, const InflateCause cause) { assert(current == Thread::current(), "must be"); assert(LockingMode != LM_LIGHTWEIGHT, "only inflate through enter"); - return inflate_impl(obj, cause); + return inflate_impl(current->is_Java_thread() ? JavaThread::cast(current) : nullptr, obj, cause); } ObjectMonitor* ObjectSynchronizer::inflate_for(JavaThread* thread, oop obj, const InflateCause cause) { assert(thread == Thread::current() || thread->is_obj_deopt_suspend(), "must be"); assert(LockingMode != LM_LIGHTWEIGHT, "LM_LIGHTWEIGHT cannot use inflate_for"); - return inflate_impl(obj, cause); + return inflate_impl(thread, obj, cause); } -ObjectMonitor* ObjectSynchronizer::inflate_impl(oop object, const InflateCause cause) { +ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* locking_thread, oop object, const InflateCause cause) { + // The JavaThread* locking_thread requires that the locking_thread == Thread::current() or + // is suspended throughout the call by some other mechanism. + // The thread might be nullptr when called from a non JavaThread. (As may still be + // the case from FastHashCode). However it is only important for correctness that the + // thread is set when called from ObjectSynchronizer::enter from the owning thread, + // ObjectSynchronizer::enter_for from any thread, or ObjectSynchronizer::exit. assert(LockingMode != LM_LIGHTWEIGHT, "LM_LIGHTWEIGHT cannot use inflate_impl"); EventJavaMonitorInflate event; @@ -1435,7 +1451,9 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(oop object, const InflateCause c const markWord mark = object->mark_acquire(); // The mark can be in one of the following states: - // * inflated - Just return it. + // * inflated - If the ObjectMonitor owner is anonymous and the + // locking_thread owns the object lock, then we + // make the locking_thread the ObjectMonitor owner. // * stack-locked - Coerce it to inflated from stack-locked. // * INFLATING - Busy wait for conversion from stack-locked to // inflated. @@ -1446,6 +1464,13 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(oop object, const InflateCause c ObjectMonitor* inf = mark.monitor(); markWord dmw = inf->header(); assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value()); + if (inf->has_anonymous_owner() && locking_thread != nullptr) { + assert(LockingMode == LM_LEGACY, "invariant"); + if (locking_thread->is_lock_owned((address)inf->stack_locker())) { + inf->set_stack_locker(nullptr); + inf->set_owner_from_anonymous(locking_thread); + } + } return inf; } @@ -1522,12 +1547,17 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(oop object, const InflateCause c // Setup monitor fields to proper values -- prepare the monitor m->set_header(dmw); - // Optimization: if the mark.locker stack address is associated - // with this thread we could simply set m->_owner = current. // Note that a thread can inflate an object // that it has stack-locked -- as might happen in wait() -- directly // with CAS. That is, we can avoid the xchg-nullptr .... ST idiom. - m->set_owner_from(nullptr, mark.locker()); + if (locking_thread != nullptr && locking_thread->is_lock_owned((address)mark.locker())) { + m->set_owner(locking_thread); + } else { + // Use ANONYMOUS_OWNER to indicate that the owner is the BasicLock on the stack, + // and set the stack locker field in the monitor. + m->set_stack_locker(mark.locker()); + m->set_anonymous_owner(); + } // TODO-FIXME: assert BasicLock->dhw != 0. // Must preserve store ordering. The monitor state must @@ -2050,7 +2080,7 @@ void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_ const intptr_t hash = UseObjectMonitorTable ? monitor->hash() : monitor->header().hash(); ResourceMark rm; out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(monitor), - monitor->is_busy(), hash != 0, monitor->owner() != nullptr, + monitor->is_busy(), hash != 0, monitor->has_owner(), p2i(obj), obj == nullptr ? "" : obj->klass()->external_name()); if (monitor->is_busy()) { out->print(" (%s)", monitor->is_busy_to_string(&ss)); diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index 5b171560ee173..d3d1f4ee4ce39 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -137,7 +137,7 @@ class ObjectSynchronizer : AllStatic { private: // Shared implementation between the different LockingMode. - static ObjectMonitor* inflate_impl(oop obj, const InflateCause cause); + static ObjectMonitor* inflate_impl(JavaThread* locking_thread, oop obj, const InflateCause cause); public: // This version is only for internal use @@ -168,10 +168,13 @@ class ObjectSynchronizer : AllStatic { template static void owned_monitors_iterate_filtered(MonitorClosure* closure, OwnerFilter filter); - // Iterate ObjectMonitors where the owner == thread; this does NOT include - // ObjectMonitors where owner is set to a stack lock address in thread. + // Iterate ObjectMonitors where the owner is thread; this does NOT include + // ObjectMonitors where the owner is anonymous. static void owned_monitors_iterate(MonitorClosure* m, JavaThread* thread); + // Iterate ObjectMonitors where the owner is vthread. + static void owned_monitors_iterate(MonitorClosure* m, oop vthread); + // Iterate ObjectMonitors owned by any thread. static void owned_monitors_iterate(MonitorClosure* closure); @@ -229,11 +232,15 @@ class ObjectSynchronizer : AllStatic { // have to pass through, and we must also be able to deal with // asynchronous exceptions. The caller is responsible for checking // the thread's pending exception if needed. +// When using ObjectLocker the top native frames in the stack will +// not be seen in case we attempt preemption, since we start walking +// from the last Java anchor, so we disable it with NoPreemptMark. class ObjectLocker : public StackObj { private: JavaThread* _thread; Handle _obj; BasicLock _lock; + NoPreemptMark _npm; public: ObjectLocker(Handle obj, JavaThread* current); ~ObjectLocker(); diff --git a/src/hotspot/share/runtime/threadIdentifier.cpp b/src/hotspot/share/runtime/threadIdentifier.cpp index ab250bbf6c347..d63033e77868c 100644 --- a/src/hotspot/share/runtime/threadIdentifier.cpp +++ b/src/hotspot/share/runtime/threadIdentifier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,22 @@ #include "runtime/atomic.hpp" #include "runtime/threadIdentifier.hpp" -static volatile int64_t next_thread_id = 2; // starting at 2, excluding the primordial thread id +// starting at 3, excluding reserved values defined in ObjectMonitor.hpp +static const int64_t INITIAL_TID = 3; +static volatile int64_t next_thread_id = INITIAL_TID; + +int64_t ThreadIdentifier::initial() { + return INITIAL_TID; +} int64_t ThreadIdentifier::unsafe_offset() { return reinterpret_cast(&next_thread_id); } +int64_t ThreadIdentifier::current() { + return Atomic::load(&next_thread_id); +} + int64_t ThreadIdentifier::next() { int64_t next_tid; do { diff --git a/src/hotspot/share/runtime/threadIdentifier.hpp b/src/hotspot/share/runtime/threadIdentifier.hpp index 44d34d880915a..b4803ae22f1f5 100644 --- a/src/hotspot/share/runtime/threadIdentifier.hpp +++ b/src/hotspot/share/runtime/threadIdentifier.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,9 @@ class ThreadIdentifier : AllStatic { public: static int64_t next(); + static int64_t current(); static int64_t unsafe_offset(); + static int64_t initial(); }; #endif // SHARE_RUNTIME_THREADIDENTIFIER_HPP diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 76ee237ae5ebe..29386a0c36895 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -166,8 +166,10 @@ static void create_initial_thread(Handle thread_group, JavaThread* thread, string, CHECK); - JFR_ONLY(assert(JFR_JVM_THREAD_ID(thread) == static_cast(java_lang_Thread::thread_id(thread_oop())), - "initial tid mismatch");) + DEBUG_ONLY(int64_t main_thread_tid = java_lang_Thread::thread_id(thread_oop());) + assert(main_thread_tid == ThreadIdentifier::initial(), ""); + assert(main_thread_tid == thread->lock_id(), ""); + JFR_ONLY(assert(JFR_JVM_THREAD_ID(thread) == static_cast(main_thread_tid), "initial tid mismatch");) // Set thread status to running since main thread has // been started and running. @@ -535,6 +537,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { main_thread->set_active_handles(JNIHandleBlock::allocate_block()); MACOS_AARCH64_ONLY(main_thread->init_wx()); + // Set the lock_id now since we will run Java code before the Thread instance + // is even created. The same value will be assigned to the Thread instance on init. + main_thread->set_lock_id(ThreadIdentifier::next()); + if (!Thread::set_as_starting_thread(main_thread)) { vm_shutdown_during_initialization( "Failed necessary internal allocation. Out of swap space"); @@ -586,6 +592,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { return status; } + ObjectMonitor::Initialize2(); + JFR_ONLY(Jfr::on_create_vm_1();) // Should be done after the heap is fully created @@ -1231,35 +1239,16 @@ GrowableArray* Threads::get_pending_threads(ThreadsList * t_list, } #endif // INCLUDE_JVMTI -JavaThread *Threads::owning_thread_from_monitor_owner(ThreadsList * t_list, - address owner) { - assert(LockingMode != LM_LIGHTWEIGHT, "Not with new lightweight locking"); - // null owner means not locked so we can skip the search - if (owner == nullptr) return nullptr; - - for (JavaThread* p : *t_list) { - // first, see if owner is the address of a Java thread - if (owner == (address)p) return p; - } +JavaThread *Threads::owning_thread_from_stacklock(ThreadsList * t_list, address basicLock) { + assert(LockingMode == LM_LEGACY, "Not with new lightweight locking"); - // Cannot assert on lack of success here since this function may be - // used by code that is trying to report useful problem information - // like deadlock detection. - if (LockingMode == LM_MONITOR) return nullptr; - - // If we didn't find a matching Java thread and we didn't force use of - // heavyweight monitors, then the owner is the stack address of the - // Lock Word in the owning Java thread's stack. - // JavaThread* the_owner = nullptr; for (JavaThread* q : *t_list) { - if (q->is_lock_owned(owner)) { + if (q->is_lock_owned(basicLock)) { the_owner = q; break; } } - - // cannot assert on lack of success here; see above comment return the_owner; } @@ -1280,17 +1269,22 @@ JavaThread* Threads::owning_thread_from_object(ThreadsList * t_list, oop obj) { } JavaThread* Threads::owning_thread_from_monitor(ThreadsList* t_list, ObjectMonitor* monitor) { - if (LockingMode == LM_LIGHTWEIGHT) { - if (monitor->is_owner_anonymous()) { + if (monitor->has_anonymous_owner()) { + if (LockingMode == LM_LIGHTWEIGHT) { return owning_thread_from_object(t_list, monitor->object()); } else { - Thread* owner = reinterpret_cast(monitor->owner()); - assert(owner == nullptr || owner->is_Java_thread(), "only JavaThreads own monitors"); - return reinterpret_cast(owner); + assert(LockingMode == LM_LEGACY, "invariant"); + return owning_thread_from_stacklock(t_list, (address)monitor->stack_locker()); } } else { - address owner = (address)monitor->owner(); - return owning_thread_from_monitor_owner(t_list, owner); + JavaThread* the_owner = nullptr; + for (JavaThread* q : *t_list) { + if (monitor->has_owner(q)) { + the_owner = q; + break; + } + } + return the_owner; } } @@ -1342,17 +1336,10 @@ void Threads::print_on(outputStream* st, bool print_stacks, p->trace_stack(); } else { p->print_stack_on(st); - const oop thread_oop = p->threadObj(); - if (thread_oop != nullptr) { - if (p->is_vthread_mounted()) { - const oop vt = p->vthread(); - assert(vt != nullptr, "vthread should not be null when vthread is mounted"); - // JavaThread._vthread can refer to the carrier thread. Print only if _vthread refers to a virtual thread. - if (vt != thread_oop) { - st->print_cr(" Mounted virtual thread #" INT64_FORMAT, (int64_t)java_lang_Thread::thread_id(vt)); - p->print_vthread_stack_on(st); - } - } + if (p->is_vthread_mounted()) { + // _lock_id is the thread ID of the mounted virtual thread + st->print_cr(" Mounted virtual thread #" INT64_FORMAT, p->lock_id()); + p->print_vthread_stack_on(st); } } } diff --git a/src/hotspot/share/runtime/threads.hpp b/src/hotspot/share/runtime/threads.hpp index 39e307122c8b8..979b1dffe265e 100644 --- a/src/hotspot/share/runtime/threads.hpp +++ b/src/hotspot/share/runtime/threads.hpp @@ -134,9 +134,8 @@ class Threads: AllStatic { static GrowableArray* get_pending_threads(ThreadsList * t_list, int count, address monitor); - // Get owning Java thread from the monitor's owner field. - static JavaThread *owning_thread_from_monitor_owner(ThreadsList * t_list, - address owner); + // Get owning Java thread from the basicLock address. + static JavaThread *owning_thread_from_stacklock(ThreadsList * t_list, address basicLock); static JavaThread* owning_thread_from_object(ThreadsList* t_list, oop obj); static JavaThread* owning_thread_from_monitor(ThreadsList* t_list, ObjectMonitor* owner); diff --git a/src/hotspot/share/runtime/vframe.cpp b/src/hotspot/share/runtime/vframe.cpp index d93b380ce92db..41a46c86a7080 100644 --- a/src/hotspot/share/runtime/vframe.cpp +++ b/src/hotspot/share/runtime/vframe.cpp @@ -280,13 +280,14 @@ intptr_t* interpretedVFrame::locals_addr_at(int offset) const { } GrowableArray* interpretedVFrame::monitors() const { + bool heap_frame = stack_chunk() != nullptr; + frame f = !heap_frame ? _fr : stack_chunk()->derelativize(_fr); GrowableArray* result = new GrowableArray(5); - if (stack_chunk() == nullptr) { // no monitors in continuations - for (BasicObjectLock* current = (fr().previous_monitor_in_interpreter_frame(fr().interpreter_frame_monitor_begin())); - current >= fr().interpreter_frame_monitor_end(); - current = fr().previous_monitor_in_interpreter_frame(current)) { - result->push(new MonitorInfo(current->obj(), current->lock(), false, false)); - } + for (BasicObjectLock* current = (f.previous_monitor_in_interpreter_frame(f.interpreter_frame_monitor_begin())); + current >= f.interpreter_frame_monitor_end(); + current = f.previous_monitor_in_interpreter_frame(current)) { + oop owner = !heap_frame ? current->obj() : StackValue::create_stack_value_from_oop_location(stack_chunk(), (void*)current->obj_adr())->get_obj()(); + result->push(new MonitorInfo(owner, current->lock(), false, false)); } return result; } diff --git a/src/hotspot/share/runtime/vframe.inline.hpp b/src/hotspot/share/runtime/vframe.inline.hpp index 4630e695ce92d..e7f9edbc232d7 100644 --- a/src/hotspot/share/runtime/vframe.inline.hpp +++ b/src/hotspot/share/runtime/vframe.inline.hpp @@ -122,6 +122,13 @@ inline vframeStream::vframeStream(JavaThread* thread, bool stop_at_java_call_stu if (thread->is_vthread_mounted()) { _frame = vthread_carrier ? _thread->carrier_last_frame(&_reg_map) : _thread->vthread_last_frame(); + if (Continuation::is_continuation_enterSpecial(_frame)) { + // This can happen when calling async_get_stack_trace() and catching the target + // vthread at the JRT_BLOCK_END in freeze_internal() or when posting the Monitor + // Waited event after target vthread was preempted. Since all continuation frames + // are freezed we get the top frame from the stackChunk instead. + _frame = Continuation::last_frame(java_lang_VirtualThread::continuation(_thread->vthread()), &_reg_map); + } } else { _frame = _thread->last_frame(); } diff --git a/src/hotspot/share/runtime/vframe_hp.cpp b/src/hotspot/share/runtime/vframe_hp.cpp index 28b62c60fd9ba..49ed47835d6a3 100644 --- a/src/hotspot/share/runtime/vframe_hp.cpp +++ b/src/hotspot/share/runtime/vframe_hp.cpp @@ -235,7 +235,7 @@ StackValue *compiledVFrame::create_stack_value(ScopeValue *sv) const { } BasicLock* compiledVFrame::resolve_monitor_lock(Location location) const { - return StackValue::resolve_monitor_lock(&_fr, location); + return StackValue::resolve_monitor_lock(stack_chunk() == nullptr ? _fr : stack_chunk()->derelativize(_fr), location); } @@ -284,6 +284,7 @@ GrowableArray* compiledVFrame::monitors() const { // Replace the original values with any stores that have been // performed through compiledVFrame::update_monitors. + if (thread() == nullptr) return result; // Unmounted continuations have no thread so nothing to do. GrowableArrayView* list = JvmtiDeferredUpdates::deferred_locals(thread()); if (list != nullptr ) { // In real life this never happens or is typically a single element search diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index af9911e0fdc74..1f5d63a917e74 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -275,13 +275,13 @@ void VM_ThreadDump::doit_epilogue() { } } -// Hash table of void* to a list of ObjectMonitor* owned by the JavaThread. +// Hash table of int64_t to a list of ObjectMonitor* owned by the JavaThread. // The JavaThread's owner key is either a JavaThread* or a stack lock -// address in the JavaThread so we use "void*". +// address in the JavaThread so we use "int64_t". // class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { private: - static unsigned int ptr_hash(void* const& s1) { + static unsigned int ptr_hash(int64_t const& s1) { // 2654435761 = 2^32 * Phi (golden ratio) return (unsigned int)(((uint32_t)(uintptr_t)s1) * 2654435761u); } @@ -294,24 +294,24 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { // ResourceHashtable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. - typedef ResourceHashtable PtrTable; PtrTable* _ptrs; size_t _key_count; size_t _om_count; - void add_list(void* key, ObjectMonitorLinkedList* list) { + void add_list(int64_t key, ObjectMonitorLinkedList* list) { _ptrs->put(key, list); _key_count++; } - ObjectMonitorLinkedList* get_list(void* key) { + ObjectMonitorLinkedList* get_list(int64_t key) { ObjectMonitorLinkedList** listpp = _ptrs->get(key); return (listpp == nullptr) ? nullptr : *listpp; } void add(ObjectMonitor* monitor) { - void* key = monitor->owner(); + int64_t key = monitor->owner(); ObjectMonitorLinkedList* list = get_list(key); if (list == nullptr) { @@ -335,7 +335,7 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { ~ObjectMonitorsDump() { class CleanupObjectMonitorsDump: StackObj { public: - bool do_entry(void*& key, ObjectMonitorLinkedList*& list) { + bool do_entry(int64_t& key, ObjectMonitorLinkedList*& list) { list->clear(); // clear the LinkListNodes delete list; // then delete the LinkedList return true; @@ -350,7 +350,7 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { void do_monitor(ObjectMonitor* monitor) override { assert(monitor->has_owner(), "Expects only owned monitors"); - if (monitor->is_owner_anonymous()) { + if (monitor->has_anonymous_owner()) { // There's no need to collect anonymous owned monitors // because the caller of this code is only interested // in JNI owned monitors. @@ -368,7 +368,8 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { // Implements the ObjectMonitorsView interface void visit(MonitorClosure* closure, JavaThread* thread) override { - ObjectMonitorLinkedList* list = get_list(thread); + int64_t key = ObjectMonitor::owner_from(thread); + ObjectMonitorLinkedList* list = get_list(key); LinkedListIterator iter(list != nullptr ? list->head() : nullptr); while (!iter.is_empty()) { ObjectMonitor* monitor = *iter.next(); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 800102f238cdf..bc941534242e3 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -665,6 +665,7 @@ nonstatic_field(JavaThread, _vframe_array_head, vframeArray*) \ nonstatic_field(JavaThread, _vframe_array_last, vframeArray*) \ nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \ + nonstatic_field(JavaThread, _lock_id, int64_t) \ volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \ nonstatic_field(Thread, _resource_area, ResourceArea*) \ nonstatic_field(CompilerThread, _env, ciEnv*) \ @@ -785,7 +786,8 @@ \ volatile_nonstatic_field(ObjectMonitor, _metadata, uintptr_t) \ unchecked_nonstatic_field(ObjectMonitor, _object, sizeof(void *)) /* NOTE: no type */ \ - unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \ + volatile_nonstatic_field(ObjectMonitor, _owner, int64_t) \ + volatile_nonstatic_field(ObjectMonitor, _stack_locker, BasicLock*) \ volatile_nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \ volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \ nonstatic_field(ObjectMonitor, _contentions, int) \ @@ -2532,7 +2534,9 @@ declare_constant(InvocationCounter::count_shift) \ \ /* ObjectMonitor constants */ \ + declare_constant(ObjectMonitor::NO_OWNER) \ declare_constant(ObjectMonitor::ANONYMOUS_OWNER) \ + declare_constant(ObjectMonitor::DEFLATER_MARKER) \ //-------------------------------------------------------------------------------- // diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 10e1a804ad213..91bcce382f0ee 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -1875,9 +1875,12 @@ void ThreadDumper::dump_stack_refs(AbstractDumpWriter * writer) { // native frame blk.set_frame_number(depth); if (is_top_frame) { - // JNI locals for the top frame. - assert(_java_thread != nullptr, "impossible for unmounted vthread"); - _java_thread->active_handles()->oops_do(&blk); + // JNI locals for the top frame if mounted + assert(_java_thread != nullptr || jvf->method()->is_synchronized() + || jvf->method()->is_object_wait0(), "impossible for unmounted vthread"); + if (_java_thread != nullptr) { + _java_thread->active_handles()->oops_do(&blk); + } } else { if (last_entry_frame != nullptr) { // JNI locals for the entry frame diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index a1ad0a910f6d6..8cb8bf19bba00 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -464,23 +464,14 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list, } else if (waitingToLockMonitor != nullptr) { if (waitingToLockMonitor->has_owner()) { currentThread = Threads::owning_thread_from_monitor(t_list, waitingToLockMonitor); - if (currentThread == nullptr) { - // This function is called at a safepoint so the JavaThread - // that owns waitingToLockMonitor should be findable, but - // if it is not findable, then the previous currentThread is - // blocked permanently. We record this as a deadlock. - num_deadlocks++; - - // add this cycle to the deadlocks list - if (deadlocks == nullptr) { - deadlocks = cycle; - } else { - last->set_next(cycle); - } - last = cycle; - cycle = new DeadlockCycle(); - break; - } + // If currentThread is null we would like to know if the owner + // is an unmounted vthread (no JavaThread*), because if it's not, + // it would mean the previous currentThread is blocked permanently + // and we should record this as a deadlock. Since there is currently + // no fast way to determine if the owner is indeed an unmounted + // vthread we never record this as a deadlock. Note: unless there + // is a bug in the VM, or a thread exits without releasing monitors + // acquired through JNI, null should imply an unmounted vthread owner. } } else { if (concurrent_locks) { @@ -1053,8 +1044,8 @@ void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const // that owns waitingToLockMonitor should be findable, but // if it is not findable, then the previous currentThread is // blocked permanently. - st->print_cr("%s UNKNOWN_owner_addr=" PTR_FORMAT, owner_desc, - p2i(waitingToLockMonitor->owner())); + st->print_cr("%s UNKNOWN_owner_addr=" INT64_FORMAT, owner_desc, + waitingToLockMonitor->owner()); continue; } } else { diff --git a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java index a07a927e635ed..14f4e87448b31 100644 --- a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java +++ b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import jdk.internal.misc.Blocker; import static sun.nio.ch.EPoll.EPOLLIN; import static sun.nio.ch.EPoll.EPOLL_CTL_ADD; @@ -105,36 +104,71 @@ protected int doSelect(Consumer action, long timeout) int numEntries; processUpdateQueue(); processDeregisterQueue(); - try { - begin(blocking); - do { - long startTime = timedPoll ? System.nanoTime() : 0; - boolean attempted = Blocker.begin(blocking); - try { + if (Thread.currentThread().isVirtual()) { + numEntries = (timedPoll) + ? timedPoll(TimeUnit.MILLISECONDS.toNanos(to)) + : untimedPoll(blocking); + } else { + try { + begin(blocking); + do { + long startTime = timedPoll ? System.nanoTime() : 0; numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, to); - } finally { - Blocker.end(attempted); - } - if (numEntries == IOStatus.INTERRUPTED && timedPoll) { - // timed poll interrupted so need to adjust timeout - long adjust = System.nanoTime() - startTime; - to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust); - if (to <= 0) { - // timeout expired so no retry - numEntries = 0; + if (numEntries == IOStatus.INTERRUPTED && timedPoll) { + // timed poll interrupted so need to adjust timeout + long adjust = System.nanoTime() - startTime; + to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust); + if (to <= 0) { + // timeout expired so no retry + numEntries = 0; + } } - } - } while (numEntries == IOStatus.INTERRUPTED); - assert IOStatus.check(numEntries); - - } finally { - end(blocking); + } while (numEntries == IOStatus.INTERRUPTED); + } finally { + end(blocking); + } } + assert IOStatus.check(numEntries); + processDeregisterQueue(); return processEvents(numEntries, action); } + /** + * If blocking, parks the current virtual thread until a file descriptor is polled + * or the thread is interrupted. + */ + private int untimedPoll(boolean block) throws IOException { + int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0); + if (block) { + while (numEntries == 0 && !Thread.currentThread().isInterrupted()) { + Poller.pollSelector(epfd, 0); + numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0); + } + } + return numEntries; + } + + /** + * Parks the current virtual thread until a file descriptor is polled, or the thread + * is interrupted, for up to the specified waiting time. + */ + private int timedPoll(long nanos) throws IOException { + long startNanos = System.nanoTime(); + int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0); + while (numEntries == 0 && !Thread.currentThread().isInterrupted()) { + long remainingNanos = nanos - (System.nanoTime() - startNanos); + if (remainingNanos <= 0) { + // timeout + break; + } + Poller.pollSelector(epfd, remainingNanos); + numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0); + } + return numEntries; + } + /** * Process changes to the interest ops. */ diff --git a/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java b/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java index 6abc9e31879dd..b1a781d71446b 100644 --- a/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java +++ b/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import jdk.internal.misc.Blocker; import static sun.nio.ch.KQueue.EVFILT_READ; import static sun.nio.ch.KQueue.EVFILT_WRITE; @@ -109,36 +108,71 @@ protected int doSelect(Consumer action, long timeout) int numEntries; processUpdateQueue(); processDeregisterQueue(); - try { - begin(blocking); - do { - long startTime = timedPoll ? System.nanoTime() : 0; - boolean attempted = Blocker.begin(blocking); - try { + if (Thread.currentThread().isVirtual()) { + numEntries = (timedPoll) + ? timedPoll(TimeUnit.MILLISECONDS.toNanos(to)) + : untimedPoll(blocking); + } else { + try { + begin(blocking); + do { + long startTime = timedPoll ? System.nanoTime() : 0; numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, to); - } finally { - Blocker.end(attempted); - } - if (numEntries == IOStatus.INTERRUPTED && timedPoll) { - // timed poll interrupted so need to adjust timeout - long adjust = System.nanoTime() - startTime; - to -= TimeUnit.NANOSECONDS.toMillis(adjust); - if (to <= 0) { - // timeout expired so no retry - numEntries = 0; + if (numEntries == IOStatus.INTERRUPTED && timedPoll) { + // timed poll interrupted so need to adjust timeout + long adjust = System.nanoTime() - startTime; + to -= TimeUnit.NANOSECONDS.toMillis(adjust); + if (to <= 0) { + // timeout expired so no retry + numEntries = 0; + } } - } - } while (numEntries == IOStatus.INTERRUPTED); - assert IOStatus.check(numEntries); - - } finally { - end(blocking); + } while (numEntries == IOStatus.INTERRUPTED); + } finally { + end(blocking); + } } + assert IOStatus.check(numEntries); + processDeregisterQueue(); return processEvents(numEntries, action); } + /** + * If blocking, parks the current virtual thread until a file descriptor is polled + * or the thread is interrupted. + */ + private int untimedPoll(boolean block) throws IOException { + int numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0); + if (block) { + while (numEntries == 0 && !Thread.currentThread().isInterrupted()) { + Poller.pollSelector(kqfd, 0); + numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0); + } + } + return numEntries; + } + + /** + * Parks the current virtual thread until a file descriptor is polled, or the thread + * is interrupted, for up to the specified waiting time. + */ + private int timedPoll(long nanos) throws IOException { + long startNanos = System.nanoTime(); + int numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0); + while (numEntries == 0 && !Thread.currentThread().isInterrupted()) { + long remainingNanos = nanos - (System.nanoTime() - startNanos); + if (remainingNanos <= 0) { + // timeout + break; + } + Poller.pollSelector(kqfd, remainingNanos); + numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0); + } + return numEntries; + } + /** * Process changes to the interest ops. */ diff --git a/src/java.base/share/classes/java/io/ByteArrayOutputStream.java b/src/java.base/share/classes/java/io/ByteArrayOutputStream.java index 8f5ff41957e70..020cd1f878a9e 100644 --- a/src/java.base/share/classes/java/io/ByteArrayOutputStream.java +++ b/src/java.base/share/classes/java/io/ByteArrayOutputStream.java @@ -159,16 +159,8 @@ public void writeBytes(byte[] b) { * @throws NullPointerException if {@code out} is {@code null}. * @throws IOException if an I/O error occurs. */ - public void writeTo(OutputStream out) throws IOException { - if (Thread.currentThread().isVirtual()) { - byte[] bytes; - synchronized (this) { - bytes = Arrays.copyOf(buf, count); - } - out.write(bytes); - } else synchronized (this) { - out.write(buf, 0, count); - } + public synchronized void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); } /** diff --git a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java index 7909f05304268..aa9c8f312372e 100644 --- a/src/java.base/share/classes/java/lang/Object.java +++ b/src/java.base/share/classes/java/lang/Object.java @@ -25,7 +25,6 @@ package java.lang; -import jdk.internal.misc.Blocker; import jdk.internal.vm.annotation.IntrinsicCandidate; /** @@ -374,21 +373,20 @@ public final void wait() throws InterruptedException { * @see #wait(long, int) */ public final void wait(long timeoutMillis) throws InterruptedException { - if (!Thread.currentThread().isVirtual()) { - wait0(timeoutMillis); - return; + if (timeoutMillis < 0) { + throw new IllegalArgumentException("timeout value is negative"); } - // virtual thread waiting - boolean attempted = Blocker.begin(); - try { + if (Thread.currentThread() instanceof VirtualThread vthread) { + try { + wait0(timeoutMillis); + } catch (InterruptedException e) { + // virtual thread's interrupt status needs to be cleared + vthread.getAndClearInterrupt(); + throw e; + } + } else { wait0(timeoutMillis); - } catch (InterruptedException e) { - // virtual thread's interrupt status needs to be cleared - Thread.currentThread().getAndClearInterrupt(); - throw e; - } finally { - Blocker.end(attempted); } } diff --git a/src/java.base/share/classes/java/lang/PinnedThreadPrinter.java b/src/java.base/share/classes/java/lang/PinnedThreadPrinter.java deleted file mode 100644 index a9b40d028f531..0000000000000 --- a/src/java.base/share/classes/java/lang/PinnedThreadPrinter.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package java.lang; - -import java.io.PrintStream; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import static java.lang.StackWalker.Option.*; -import jdk.internal.access.JavaIOPrintStreamAccess; -import jdk.internal.access.SharedSecrets; -import jdk.internal.misc.InternalLock; -import jdk.internal.vm.Continuation; - -/** - * Helper class to print the virtual thread stack trace when pinned. - * - * The class maintains a ClassValue with the hashes of stack traces that are pinned by - * code in that Class. This is used to avoid printing the same stack trace many times. - */ -class PinnedThreadPrinter { - private static final JavaIOPrintStreamAccess JIOPSA = SharedSecrets.getJavaIOPrintStreamAccess(); - private static final StackWalker STACK_WALKER; - static { - var options = Set.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE); - PrivilegedAction pa = () -> - LiveStackFrame.getStackWalker(options, VirtualThread.continuationScope()); - @SuppressWarnings("removal") - var stackWalker = AccessController.doPrivileged(pa); - STACK_WALKER = stackWalker; - } - - private static final ClassValue HASHES = new ClassValue<>() { - @Override - protected Hashes computeValue(Class type) { - return new Hashes(); - } - }; - - @SuppressWarnings("serial") - private static class Hashes extends LinkedHashMap { - boolean add(int hash) { - return (putIfAbsent(hash, Boolean.TRUE) == null); - } - @Override - protected boolean removeEldestEntry(Map.Entry oldest) { - // limit number of hashes - return size() > 8; - } - } - - /** - * Returns a hash of the given stack trace. The hash is based on the class, - * method and bytecode index. - */ - private static int hash(List stack) { - int hash = 0; - for (LiveStackFrame frame : stack) { - hash = (31 * hash) + Objects.hash(frame.getDeclaringClass(), - frame.getMethodName(), - frame.getByteCodeIndex()); - } - return hash; - } - - /** - * Returns true if the frame is native, a class initializer, or holds monitors. - */ - private static boolean isInterestingFrame(LiveStackFrame f) { - return f.isNativeMethod() - || "".equals(f.getMethodName()) - || (f.getMonitors().length > 0); - } - - /** - * Prints the current thread's stack trace. - * - * @param printAll true to print all stack frames, false to only print the - * frames that are native or holding a monitor - */ - static void printStackTrace(PrintStream out, Continuation.Pinned reason, boolean printAll) { - List stack = STACK_WALKER.walk(s -> - s.map(f -> (LiveStackFrame) f) - .filter(f -> f.getDeclaringClass() != PinnedThreadPrinter.class) - .collect(Collectors.toList()) - ); - Object lockObj = JIOPSA.lock(out); - if (lockObj instanceof InternalLock lock && lock.tryLock()) { - try { - // find the closest frame that is causing the thread to be pinned - stack.stream() - .filter(f -> isInterestingFrame(f)) - .map(LiveStackFrame::getDeclaringClass) - .findFirst() - .ifPresentOrElse(klass -> { - // print the stack trace if not already seen - int hash = hash(stack); - if (HASHES.get(klass).add(hash)) { - printStackTrace(out, reason, stack, printAll); - } - }, () -> printStackTrace(out, reason, stack, true)); // not found - - } finally { - lock.unlock(); - } - } - } - - private static void printStackTrace(PrintStream out, - Continuation.Pinned reason, - List stack, - boolean printAll) { - out.format("%s reason:%s%n", Thread.currentThread(), reason); - for (LiveStackFrame frame : stack) { - var ste = frame.toStackTraceElement(); - int monitorCount = frame.getMonitors().length; - if (monitorCount > 0) { - out.format(" %s <== monitors:%d%n", ste, monitorCount); - } else if (printAll || isInterestingFrame(frame)) { - out.format(" %s%n", ste); - } - } - } -} diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java index 07b3c7c9bd2d1..b8ffedac20a1f 100644 --- a/src/java.base/share/classes/java/lang/Thread.java +++ b/src/java.base/share/classes/java/lang/Thread.java @@ -636,12 +636,17 @@ public static void onSpinWait() {} */ static final int NO_INHERIT_THREAD_LOCALS = 1 << 2; + /** + * Thread identifier assigned to the primordial thread. + */ + static final long PRIMORDIAL_TID = 3; + /** * Helper class to generate thread identifiers. The identifiers start at - * 2 as this class cannot be used during early startup to generate the - * identifier for the primordial thread. The counter is off-heap and - * shared with the VM to allow it assign thread identifiers to non-Java - * threads. + * {@link Thread#PRIMORDIAL_TID} +1 as this class cannot be used during + * early startup to generate the identifier for the primordial thread. The + * counter is off-heap and shared with the VM to allow it to assign thread + * identifiers to non-Java threads. * See Thread initialization. */ private static class ThreadIdentifiers { @@ -722,10 +727,11 @@ private static ClassLoader contextClassLoader(Thread parent) { } if (attached && VM.initLevel() < 1) { - this.tid = 1; // primordial thread + this.tid = PRIMORDIAL_TID; // primordial thread } else { this.tid = ThreadIdentifiers.next(); } + this.name = (name != null) ? name : genThreadName(); if (acc != null) { diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index f76a6eec9140e..efe42488c6db5 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -41,7 +41,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import jdk.internal.event.VirtualThreadEndEvent; -import jdk.internal.event.VirtualThreadPinnedEvent; import jdk.internal.event.VirtualThreadStartEvent; import jdk.internal.event.VirtualThreadSubmitFailedEvent; import jdk.internal.misc.CarrierThread; @@ -70,12 +69,12 @@ final class VirtualThread extends BaseVirtualThread { private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads"); private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler(); private static final ScheduledExecutorService[] DELAYED_TASK_SCHEDULERS = createDelayedTaskSchedulers(); - private static final int TRACE_PINNING_MODE = tracePinningMode(); private static final long STATE = U.objectFieldOffset(VirtualThread.class, "state"); private static final long PARK_PERMIT = U.objectFieldOffset(VirtualThread.class, "parkPermit"); private static final long CARRIER_THREAD = U.objectFieldOffset(VirtualThread.class, "carrierThread"); private static final long TERMINATION = U.objectFieldOffset(VirtualThread.class, "termination"); + private static final long ON_WAITING_LIST = U.objectFieldOffset(VirtualThread.class, "onWaitingList"); // scheduler and continuation private final Executor scheduler; @@ -106,6 +105,21 @@ final class VirtualThread extends BaseVirtualThread { * TIMED_PARKED -> UNPARKED // unparked, may be scheduled to continue * TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier * + * RUNNING -> BLOCKING // blocking on monitor enter + * BLOCKING -> BLOCKED // blocked on monitor enter + * BLOCKED -> UNBLOCKED // unblocked, may be scheduled to continue + * UNBLOCKED -> RUNNING // continue execution after blocked on monitor enter + * + * RUNNING -> WAITING // transitional state during wait on monitor + * WAITING -> WAITED // waiting on monitor + * WAITED -> BLOCKED // notified, waiting to be unblocked by monitor owner + * WAITED -> UNBLOCKED // timed-out/interrupted + * + * RUNNING -> TIMED_WAITING // transition state during timed-waiting on monitor + * TIMED_WAITING -> TIMED_WAITED // timed-waiting on monitor + * TIMED_WAITED -> BLOCKED // notified, waiting to be unblocked by monitor owner + * TIMED_WAITED -> UNBLOCKED // timed-out/interrupted + * * RUNNING -> YIELDING // Thread.yield * YIELDING -> YIELDED // cont.yield successful, may be scheduled to continue * YIELDING -> RUNNING // cont.yield failed @@ -128,18 +142,44 @@ final class VirtualThread extends BaseVirtualThread { private static final int YIELDING = 10; private static final int YIELDED = 11; // unmounted but runnable + // monitor enter + private static final int BLOCKING = 12; + private static final int BLOCKED = 13; // unmounted + private static final int UNBLOCKED = 14; // unmounted but runnable + + // monitor wait/timed-wait + private static final int WAITING = 15; + private static final int WAIT = 16; // waiting in Object.wait + private static final int TIMED_WAITING = 17; + private static final int TIMED_WAIT = 18; // waiting in timed-Object.wait + private static final int TERMINATED = 99; // final state // can be suspended from scheduling when unmounted private static final int SUSPENDED = 1 << 8; - // parking permit + // parking permit made available by LockSupport.unpark private volatile boolean parkPermit; - // timeout for timed-park, in nanoseconds, only accessed on current/carrier thread - private long parkTimeout; + // blocking permit made available by unblocker thread when another thread exits monitor + private volatile boolean blockPermit; + + // true when on the list of virtual threads waiting to be unblocked + private volatile boolean onWaitingList; - // timer task for timed-park, only accessed on current/carrier thread + // next virtual thread on the list of virtual threads waiting to be unblocked + private volatile VirtualThread next; + + // notified by Object.notify/notifyAll while waiting in Object.wait + private volatile boolean notified; + + // timed-wait support + private byte timedWaitSeqNo; + + // timeout for timed-park and timed-wait, only accessed on current/carrier thread + private long timeout; + + // timer task for timed-park and timed-wait, only accessed on current/carrier thread private Future timeoutTask; // carrier thread when mounted, accessed by VM @@ -202,18 +242,6 @@ private static class VThreadContinuation extends Continuation { } @Override protected void onPinned(Continuation.Pinned reason) { - if (TRACE_PINNING_MODE > 0) { - boolean printAll = (TRACE_PINNING_MODE == 1); - VirtualThread vthread = (VirtualThread) Thread.currentThread(); - int oldState = vthread.state(); - try { - // avoid printing when in transition states - vthread.setState(RUNNING); - PinnedThreadPrinter.printStackTrace(System.out, reason, printAll); - } finally { - vthread.setState(oldState); - } - } } private static Runnable wrap(VirtualThread vthread, Runnable task) { return new Runnable() { @@ -245,16 +273,20 @@ private void runContinuation() { // set state to RUNNING int initialState = state(); - if (initialState == STARTED || initialState == UNPARKED || initialState == YIELDED) { + if (initialState == STARTED || initialState == UNPARKED + || initialState == UNBLOCKED || initialState == YIELDED) { // newly started or continue after parking/blocking/Thread.yield if (!compareAndSetState(initialState, RUNNING)) { return; } - // consume permit when continuing after parking. If continuing after a - // timed-park then the timeout task is cancelled. + // consume permit when continuing after parking or blocking. If continue + // after a timed-park or timed-wait then the timeout task is cancelled. if (initialState == UNPARKED) { cancelTimeoutTask(); setParkPermit(false); + } else if (initialState == UNBLOCKED) { + cancelTimeoutTask(); + blockPermit = false; } } else { // not runnable @@ -275,8 +307,8 @@ private void runContinuation() { } /** - * Cancel timeout task when continuing after a timed-park. The - * timeout task may be executing, or may have already completed. + * Cancel timeout task when continuing after timed-park or timed-wait. + * The timeout task may be executing, or may have already completed. */ private void cancelTimeoutTask() { if (timeoutTask != null) { @@ -511,7 +543,7 @@ private boolean yieldContinuation() { /** * Invoked in the context of the carrier thread after the Continuation yields when - * parking or Thread.yield. + * parking, blocking on monitor enter, Object.wait, or Thread.yield. */ private void afterYield() { assert carrierThread == null; @@ -530,8 +562,8 @@ private void afterYield() { setState(newState = PARKED); } else { // schedule unpark - assert parkTimeout > 0; - timeoutTask = schedule(this::unpark, parkTimeout, NANOSECONDS); + assert timeout > 0; + timeoutTask = schedule(this::unpark, timeout, NANOSECONDS); setState(newState = TIMED_PARKED); } @@ -556,6 +588,56 @@ private void afterYield() { return; } + // blocking on monitorenter + if (s == BLOCKING) { + setState(BLOCKED); + + // may have been unblocked while blocking + if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) { + // lazy submit if local queue is empty + lazySubmitRunContinuation(); + } + return; + } + + // Object.wait + if (s == WAITING || s == TIMED_WAITING) { + int newState; + if (s == WAITING) { + setState(newState = WAIT); + } else { + // For timed-wait, a timeout task is scheduled to execute. The timeout + // task will change the thread state to UNBLOCKED and submit the thread + // to the scheduler. A sequence number is used to ensure that the timeout + // task only unblocks the thread for this timed-wait. We synchronize with + // the timeout task to coordinate access to the sequence number and to + // ensure the timeout task doesn't execute until the thread has got to + // the TIMED_WAIT state. + assert timeout > 0; + synchronized (timedWaitLock()) { + byte seqNo = ++timedWaitSeqNo; + timeoutTask = schedule(() -> waitTimeoutExpired(seqNo), timeout, MILLISECONDS); + setState(newState = TIMED_WAIT); + } + } + + // may have been notified while in transition to wait state + if (notified && compareAndSetState(newState, BLOCKED)) { + // may have even been unblocked already + if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) { + submitRunContinuation(); + } + return; + } + + // may have been interrupted while in transition to wait state + if (interrupted && compareAndSetState(newState, UNBLOCKED)) { + submitRunContinuation(); + return; + } + return; + } + assert false; } @@ -695,7 +777,7 @@ void parkNanos(long nanos) { // park the thread, afterYield will schedule the thread to unpark boolean yielded = false; - setParkTimeout(nanos); + timeout = nanos; setState(TIMED_PARKING); try { yielded = yieldContinuation(); @@ -727,14 +809,6 @@ void parkNanos(long nanos) { private void parkOnCarrierThread(boolean timed, long nanos) { assert state() == RUNNING; - VirtualThreadPinnedEvent event; - try { - event = new VirtualThreadPinnedEvent(); - event.begin(); - } catch (OutOfMemoryError e) { - event = null; - } - setState(timed ? TIMED_PINNED : PINNED); try { if (!parkPermit) { @@ -751,15 +825,18 @@ private void parkOnCarrierThread(boolean timed, long nanos) { // consume parking permit setParkPermit(false); - if (event != null) { - try { - event.commit(); - } catch (OutOfMemoryError e) { - // ignore - } - } + // JFR jdk.VirtualThreadPinned event + postPinnedEvent("LockSupport.park"); } + /** + * Call into VM when pinned to record a JFR jdk.VirtualThreadPinned event. + * Recording the event in the VM avoids having JFR event recorded in Java + * with the same name, but different ID, to events recorded by the VM. + */ + @Hidden + private static native void postPinnedEvent(String op); + /** * Re-enables this virtual thread for scheduling. If this virtual thread is parked * then its task is scheduled to continue, otherwise its next call to {@code park} or @@ -796,6 +873,49 @@ void unpark() { } } + /** + * Invoked by unblocker thread to unblock this virtual thread. + */ + private void unblock() { + assert !Thread.currentThread().isVirtual(); + blockPermit = true; + if (state() == BLOCKED && compareAndSetState(BLOCKED, UNBLOCKED)) { + submitRunContinuation(); + } + } + + /** + * Invoked by timer thread when wait timeout for virtual thread has expired. + * If the virtual thread is in timed-wait then this method will unblock the thread + * and submit its task so that it continues and attempts to reenter the monitor. + * This method does nothing if the thread has been woken by notify or interrupt. + */ + private void waitTimeoutExpired(byte seqNo) { + assert !Thread.currentThread().isVirtual(); + for (;;) { + boolean unblocked = false; + synchronized (timedWaitLock()) { + if (seqNo != timedWaitSeqNo) { + // this timeout task is for a past timed-wait + return; + } + int s = state(); + if (s == TIMED_WAIT) { + unblocked = compareAndSetState(TIMED_WAIT, UNBLOCKED); + } else if (s != (TIMED_WAIT | SUSPENDED)) { + // notified or interrupted, no longer waiting + return; + } + } + if (unblocked) { + submitRunContinuation(); + return; + } + // need to retry when thread is suspended in time-wait + Thread.yield(); + } + } + /** * Attempts to yield the current virtual thread (Thread.yield). */ @@ -926,6 +1046,12 @@ public void interrupt() { // make available parking permit, unpark thread if parked unpark(); + // if thread is waiting in Object.wait then schedule to try to reenter + int s = state(); + if ((s == WAIT || s == TIMED_WAIT) && compareAndSetState(s, UNBLOCKED)) { + submitRunContinuation(); + } + } else { interrupted = true; carrierThread.setInterrupt(); @@ -970,6 +1096,7 @@ Thread.State threadState() { return Thread.State.RUNNABLE; } case UNPARKED: + case UNBLOCKED: case YIELDED: // runnable, not mounted return Thread.State.RUNNABLE; @@ -992,15 +1119,22 @@ Thread.State threadState() { return Thread.State.RUNNABLE; case PARKING: case TIMED_PARKING: + case WAITING: + case TIMED_WAITING: case YIELDING: // runnable, in transition return Thread.State.RUNNABLE; case PARKED: case PINNED: + case WAIT: return Thread.State.WAITING; case TIMED_PARKED: case TIMED_PINNED: + case TIMED_WAIT: return Thread.State.TIMED_WAITING; + case BLOCKING: + case BLOCKED: + return Thread.State.BLOCKED; case TERMINATED: return Thread.State.TERMINATED; default: @@ -1046,13 +1180,13 @@ private StackTraceElement[] tryGetStackTrace() { case RUNNING, PINNED, TIMED_PINNED -> { return null; // mounted } - case PARKED, TIMED_PARKED -> { + case PARKED, TIMED_PARKED, BLOCKED, WAIT, TIMED_WAIT -> { // unmounted, not runnable } - case UNPARKED, YIELDED -> { + case UNPARKED, UNBLOCKED, YIELDED -> { // unmounted, runnable } - case PARKING, TIMED_PARKING, YIELDING -> { + case PARKING, TIMED_PARKING, BLOCKING, YIELDING, WAITING, TIMED_WAITING -> { return null; // in transition } default -> throw new InternalError("" + initialState); @@ -1073,7 +1207,7 @@ private StackTraceElement[] tryGetStackTrace() { setState(initialState); } boolean resubmit = switch (initialState) { - case UNPARKED, YIELDED -> { + case UNPARKED, UNBLOCKED, YIELDED -> { // resubmit as task may have run while suspended yield true; } @@ -1081,6 +1215,15 @@ private StackTraceElement[] tryGetStackTrace() { // resubmit if unparked while suspended yield parkPermit && compareAndSetState(initialState, UNPARKED); } + case BLOCKED -> { + // resubmit if unblocked while suspended + yield blockPermit && compareAndSetState(BLOCKED, UNBLOCKED); + } + case WAIT, TIMED_WAIT -> { + // resubmit if notified or interrupted while waiting (Object.wait) + // waitTimeoutExpired will retry if the timed expired when suspended + yield (notified || interrupted) && compareAndSetState(initialState, UNBLOCKED); + } default -> throw new InternalError(); }; if (resubmit) { @@ -1175,6 +1318,14 @@ private Object carrierThreadAccessLock() { return interruptLock; } + /** + * Returns a lock object for coordinating timed-wait setup and timeout handling. + */ + private Object timedWaitLock() { + // use this object for now to avoid the overhead of introducing another lock + return runContinuation; + } + /** * Disallow the current thread be suspended or preempted. */ @@ -1205,6 +1356,10 @@ private boolean compareAndSetState(int expectedValue, int newValue) { return U.compareAndSetInt(this, STATE, expectedValue, newValue); } + private boolean compareAndSetOnWaitingList(boolean expectedValue, boolean newValue) { + return U.compareAndSetBoolean(this, ON_WAITING_LIST, expectedValue, newValue); + } + private void setParkPermit(boolean newValue) { if (parkPermit != newValue) { parkPermit = newValue; @@ -1219,10 +1374,6 @@ private boolean getAndSetParkPermit(boolean newValue) { } } - private void setParkTimeout(long timeout) { - parkTimeout = timeout; - } - private void setCarrierThread(Thread carrier) { // U.putReferenceRelease(this, CARRIER_THREAD, carrier); this.carrierThread = carrier; @@ -1255,9 +1406,6 @@ private void setCarrierThread(Thread carrier) { // ensure VTHREAD_GROUP is created, may be accessed by JVMTI var group = Thread.virtualThreadGroup(); - - // ensure VirtualThreadPinnedEvent is loaded/initialized - U.ensureClassInitialized(VirtualThreadPinnedEvent.class); } /** @@ -1338,18 +1486,37 @@ private static ScheduledExecutorService[] createDelayedTaskSchedulers() { } /** - * Reads the value of the jdk.tracePinnedThreads property to determine if stack - * traces should be printed when a carrier thread is pinned when a virtual thread - * attempts to park. + * Schedule virtual threads that are ready to be scheduled after they blocked on + * monitor enter. */ - private static int tracePinningMode() { - String propValue = GetPropertyAction.privilegedGetProperty("jdk.tracePinnedThreads"); - if (propValue != null) { - if (propValue.length() == 0 || "full".equalsIgnoreCase(propValue)) - return 1; - if ("short".equalsIgnoreCase(propValue)) - return 2; + private static void unblockVirtualThreads() { + while (true) { + VirtualThread vthread = takeVirtualThreadListToUnblock(); + while (vthread != null) { + assert vthread.onWaitingList; + VirtualThread nextThread = vthread.next; + + // remove from list and unblock + vthread.next = null; + boolean changed = vthread.compareAndSetOnWaitingList(true, false); + assert changed; + vthread.unblock(); + + vthread = nextThread; + } } - return 0; + } + + /** + * Retrieves the list of virtual threads that are waiting to be unblocked, waiting + * if necessary until a list of one or more threads becomes available. + */ + private static native VirtualThread takeVirtualThreadListToUnblock(); + + static { + var unblocker = InnocuousThread.newThread("VirtualThread-unblocker", + VirtualThread::unblockVirtualThreads); + unblocker.setDaemon(true); + unblocker.start(); } } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java index faf8302206ede..c3dc973369db0 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -231,7 +231,7 @@ private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num } static final ReferencedKeySet internTable = - ReferencedKeySet.create(false, true, new Supplier<>() { + ReferencedKeySet.create(false, new Supplier<>() { @Override public Map, ReferenceKey> get() { return new ConcurrentHashMap<>(512); diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index cca16a18580ac..2f5b8f2407e46 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -1134,7 +1134,7 @@ public void accept(CodeBuilder cb) { }; static final ReferencedKeyMap> CACHE = - ReferencedKeyMap.create(true, true, + ReferencedKeyMap.create(true, new Supplier<>() { @Override public Map, SoftReference> get() { diff --git a/src/java.base/share/classes/java/lang/ref/Finalizer.java b/src/java.base/share/classes/java/lang/ref/Finalizer.java index e5db2300d16b1..d0c04d7345dd2 100644 --- a/src/java.base/share/classes/java/lang/ref/Finalizer.java +++ b/src/java.base/share/classes/java/lang/ref/Finalizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ final class Finalizer extends FinalReference { /* Package-private; must same package as the Reference class */ - private static ReferenceQueue queue = new NativeReferenceQueue<>(); + private static ReferenceQueue queue = new ReferenceQueue<>(); /** Head of doubly linked list of Finalizers awaiting finalization. */ private static Finalizer unfinalized = null; diff --git a/src/java.base/share/classes/java/lang/ref/NativeReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/NativeReferenceQueue.java deleted file mode 100644 index 1161044f0c29b..0000000000000 --- a/src/java.base/share/classes/java/lang/ref/NativeReferenceQueue.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.ref; - -/** - * An implementation of a ReferenceQueue that uses native monitors. - * The use of java.util.concurrent.lock locks interacts with various mechanisms, - * such as virtual threads and ForkJoinPool, that might not be appropriate for some - * low-level mechanisms, in particular MethodType's weak intern set. - */ -final class NativeReferenceQueue extends ReferenceQueue { - public NativeReferenceQueue() { - super(0); - } - - private static class Lock { }; - private final Lock lock = new Lock(); - - @Override - void signal() { - lock.notifyAll(); - } - @Override - void await() throws InterruptedException { - lock.wait(); - } - - @Override - void await(long timeoutMillis) throws InterruptedException { - lock.wait(timeoutMillis); - } - - @Override - boolean enqueue(Reference r) { - synchronized(lock) { - return enqueue0(r); - } - } - - @Override - public Reference poll() { - if (headIsNull()) - return null; - - synchronized(lock) { - return poll0(); - } - } - - @Override - public Reference remove(long timeout) - throws IllegalArgumentException, InterruptedException { - if (timeout < 0) - throw new IllegalArgumentException("Negative timeout value"); - if (timeout == 0) - return remove(); - - synchronized(lock) { - return remove0(timeout); - } - } - - @Override - public Reference remove() throws InterruptedException { - synchronized(lock) { - return remove0(); - } - } -} diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 88cd2a3190b71..13ba76e5dd2f2 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -330,11 +330,6 @@ public boolean waitForReferenceProcessing() public void runFinalization() { Finalizer.runFinalization(); } - - @Override - public ReferenceQueue newNativeReferenceQueue() { - return new NativeReferenceQueue(); - } }); } diff --git a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index 9cbfb733706b8..07110a19fa132 100644 --- a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,10 @@ package java.lang.ref; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import jdk.internal.misc.VM; +import jdk.internal.vm.Continuation; +import jdk.internal.vm.ContinuationSupport; /** * Reference queues, to which registered reference objects are appended by the @@ -50,8 +49,6 @@ public class ReferenceQueue { private static class Null extends ReferenceQueue { - public Null() { super(0); } - @Override boolean enqueue(Reference r) { return false; @@ -64,35 +61,16 @@ boolean enqueue(Reference r) { private volatile Reference head; private long queueLength = 0; - private final ReentrantLock lock; - private final Condition notEmpty; - - void signal() { - notEmpty.signalAll(); - } - - void await() throws InterruptedException { - notEmpty.await(); - } - - void await(long timeoutMillis) throws InterruptedException { - notEmpty.await(timeoutMillis, TimeUnit.MILLISECONDS); - } + private static class Lock { }; + private final Lock lock = new Lock(); /** * Constructs a new reference-object queue. */ public ReferenceQueue() { - this.lock = new ReentrantLock(); - this.notEmpty = lock.newCondition(); } - ReferenceQueue(int dummy) { - this.lock = null; - this.notEmpty = null; - } - - final boolean enqueue0(Reference r) { // must hold lock + private boolean enqueue0(Reference r) { // must hold lock // Check that since getting the lock this reference hasn't already been // enqueued (and even then removed) ReferenceQueue queue = r.queue; @@ -111,15 +89,11 @@ final boolean enqueue0(Reference r) { // must hold lock if (r instanceof FinalReference) { VM.addFinalRefCount(1); } - signal(); + lock.notifyAll(); return true; } - final boolean headIsNull() { - return head == null; - } - - final Reference poll0() { // must hold lock + private Reference poll0() { // must hold lock Reference r = head; if (r != null) { r.queue = NULL; @@ -142,16 +116,14 @@ final Reference poll0() { // must hold lock return null; } - final Reference remove0(long timeout) - throws IllegalArgumentException, InterruptedException { // must hold lock + private Reference remove0(long timeout) throws InterruptedException { // must hold lock Reference r = poll0(); if (r != null) return r; long start = System.nanoTime(); for (;;) { - await(timeout); + lock.wait(timeout); r = poll0(); if (r != null) return r; - long end = System.nanoTime(); timeout -= (end - start) / 1000_000; if (timeout <= 0) return null; @@ -159,23 +131,33 @@ final Reference remove0(long timeout) } } - final Reference remove0() throws InterruptedException { // must hold lock + private Reference remove0() throws InterruptedException { // must hold lock for (;;) { var r = poll0(); if (r != null) return r; - await(); + lock.wait(); } } boolean enqueue(Reference r) { /* Called only by Reference class */ - lock.lock(); - try { + synchronized (lock) { return enqueue0(r); - } finally { - lock.unlock(); } } + private boolean tryDisablePreempt() { + if (Thread.currentThread().isVirtual() && ContinuationSupport.isSupported()) { + Continuation.pin(); + return true; + } else { + return false; + } + } + + private void enablePreempt() { + Continuation.unpin(); + } + /** * Polls this queue to see if a reference object is available. If one is * available without further delay then it is removed from the queue and @@ -186,13 +168,18 @@ boolean enqueue(Reference r) { /* Called only by Reference class */ * @see java.lang.ref.Reference#enqueue() */ public Reference poll() { - if (headIsNull()) + if (head == null) return null; - lock.lock(); + + // Prevent a virtual thread from being preempted as this could potentially + // deadlock with a carrier that is polling the same reference queue. + boolean disabled = tryDisablePreempt(); try { - return poll0(); + synchronized (lock) { + return poll0(); + } } finally { - lock.unlock(); + if (disabled) enablePreempt(); } } @@ -224,11 +211,8 @@ public Reference remove(long timeout) throws InterruptedException { if (timeout == 0) return remove(); - lock.lock(); - try { + synchronized (lock) { return remove0(timeout); - } finally { - lock.unlock(); } } @@ -241,11 +225,8 @@ public Reference remove(long timeout) throws InterruptedException { * @see java.lang.ref.Reference#enqueue() */ public Reference remove() throws InterruptedException { - lock.lock(); - try { + synchronized (lock) { return remove0(); - } finally { - lock.unlock(); } } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangRefAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangRefAccess.java index ed9967ec3ebe0..909c5b80d25f4 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangRefAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangRefAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,11 +50,4 @@ public interface JavaLangRefAccess { * Invoked by Runtime.runFinalization() */ void runFinalization(); - - /** - * Constructs a new NativeReferenceQueue. - * - * Invoked by jdk.internal.util.ReferencedKeyMap - */ - ReferenceQueue newNativeReferenceQueue(); } diff --git a/src/java.base/share/classes/jdk/internal/misc/InternalLock.java b/src/java.base/share/classes/jdk/internal/misc/InternalLock.java index d7a271e68c5d4..822b64f74ca6a 100644 --- a/src/java.base/share/classes/jdk/internal/misc/InternalLock.java +++ b/src/java.base/share/classes/jdk/internal/misc/InternalLock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,10 +39,10 @@ public class InternalLock { private static final boolean CAN_USE_INTERNAL_LOCK; static { String s = System.getProperty("jdk.io.useMonitors"); - if (s != null && (s.isEmpty() || s.equals("true"))) { - CAN_USE_INTERNAL_LOCK = false; - } else { + if (s != null && s.equals("false")) { CAN_USE_INTERNAL_LOCK = true; + } else { + CAN_USE_INTERNAL_LOCK = false; } } diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java index 810d43ae38a3a..766aca20b2167 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java @@ -142,29 +142,7 @@ private ReferencedKeyMap(boolean isSoft, Map, V> map, ReferenceQ */ public static ReferencedKeyMap create(boolean isSoft, Supplier, V>> supplier) { - return create(isSoft, false, supplier); - } - - /** - * Create a new {@link ReferencedKeyMap} map. - * - * @param isSoft true if {@link SoftReference} keys are to - * be used, {@link WeakReference} otherwise. - * @param useNativeQueue true if uses NativeReferenceQueue - * otherwise use {@link ReferenceQueue}. - * @param supplier {@link Supplier} of the backing map - * - * @return a new map with {@link Reference} keys - * - * @param the type of keys maintained by the new map - * @param the type of mapped values - */ - public static ReferencedKeyMap - create(boolean isSoft, boolean useNativeQueue, Supplier, V>> supplier) { - return new ReferencedKeyMap(isSoft, supplier.get(), - useNativeQueue ? SharedSecrets.getJavaLangRefAccess().newNativeReferenceQueue() - : new ReferenceQueue<>() - ); + return new ReferencedKeyMap(isSoft, supplier.get(), new ReferenceQueue<>()); } /** diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java index 73ad6f326409d..355af10aa7c31 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,25 +106,7 @@ private ReferencedKeySet(ReferencedKeyMap> map) { */ public static ReferencedKeySet create(boolean isSoft, Supplier, ReferenceKey>> supplier) { - return create(isSoft, false, supplier); - } - - /** - * Create a new {@link ReferencedKeySet} elements. - * - * @param isSoft true if {@link SoftReference} elements are to - * be used, {@link WeakReference} otherwise. - * @param useNativeQueue true if uses NativeReferenceQueue - * otherwise use {@link ReferenceQueue}. - * @param supplier {@link Supplier} of the backing map - * - * @return a new set with {@link Reference} elements - * - * @param the type of elements maintained by this set - */ - public static ReferencedKeySet - create(boolean isSoft, boolean useNativeQueue, Supplier, ReferenceKey>> supplier) { - return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, useNativeQueue, supplier)); + return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, supplier)); } /** diff --git a/src/java.base/share/classes/jdk/internal/vm/Continuation.java b/src/java.base/share/classes/jdk/internal/vm/Continuation.java index b863def8e6a78..05a32cc3b21fe 100644 --- a/src/java.base/share/classes/jdk/internal/vm/Continuation.java +++ b/src/java.base/share/classes/jdk/internal/vm/Continuation.java @@ -59,7 +59,9 @@ public class Continuation { public enum Pinned { /** Native frame on stack */ NATIVE, /** Monitor held */ MONITOR, - /** In critical section */ CRITICAL_SECTION } + /** In critical section */ CRITICAL_SECTION, + /** Exception (OOME/SOE) */ EXCEPTION + } /** Preemption attempt result */ public enum PreemptStatus { @@ -85,6 +87,7 @@ private static Pinned pinnedReason(int reason) { case 2 -> Pinned.CRITICAL_SECTION; case 3 -> Pinned.NATIVE; case 4 -> Pinned.MONITOR; + case 5 -> Pinned.EXCEPTION; default -> throw new AssertionError("Unknown pinned reason: " + reason); }; } @@ -358,8 +361,6 @@ public static boolean yield(ContinuationScope scope) { @Hidden @JvmtiHideEvents private boolean yield0(ContinuationScope scope, Continuation child) { - preempted = false; - if (scope != this.scope) this.yieldInfo = scope; int res = doYield(); diff --git a/src/java.base/share/classes/sun/nio/ch/Poller.java b/src/java.base/share/classes/sun/nio/ch/Poller.java index 9d3a0e7d32c76..ec45a398176c6 100644 --- a/src/java.base/share/classes/sun/nio/ch/Poller.java +++ b/src/java.base/share/classes/sun/nio/ch/Poller.java @@ -26,7 +26,9 @@ import java.io.IOException; import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -40,7 +42,7 @@ * Polls file descriptors. Virtual threads invoke the poll method to park * until a given file descriptor is ready for I/O. */ -abstract class Poller { +public abstract class Poller { private static final Pollers POLLERS; static { try { @@ -142,6 +144,20 @@ static void poll(int fdVal, int event, long nanos, BooleanSupplier supplier) } } + /** + * Parks the current thread until a Selector's file descriptor is ready. + * @param fdVal the Selector's file descriptor + * @param nanos the waiting time or 0 to wait indefinitely + */ + static void pollSelector(int fdVal, long nanos) throws IOException { + assert nanos >= 0L; + Poller poller = POLLERS.masterPoller(); + if (poller == null) { + poller = POLLERS.readPoller(fdVal); + } + poller.poll(fdVal, nanos, () -> true); + } + /** * If there is a thread polling the given file descriptor for the given event then * the thread is unparked. @@ -258,6 +274,18 @@ private void subPollerLoop(Poller masterPoller) { } } + /** + * Returns the number I/O operations currently registered with this poller. + */ + public int registered() { + return map.size(); + } + + @Override + public String toString() { + return Objects.toIdentityString(this) + " [registered = " + registered() + "]"; + } + /** * The Pollers used for read and write events. */ @@ -344,6 +372,13 @@ void start() { } } + /** + * Returns the master poller, or null if there is no master poller. + */ + Poller masterPoller() { + return masterPoller; + } + /** * Returns the read poller for the given file descriptor. */ @@ -360,6 +395,21 @@ Poller writePoller(int fdVal) { return writePollers[index]; } + /** + * Return the list of read pollers. + */ + List readPollers() { + return List.of(readPollers); + } + + /** + * Return the list of write pollers. + */ + List writePollers() { + return List.of(writePollers); + } + + /** * Reads the given property name to get the poller count. If the property is * set then the value must be a power of 2. Returns 1 if the property is not diff --git a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java index 58794e5dce87f..ee19ff62a38f1 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java @@ -25,6 +25,7 @@ package sun.security.ssl; +import java.lang.invoke.MethodHandles; import java.net.Socket; import java.security.*; import java.security.cert.*; @@ -51,6 +52,15 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager implements X509TrustManager { + static { + // eagerly initialize to avoid pinning virtual thread during TLS handshake + try { + MethodHandles.lookup().ensureInitialized(AnchorCertificates.class); + } catch (IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + private final String validatorType; /** diff --git a/src/java.base/share/native/libjava/Thread.c b/src/java.base/share/native/libjava/Thread.c index fe9f2c91d0cad..4ac30638bc2ac 100644 --- a/src/java.base/share/native/libjava/Thread.c +++ b/src/java.base/share/native/libjava/Thread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/java.base/share/native/libjava/VirtualThread.c b/src/java.base/share/native/libjava/VirtualThread.c index f9d1d4996fcc7..3cacf89e752ce 100644 --- a/src/java.base/share/native/libjava/VirtualThread.c +++ b/src/java.base/share/native/libjava/VirtualThread.c @@ -28,7 +28,7 @@ #include "jvm.h" #include "java_lang_VirtualThread.h" -#define THREAD "Ljava/lang/Thread;" +#define STR "Ljava/lang/String;" #define VIRTUAL_THREAD "Ljava/lang/VirtualThread;" static JNINativeMethod methods[] = { @@ -37,6 +37,8 @@ static JNINativeMethod methods[] = { { "notifyJvmtiMount", "(Z)V", (void *)&JVM_VirtualThreadMount }, { "notifyJvmtiUnmount", "(Z)V", (void *)&JVM_VirtualThreadUnmount }, { "notifyJvmtiDisableSuspend", "(Z)V", (void *)&JVM_VirtualThreadDisableSuspend }, + { "postPinnedEvent", "(" STR ")V", (void *)&JVM_VirtualThreadPinnedEvent }, + { "takeVirtualThreadListToUnblock", "()" VIRTUAL_THREAD, (void *)&JVM_TakeVirtualThreadListToUnblock}, }; JNIEXPORT void JNICALL diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java index 03e0ed4f29a30..84cf7e38d1f39 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ public class JavaThread extends Thread { private static CIntegerField stackSizeField; private static CIntegerField terminatedField; private static AddressField activeHandlesField; + private static CIntegerField lockIdField; private static long oopPtrSize; private static JavaThreadPDAccess access; @@ -101,6 +102,7 @@ private static synchronized void initialize(TypeDataBase db) { stackSizeField = type.getCIntegerField("_stack_size"); terminatedField = type.getCIntegerField("_terminated"); activeHandlesField = type.getAddressField("_active_handles"); + lockIdField = type.getCIntegerField("_lock_id"); lockStackTopOffset = type.getField("_lock_stack").getOffset() + typeLockStack.getField("_top").getOffset(); lockStackBaseOffset = type.getField("_lock_stack").getOffset() + typeLockStack.getField("_base[0]").getOffset(); @@ -376,6 +378,10 @@ public String getThreadName() { return OopUtilities.threadOopGetName(threadObj); } + public Address getLockId() { + return lockIdField.getAddress(addr); + } + // // Oop traversal // diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java index e16982ce43417..25c5344506cdb 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,8 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeExc objectFieldOffset = f.getOffset(); f = type.getField("_owner"); ownerFieldOffset = f.getOffset(); + f = type.getField("_stack_locker"); + stackLockerFieldOffset = f.getOffset(); f = type.getField("_next_om"); nextOMFieldOffset = f.getOffset(); contentionsField = new CIntField(type.getCIntegerField("_contentions"), 0); @@ -87,6 +89,7 @@ public boolean isOwnedAnonymous() { } public Address owner() { return addr.getAddressAt(ownerFieldOffset); } + public Address stackLocker() { return addr.getAddressAt(stackLockerFieldOffset); } // FIXME // void set_owner(void* owner); @@ -117,6 +120,7 @@ public int contentions() { private static long metadataFieldOffset; private static long objectFieldOffset; private static long ownerFieldOffset; + private static long stackLockerFieldOffset; private static long nextOMFieldOffset; private static CIntField contentionsField; private static CIntField waitersField; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java index 4a40525b78a93..6e347858154c8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -212,28 +212,20 @@ public void oopsDo(AddressVisitor oopVisitor) { } } - // refer to Threads::owning_thread_from_monitor_owner - public JavaThread owningThreadFromMonitor(Address o) { - assert(VM.getVM().getCommandLineFlag("LockingMode").getInt() != LockingMode.getLightweight()); + private JavaThread owningThreadFromMonitor(Address o) { if (o == null) return null; for (int i = 0; i < getNumberOfThreads(); i++) { JavaThread thread = getJavaThreadAt(i); - if (o.equals(thread.threadObjectAddress())) { + if (o.equals(thread.getLockId())) { return thread; } } - - for (int i = 0; i < getNumberOfThreads(); i++) { - JavaThread thread = getJavaThreadAt(i); - if (thread.isLockOwned(o)) - return thread; - } return null; } public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) { - if (VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLightweight()) { - if (monitor.isOwnedAnonymous()) { + if (monitor.isOwnedAnonymous()) { + if (VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLightweight()) { OopHandle object = monitor.object(); for (int i = 0; i < getNumberOfThreads(); i++) { JavaThread thread = getJavaThreadAt(i); @@ -246,11 +238,16 @@ public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) { System.out.println("Warning: We failed to find a thread that owns an anonymous lock. This is likely"); System.out.println("due to the JVM currently running a GC. Locking information may not be accurate."); return null; + } else { + assert(VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLegacy()); + Address o = (Address)monitor.stackLocker(); + for (int i = 0; i < getNumberOfThreads(); i++) { + JavaThread thread = getJavaThreadAt(i); + if (thread.isLockOwned(o)) + return thread; + } + return null; } - // Owner can only be threads at this point. - Address o = monitor.owner(); - if (o == null) return null; - return new JavaThread(o); } else { return owningThreadFromMonitor(monitor.owner()); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java index ee9b5f2b4cd31..d0d186e2479d0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java @@ -68,7 +68,6 @@ public final class JDKEvents { jdk.internal.event.TLSHandshakeEvent.class, jdk.internal.event.VirtualThreadStartEvent.class, jdk.internal.event.VirtualThreadEndEvent.class, - jdk.internal.event.VirtualThreadPinnedEvent.class, jdk.internal.event.VirtualThreadSubmitFailedEvent.class, jdk.internal.event.X509CertificateEvent.class, jdk.internal.event.X509ValidationEvent.class, diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java index e8a26fe425d14..48dc0d22cea3a 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java @@ -43,7 +43,6 @@ import jdk.jfr.events.TLSHandshakeEvent; import jdk.jfr.events.ThreadSleepEvent; import jdk.jfr.events.VirtualThreadEndEvent; -import jdk.jfr.events.VirtualThreadPinnedEvent; import jdk.jfr.events.VirtualThreadStartEvent; import jdk.jfr.events.VirtualThreadSubmitFailedEvent; import jdk.jfr.events.X509CertificateEvent; @@ -72,7 +71,6 @@ final class MirrorEvents { register("jdk.internal.event.TLSHandshakeEvent", TLSHandshakeEvent.class); register("jdk.internal.event.VirtualThreadStartEvent", VirtualThreadStartEvent.class); register("jdk.internal.event.VirtualThreadEndEvent", VirtualThreadEndEvent.class); - register("jdk.internal.event.VirtualThreadPinnedEvent", VirtualThreadPinnedEvent.class); register("jdk.internal.event.VirtualThreadSubmitFailedEvent", VirtualThreadSubmitFailedEvent.class); register("jdk.internal.event.X509CertificateEvent", X509CertificateEvent.class); register("jdk.internal.event.X509ValidationEvent", X509ValidationEvent.class); diff --git a/test/hotspot/gtest/nmt/test_vmatree.cpp b/test/hotspot/gtest/nmt/test_vmatree.cpp index 7a5a98b786305..d7a9a5fcff7a6 100644 --- a/test/hotspot/gtest/nmt/test_vmatree.cpp +++ b/test/hotspot/gtest/nmt/test_vmatree.cpp @@ -31,7 +31,7 @@ #include "unittest.hpp" using Tree = VMATree; -using Node = Tree::TreapNode; +using TNode = Tree::TreapNode; using NCS = NativeCallStackStorage; class NMTVMATreeTest : public testing::Test { @@ -77,7 +77,7 @@ class NMTVMATreeTest : public testing::Test { int count_nodes(Tree& tree) { int count = 0; - treap(tree).visit_in_order([&](Node* x) { + treap(tree).visit_in_order([&](TNode* x) { ++count; }); return count; @@ -130,7 +130,7 @@ class NMTVMATreeTest : public testing::Test { for (int i = 0; i < 10; i++) { tree.commit_mapping(i * 100, 100, rd); } - treap(tree).visit_in_order([&](Node* x) { + treap(tree).visit_in_order([&](TNode* x) { VMATree::StateType in = in_type_of(x); VMATree::StateType out = out_type_of(x); EXPECT_TRUE((in == VMATree::StateType::Released && out == VMATree::StateType::Committed) || @@ -155,7 +155,7 @@ class NMTVMATreeTest : public testing::Test { }; int i = 0; - treap(tree).visit_in_order([&](Node* x) { + treap(tree).visit_in_order([&](TNode* x) { if (i < 16) { found[i] = x->key(); } @@ -187,7 +187,7 @@ TEST_VM_F(NMTVMATreeTest, UseFlagInplace) { tree.reserve_mapping(0, 100, rd1); tree.commit_mapping(20, 50, rd2, true); tree.uncommit_mapping(30, 10, rd2); - tree.visit_in_order([&](Node* node) { + tree.visit_in_order([&](TNode* node) { if (node->key() != 100) { EXPECT_EQ(mtTest, node->val().out.mem_tag()) << "failed at: " << node->key(); if (node->key() != 20 && node->key() != 40) { @@ -227,7 +227,7 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { VMATree::RegionData rd2{si[1], mtNMT }; tree.commit_mapping(50, 50, rd2); tree.reserve_mapping(0, 100, rd); - treap(tree).visit_in_order([&](Node* x) { + treap(tree).visit_in_order([&](TNode* x) { EXPECT_TRUE(x->key() == 0 || x->key() == 100); if (x->key() == 0) { EXPECT_EQ(x->val().out.regiondata().mem_tag, mtTest); @@ -264,7 +264,7 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { Tree tree; tree.reserve_mapping(0, 100, rd); tree.commit_mapping(0, 100, rd2); - treap(tree).visit_range_in_order(0, 99999, [&](Node* x) { + treap(tree).visit_range_in_order(0, 99999, [&](TNode* x) { if (x->key() == 0) { EXPECT_EQ(mtTest, x->val().out.regiondata().mem_tag); } diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 06110ddcbc1a8..24a1d0c7c5b03 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -86,6 +86,20 @@ vmTestbase/nsk/jdi/ExceptionEvent/catchLocation/location002/TestDescription.java vmTestbase/nsk/jdi/VMOutOfMemoryException/VMOutOfMemoryException001/VMOutOfMemoryException001.java 8285417 generic-all +### +# The test first suspends a vthread and all the carriers and then it resumes the vthread expecting it +# to run to completion. In mainline it only works because the suspension step happens while the vthread is +# pinned to the carrier blocked on synchronized. So the carrier only actually suspends on the next unmount +# transition which in this test happens once the vthread has finished executing the expected code. + +vmTestbase/nsk/jdi/ThreadReference/isSuspended/issuspended002/TestDescription.java 8338713 generic-all + +### +# The test sends a StopThread to a vthread expecting that is currently pinned to the carrier blocked on +# synchronized. Since the vthread is now unmounted StopThread returns JVMTI_ERROR_OPAQUE_FRAME error. + +vmTestbase/nsk/jdb/kill/kill001/kill001.java 8338714 generic-all + ### # Fails on Windows because of additional memory allocation. diff --git a/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java b/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java index 0b71f89245221..548a746c3afa6 100644 --- a/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java +++ b/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ * -XX:CompileCommand=exclude,java.lang.Thread::sleepNanos * -XX:CompileCommand=exclude,java.lang.Thread::beforeSleep * -XX:CompileCommand=exclude,java.lang.Thread::afterSleep + * -XX:CompileCommand=exclude,java.util.concurrent.TimeUnit::toNanos * -XX:CompileCommand=exclude,java.lang.Math::min * -XX:CompileCommand=exclude,jdk.internal.event.ThreadSleepEvent::* * -XX:CompileCommand=exclude,jdk.internal.event.SleepEvent::* @@ -52,6 +53,7 @@ * -XX:CompileCommand=exclude,java.lang.Thread::sleepNanos * -XX:CompileCommand=exclude,java.lang.Thread::beforeSleep * -XX:CompileCommand=exclude,java.lang.Thread::afterSleep + * -XX:CompileCommand=exclude,java.util.concurrent.TimeUnit::toNanos * -XX:CompileCommand=exclude,java.lang.Math::min * -XX:CompileCommand=exclude,jdk.internal.event.ThreadSleepEvent::* * -XX:CompileCommand=exclude,jdk.internal.event.SleepEvent::* diff --git a/test/hotspot/jtreg/runtime/vthread/JNIMonitor/JNIMonitor.java b/test/hotspot/jtreg/runtime/vthread/JNIMonitor/JNIMonitor.java index 5fe5aaf286fe5..2004b3599c47a 100644 --- a/test/hotspot/jtreg/runtime/vthread/JNIMonitor/JNIMonitor.java +++ b/test/hotspot/jtreg/runtime/vthread/JNIMonitor/JNIMonitor.java @@ -119,6 +119,11 @@ public static void main(String[] args) throws Exception { "-Xlog:jni=debug", // Enable thread termination logging as a visual cross-check "-Xlog:thread+os=info", + // We only count monitors in LM_LEGACY mode + "-XX:LockingMode=1", + // Disable compact headers since that switches locking mode to LM_LIGHTWEIGHT + "-XX:+UnlockExperimentalVMOptions", + "-XX:-UseCompactObjectHeaders", "JNIMonitor$" + test, }; OutputAnalyzer oa = ProcessTools.executeTestJava(cmdArgs); @@ -219,12 +224,16 @@ private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) } static void runTest(int nThreads, boolean skipUnlock, boolean throwOnExit) throws Throwable { - final Object monitor = new Object(); + final Object[] monitors = new Object[nThreads]; + for (int i = 0; i < nThreads; i++) { + monitors[i] = new Object(); + } final AtomicReference exception = new AtomicReference(); // Ensure all our VT's operate of the same carrier, sequentially. ExecutorService scheduler = Executors.newSingleThreadExecutor(); ThreadFactory factory = virtualThreadBuilder(scheduler).factory(); for (int i = 0 ; i < nThreads; i++) { + Object monitor = skipUnlock ? monitors[i] : monitors[0]; Thread th = factory.newThread(() -> { try { int res = monitorEnter(monitor); diff --git a/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java b/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java new file mode 100644 index 0000000000000..62dfd6f8a79bb --- /dev/null +++ b/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test dumping the heap while a virtual thread is unmounted with a native method frame at top. + * @requires vm.continuations + * @modules jdk.management + * @library /test/lib + * @run junit/othervm --enable-native-access=ALL-UNNAMED UnmountedVThreadNativeMethodAtTop + */ + +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import com.sun.management.HotSpotDiagnosticMXBean; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import jdk.test.lib.hprof.model.Snapshot; +import jdk.test.lib.hprof.model.ThreadObject; +import jdk.test.lib.hprof.parser.Reader; + +public class UnmountedVThreadNativeMethodAtTop { + + /** + * Test dumping the heap while a virtual thread is blocked entering a synchronized native method. + */ + @Test + void VThreadBlockedAtSynchronizedNative() throws Exception { + var lock = this; + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + runWithSynchronizedNative(); + }); + try { + synchronized (lock) { + vthread.start(); + + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + + Path dumpFile = dumpHeap(); + verifyHeapDump(dumpFile); + } + } finally { + vthread.join(); + } + } + + /** + * Run native method while holding the monitor for "this". + */ + private synchronized native void runWithSynchronizedNative(); + + /** + * Called from the native method. + */ + private void run() { + } + + /** + * Test dumping the heap while a virtual thread is waiting in Object.wait(). + */ + @Test + void VThreadBlockedAtOjectWait() throws Exception { + var lock = this; + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + try { + synchronized (lock) { + lock.wait(); + } + } catch (InterruptedException e) { } + }); + try { + vthread.start(); + + // wait for thread to start and wait + started.await(); + await(vthread, Thread.State.WAITING); + + Path dumpFile = dumpHeap(); + verifyHeapDump(dumpFile); + + synchronized (lock) { + lock.notify(); + } + } finally { + vthread.join(); + } + } + + private Path dumpHeap() throws Exception { + Path df = Files.createTempFile(Path.of("."), "dump", ".hprof"); + Files.delete(df); + var bean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); + bean.dumpHeap(df.toString(), false); + return df; + } + + private void verifyHeapDump(Path dumpFile) throws Exception { + // Make sure that heap dump can be parsed + System.out.println("Parse " + dumpFile.toAbsolutePath() + " ..."); + try (Snapshot snapshot = Reader.readFile(dumpFile.toString(), false, 0)) { + snapshot.resolve(true); + + // find virtual threads + List vthreads = snapshot.getThreads() + .stream() + .filter(t -> snapshot.findThing(t.getId()) + .getClazz() + .getName().equals("java.lang.VirtualThread")) + .toList(); + + assertFalse(vthreads.isEmpty(), "No virtual threads found!!"); + System.out.format("%s virtual thread(s) found%n", vthreads.size()); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + Thread.sleep(10); + state = thread.getState(); + } + } + + static { + System.loadLibrary("UnmountedVThreadNativeMethodAtTop"); + } +} \ No newline at end of file diff --git a/test/jdk/java/lang/Thread/virtual/libTracePinnedThreads.c b/test/hotspot/jtreg/serviceability/HeapDump/libUnmountedVThreadNativeMethodAtTop.c similarity index 74% rename from test/jdk/java/lang/Thread/virtual/libTracePinnedThreads.c rename to test/hotspot/jtreg/serviceability/HeapDump/libUnmountedVThreadNativeMethodAtTop.c index 2200b91911ffe..3f285d8d9df02 100644 --- a/test/jdk/java/lang/Thread/virtual/libTracePinnedThreads.c +++ b/test/hotspot/jtreg/serviceability/HeapDump/libUnmountedVThreadNativeMethodAtTop.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,9 +23,11 @@ #include "jni.h" -JNIEXPORT void JNICALL Java_TracePinnedThreads_invokePark(JNIEnv *env, jclass clazz) { - jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "park", "()V"); +JNIEXPORT void JNICALL +Java_UnmountedVThreadNativeMethodAtTop_runWithSynchronizedNative(JNIEnv *env, jobject obj) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "()V"); if (mid != NULL) { - (*env)->CallStaticVoidMethod(env, clazz, mid); + (*env)->CallVoidMethod(env, obj, mid); } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEnter/mcontenter01/libmcontenter01.cpp b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEnter/mcontenter01/libmcontenter01.cpp index 4dbb9f71969db..1cfd7dfd57d9f 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEnter/mcontenter01/libmcontenter01.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEnter/mcontenter01/libmcontenter01.cpp @@ -35,7 +35,6 @@ extern "C" { /* ========================================================================== */ /* scaffold objects */ -static JNIEnv *jni = nullptr; static jvmtiEnv *jvmti = nullptr; static jlong timeout = 0; @@ -71,7 +70,7 @@ MonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv *jni, jthread thr, jobject obj) { /* ========================================================================== */ -static int prepare() { +static int prepare(JNIEnv* jni) { jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, nullptr); if (err != JVMTI_ERROR_NONE) { jni->FatalError("Error enabling JVMTI_EVENT_MONITOR_CONTENDED_ENTER."); @@ -92,15 +91,14 @@ static int clean() { /* agent algorithm */ static void JNICALL -agentProc(jvmtiEnv *jvmti, JNIEnv *agentJNI, void *arg) { - jni = agentJNI; +agentProc(jvmtiEnv *jvmti, JNIEnv *jni, void *arg) { /* wait for initial sync */ if (!agent_wait_for_sync(timeout)) { return; } - if (!prepare()) { + if (!prepare(jni)) { set_agent_fail_status(); return; } diff --git a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEntered/mcontentered01/libmcontentered01.cpp b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEntered/mcontentered01/libmcontentered01.cpp index 207ceeb6ac33f..f2121380f63a1 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEntered/mcontentered01/libmcontentered01.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorContendedEntered/mcontentered01/libmcontentered01.cpp @@ -32,7 +32,6 @@ extern "C" { /* ========================================================================== */ /* scaffold objects */ -static JNIEnv *jni = nullptr; static jvmtiEnv *jvmti = nullptr; static jlong timeout = 0; @@ -127,7 +126,6 @@ static int clean() { */ static void JNICALL agentProc(jvmtiEnv *jvmti, JNIEnv *agentJNI, void *arg) { - jni = agentJNI; /* wait for initial sync */ if (!agent_wait_for_sync(timeout)) diff --git a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/libmonitorwaited01.cpp b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/libmonitorwaited01.cpp index 8f66851a0848e..8c9f2e174838e 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/libmonitorwaited01.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/libmonitorwaited01.cpp @@ -30,8 +30,9 @@ extern "C" { +const int MAX_COUNT = 50; + /* scaffold objects */ -static JNIEnv *jni = nullptr; static jvmtiEnv *jvmti = nullptr; static jlong timeout = 0; @@ -40,6 +41,8 @@ static jthread expected_thread = nullptr; static jobject expected_object = nullptr; static volatile int eventsCount = 0; +static void check_stack_trace(JNIEnv* env, jthread thr); + void JNICALL MonitorWaited(jvmtiEnv *jvmti, JNIEnv *jni, jthread thr, jobject obj, jboolean timed_out) { @@ -65,10 +68,49 @@ MonitorWaited(jvmtiEnv *jvmti, JNIEnv *jni, jthread thr, jobject obj, jboolean t set_agent_fail_status(); } } + + if (jni->IsVirtualThread(thr)) { + check_stack_trace(jni, thr); + } } /* ========================================================================== */ +static void check_stack_trace(JNIEnv* jni, jthread thr) { + jvmtiError err; + jint count = 0; + jint skipped = 0; + + print_stack_trace(jvmti, jni, nullptr); + + jvmtiFrameInfo frameInfo[MAX_COUNT]; + + err = jvmti->GetStackTrace(thr, 0, MAX_COUNT, frameInfo, &count); + check_jvmti_status(jni, err, "event handler: error in JVMTI GetStackTrace call"); + + const int expected_count = 8; + const char* expected_methods[expected_count] = {"wait0", "wait", "run", "runWith", "run", "run", "enter0", "enter"}; + + if (count != expected_count) { + LOG("Expected 8 methods in the stack but found %d", count); + jni->FatalError("Unexpected method count"); + } + + for (int idx = 0; idx < count; idx++) { + jclass declaringClass = nullptr; + char *clasSignature = nullptr; + char *methodName = nullptr; + + err = jvmti->GetMethodName(frameInfo[idx].method, &methodName, nullptr, nullptr); + check_jvmti_status(jni, err, "event handler: error in JVMTI GetMethodName call"); + + if (strcmp(methodName, expected_methods[idx]) != 0) { + LOG("Expected method %s but found %s", expected_methods[idx], methodName); + jni->FatalError("Unexpected method found"); + } + } +} + static int prepare() { /* enable MonitorWait event */ jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAITED, nullptr); @@ -90,7 +132,6 @@ static int clean() { static void JNICALL agentProc(jvmtiEnv *jvmti, JNIEnv *agentJNI, void *arg) { - jni = agentJNI; /* wait for initial sync */ if (!agent_wait_for_sync(timeout)) diff --git a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/monitorwaited01.java b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/monitorwaited01.java index 2bdf36df950e1..db246c17d8f5a 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/monitorwaited01.java +++ b/test/hotspot/jtreg/serviceability/jvmti/events/MonitorWaited/monitorwaited01/monitorwaited01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,7 @@ * - change signature of agentProc function * and save JNIEnv pointer now passed as argument. * + * @requires vm.continuations * @library /test/lib * @compile monitorwaited01.java * @run main/othervm/native -agentlib:monitorwaited01 monitorwaited01 platform diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest.java index 3a3b28fdcfad3..631c893df62f6 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -251,13 +251,14 @@ public static void main(String[] args) throws Exception { } } + private static native void runFromNative(Runnable runnable); + private static Thread createPinnedVThread(Runnable runnable) { - final Object syncObj = new Object(); - return Thread.ofVirtual().unstarted(() -> { - synchronized (syncObj) { - runnable.run(); - } - }); + return Thread.ofVirtual().unstarted(() -> runFromNative(runnable)); + } + + private static void runUpcall(Runnable runnable) { + runnable.run(); } // Native implementation of suspendWaiting. diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/libGetThreadStateMountedTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/libGetThreadStateMountedTest.cpp index e6ee99eb23f6c..eb554d3550fef 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/libGetThreadStateMountedTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadStateMountedTest/libGetThreadStateMountedTest.cpp @@ -191,6 +191,16 @@ Java_GetThreadStateMountedTest_getErrorCount(JNIEnv* jni, jclass clazz) { static std::atomic time_to_exit(false); +extern "C" JNIEXPORT void JNICALL +Java_GetThreadStateMountedTest_runFromNative(JNIEnv* jni, jclass clazz, jobject runnable) { + jmethodID mid = jni->GetStaticMethodID(clazz, "runUpcall", "(Ljava/lang/Runnable;)V"); + if (mid == nullptr) { + jni->FatalError("failed to get runUpcall method"); + return; + } + jni->CallStaticVoidMethod(clazz, mid, runnable); +} + extern "C" JNIEXPORT void JNICALL Java_GetThreadStateMountedTest_waitInNative(JNIEnv* jni, jclass clazz) { // Notify main thread that we are ready diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java index d0a2eb65c6367..d455a16c99849 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,30 @@ * @test id=default * @summary Verifies JVMTI StopThread support for virtual threads. * @requires vm.continuations + * @library /test/lib * @run main/othervm/native -agentlib:StopThreadTest StopThreadTest */ /* * @test id=no-vmcontinuations * @summary Verifies JVMTI StopThread support for bound virtual threads. - * @run main/othervm/native -agentlib:StopThreadTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations StopThreadTest + * @library /test/lib + * @run main/othervm/native -agentlib:StopThreadTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations -DboundVThread=true StopThreadTest */ /* * @test id=platform * @summary Verifies JVMTI StopThread support for platform threads. + * @library /test/lib * @run main/othervm/native -agentlib:StopThreadTest StopThreadTest platform */ +import jdk.test.lib.Platform; import java.lang.AssertionError; +import com.sun.management.HotSpotDiagnosticMXBean; +import java.lang.management.ManagementFactory; + /* * The test exercises the JVMTI function: StopThread(jthread). * The test creates a new virtual or platform thread. @@ -53,7 +60,9 @@ */ public class StopThreadTest { private static final String agentLib = "StopThreadTest"; + static final boolean isBoundVThread = Boolean.getBoolean("boundVThread"); static final int JVMTI_ERROR_NONE = 0; + static final int JVMTI_ERROR_OPAQUE_FRAME = 32; static final int THREAD_NOT_SUSPENDED = 13; static final int PASSED = 0; static final int FAILED = 2; @@ -104,7 +113,7 @@ public static void run() { } else { testTaskThread = Thread.ofPlatform().name("TestTaskThread").start(testTask); } - TestTask.ensureAtPointA(); + TestTask.ensureBlockedAfterPointA(testTaskThread); if (is_virtual) { // this check is for virtual target thread only log("\nMain #A.1: unsuspended"); @@ -119,10 +128,12 @@ public static void run() { log("\nMain #A.2: suspended"); suspendThread(testTaskThread); retCode = stopThread(testTaskThread); - if (retCode != JVMTI_ERROR_NONE) { - throwFailed("Main #A.2: expected JVMTI_ERROR_NONE instead of: " + retCode); + int expectedRetCode = preemptableVirtualThread() ? JVMTI_ERROR_OPAQUE_FRAME : JVMTI_ERROR_NONE; + String expectedRetCodeName = preemptableVirtualThread() ? "JVMTI_ERROR_OPAQUE_FRAME" : "JVMTI_ERROR_NONE"; + if (retCode != expectedRetCode) { + throwFailed("Main #A.2: expected " + expectedRetCodeName + " instead of: " + retCode); } else { - log("Main #A.2: got expected JVMTI_ERROR_NONE"); + log("Main #A.2: got expected " + expectedRetCodeName); } resumeThread(testTaskThread); } @@ -168,7 +179,7 @@ static class TestTask implements Runnable { static Object lock = new Object(); static void log(String str) { System.out.println(str); } - static volatile boolean atPointA = false; + static volatile boolean reachedPointA = false; static volatile boolean finished = false; static void sleep(long millis) { @@ -179,8 +190,9 @@ static void sleep(long millis) { } } - static void ensureAtPointA() { - while (!atPointA) { + static void ensureBlockedAfterPointA(Thread vt) { + // wait while the thread state is not the expected one + while (!reachedPointA || vt.getState() != Thread.State.BLOCKED) { sleep(1); } } @@ -203,7 +215,7 @@ public void run() { seenExceptionFromA = true; } Thread.interrupted(); - if (!seenExceptionFromA) { + if (!seenExceptionFromA && !preemptableVirtualThread()) { StopThreadTest.setFailed("TestTask.run: expected AssertionError from method A()"); } sleep(1); // to cause yield @@ -241,7 +253,7 @@ public void run() { // - when suspended: JVMTI_ERROR_NONE is expected static void A() { log("TestTask.A: started"); - atPointA = true; + reachedPointA = true; synchronized (lock) { } log("TestTask.A: finished"); @@ -263,4 +275,10 @@ static void C() { log("TestTask.C: finished"); } } + + static boolean preemptableVirtualThread() { + boolean legacyLockingMode = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class) + .getVMOption("LockingMode").getValue().equals("1"); + return is_virtual && !isBoundVThread && !legacyLockingMode; + } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume1/SuspendResume1.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume1/SuspendResume1.java index 96926a389e1b2..e4673c3d01769 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume1/SuspendResume1.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume1/SuspendResume1.java @@ -25,6 +25,7 @@ * @test id=default * @summary Test SuspendThread/ResumeThread, SuspendThreadList/ResumeThreadList * for virtual threads. + * @requires vm.debug != true * @library /test/lib * @compile SuspendResume1.java * @run driver jdk.test.lib.FileInstaller . . @@ -34,6 +35,19 @@ * SuspendResume1 */ +/* + * @test id=debug + * @requires vm.debug == true + * @library /test/lib + * @compile SuspendResume1.java + * @run driver jdk.test.lib.FileInstaller . . + * @run main/othervm/native/timeout=600 + * -Djdk.virtualThreadScheduler.maxPoolSize=1 + * -agentlib:SuspendResume1 + * -XX:-VerifyContinuations + * SuspendResume1 + */ + /* * @test id=no-vmcontinuations * @requires vm.continuations @@ -168,7 +182,7 @@ public void run() { public void ensureReady() { try { while (!threadReady) { - sleep(1000); + sleep(100); } } catch (InterruptedException e) { throw new RuntimeException("Interruption while preparing tested thread: \n\t" + e); diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume2/SuspendResume2.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume2/SuspendResume2.java index 65381468ba953..7c7d21160ae85 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume2/SuspendResume2.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendResume2/SuspendResume2.java @@ -24,6 +24,7 @@ /* * @test id=default * @summary Test SuspendAllVirtualThreads/ResumeAllVirtualThreads + * @requires vm.debug != true * @library /test/lib * @compile SuspendResume2.java * @run driver jdk.test.lib.FileInstaller . . @@ -33,6 +34,19 @@ * SuspendResume2 */ +/* + * @test id=debug + * @requires vm.debug == true + * @library /test/lib + * @compile SuspendResume2.java + * @run driver jdk.test.lib.FileInstaller . . + * @run main/othervm/native + * -Djdk.virtualThreadScheduler.maxPoolSize=1 + * -agentlib:SuspendResume2 + * -XX:-VerifyContinuations + * SuspendResume2 + */ + /* * @test id=no-vmcontinuations * @requires vm.continuations @@ -148,7 +162,7 @@ public void run() { while (!shouldFinish) { if (n <= 0) { n = 1000; - SuspendResume2.sleep(1); + SuspendResume2.sleep(10); } if (i > n) { i = 0; @@ -162,7 +176,7 @@ public void run() { public void ensureReady() { try { while (!threadReady) { - sleep(1); + sleep(100); } } catch (InterruptedException e) { throw new RuntimeException("Interruption while preparing tested thread: \n\t" + e); diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendWithInterruptLock/SuspendWithInterruptLock.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendWithInterruptLock/SuspendWithInterruptLock.java index d679f382d93dd..107c843226e24 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendWithInterruptLock/SuspendWithInterruptLock.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/SuspendWithInterruptLock/SuspendWithInterruptLock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,13 +32,20 @@ /** * @test id=xint - * @summary Do not suspend virtual threads in a critical section. - * @bug 8311218 + * @requires vm.debug != true * @requires vm.continuations * @library /testlibrary * @run main/othervm -Xint SuspendWithInterruptLock */ +/** + * @test id=xint-debug + * @requires vm.debug == true + * @requires vm.continuations + * @library /testlibrary + * @run main/othervm -Xint -XX:-VerifyContinuations SuspendWithInterruptLock + */ + import jvmti.JVMTIUtils; public class SuspendWithInterruptLock { diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp index 13ff93de9dc25..7d898bcab255e 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp @@ -28,6 +28,7 @@ extern "C" { +const int MAX_COUNT = 50; static jvmtiEnv *jvmti = nullptr; static std::atomic thread_end_cnt(0); static std::atomic thread_unmount_cnt(0); @@ -38,10 +39,70 @@ void JNICALL VirtualThreadEnd(jvmtiEnv *jvmti, JNIEnv* jni, jthread vthread) { } void JNICALL VirtualThreadMount(jvmtiEnv* jvmti, ...) { + jvmtiError err; + jint count = 0; + jvmtiFrameInfo frameInfo[MAX_COUNT]; + + va_list ap; + JNIEnv* jni = nullptr; + jthread thread = nullptr; + + va_start(ap, jvmti); + jni = va_arg(ap, JNIEnv*); + thread = va_arg(ap, jthread); + va_end(ap); + + err = jvmti->GetStackTrace(thread, 0, MAX_COUNT, frameInfo, &count); + check_jvmti_status(jni, err, "event handler: error in JVMTI GetStackTrace call"); + + // Verify top 3 methods to filter events from Continuation::try_preempt(). + const int verification_count = 3; + const char* expected_methods[verification_count] = {"yieldContinuation", "park", "parkVirtualThread"}; + + if (count < verification_count) return; + + for (int idx = 0; idx < verification_count; idx++) { + char* methodName = get_method_name(jvmti, jni, frameInfo[idx].method); + if (strcmp(methodName, expected_methods[idx]) != 0) { + deallocate(jvmti, jni, methodName); + return; + } + deallocate(jvmti, jni, methodName); + } thread_mount_cnt++; } void JNICALL VirtualThreadUnmount(jvmtiEnv* jvmti, ...) { + jvmtiError err; + jint count = 0; + jvmtiFrameInfo frameInfo[MAX_COUNT]; + + va_list ap; + JNIEnv* jni = nullptr; + jthread thread = nullptr; + + va_start(ap, jvmti); + jni = va_arg(ap, JNIEnv*); + thread = va_arg(ap, jthread); + va_end(ap); + + err = jvmti->GetStackTrace(thread, 0, MAX_COUNT, frameInfo, &count); + check_jvmti_status(jni, err, "event handler: error in JVMTI GetStackTrace call"); + + // Verify top 3 methods to filter events from Continuation::try_preempt(). + const int verification_count = 3; + const char* expected_methods[verification_count] = {"run", "enter0", "enter"}; + + if (count < verification_count) return; + + for (int idx = 0; idx < verification_count; idx++) { + char* methodName = get_method_name(jvmti, jni, frameInfo[idx].method); + if (strcmp(methodName, expected_methods[idx]) != 0) { + deallocate(jvmti, jni, methodName); + return; + } + deallocate(jvmti, jni, methodName); + } thread_unmount_cnt++; } diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/VThreadMonitorTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/VThreadMonitorTest.java index 77f53f051d63e..9a339f8202b08 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/VThreadMonitorTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/VThreadMonitorTest.java @@ -49,6 +49,7 @@ public class VThreadMonitorTest { } private static native boolean hasEventPosted(); private static native void checkContendedMonitor(Thread thread, Object mon1, Object mon2); + private static native void checkOwnedMonitor(Thread thread, int expectedCount); private static native int check(); private static void log(String str) { System.out.println(str); } @@ -57,6 +58,7 @@ public class VThreadMonitorTest { private static final Object lock0 = new MonitorClass0(); private static final Object lock1 = new Object(); private static final Object lock2 = new MonitorClass2(); + private static volatile Thread waiter; static void sleep(long millis) { try { @@ -73,6 +75,12 @@ static void m0() { static void m1() { synchronized (lock1) { // log(thrName() +" entered sync section with lock1"); + if (waiter == null) { + try { + waiter = Thread.currentThread(); + lock1.wait(); + } catch (InterruptedException e) {} + } m0(); } } @@ -121,6 +129,13 @@ public static void main(String[] args) throws Exception { for (int i = 0; i < VT_TOTAL; i++) { vthreads[i].start(); } + // Wait for waiter to enter lock1. + while (waiter == null) {} + synchronized (lock1) { + checkOwnedMonitor(waiter, 1); + lock1.notify(); + } + // Wait for the MonitorContendedEnter event. while (!hasEventPosted()) { log("Main thread is waiting for event\n"); diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/libVThreadMonitorTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/libVThreadMonitorTest.cpp index af0861ee03187..13df3356ba4ea 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/libVThreadMonitorTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadMonitorTest/libVThreadMonitorTest.cpp @@ -119,7 +119,7 @@ check_contended_monitor(jvmtiEnv *jvmti, JNIEnv *jni, const char* func, static void check_owned_monitor(jvmtiEnv *jvmti, JNIEnv *jni, const char* func, - jthread thread, char* tname, jboolean is_vt, jobject monitor) { + jthread thread, char* tname, jboolean is_vt, jint expected_count) { jvmtiError err; jint state = 0; jint mcount = -1; @@ -135,8 +135,8 @@ check_owned_monitor(jvmtiEnv *jvmti, JNIEnv *jni, const char* func, LOG("\n%s: GetOwnedMonitorInfo: %s owns %d monitor(s)\n", func, tname, mcount); jvmti->Deallocate((unsigned char *)owned_monitors); - if (is_vt == JNI_TRUE && mcount < 2) { - LOG("%s: FAIL: monitorCount for %s expected to be >= 2\n", func, tname); + if (is_vt == JNI_TRUE && mcount != expected_count) { + LOG("%s: FAIL: monitorCount for %s expected to be %d\n", func, tname, expected_count); status = STATUS_FAILED; return; } @@ -186,9 +186,9 @@ MonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread, jobject mon check_contended_monitor(jvmti, jni, "MonitorContendedEnter", cthread, ctname, JNI_FALSE, nullptr, nullptr); check_owned_monitor(jvmti, jni, "MonitorContendedEnter", - vthread, vtname, JNI_TRUE, monitor); + vthread, vtname, JNI_TRUE, 2); check_owned_monitor(jvmti, jni, "MonitorContendedEnter", - cthread, ctname, JNI_FALSE, monitor); + cthread, ctname, JNI_FALSE, 2); deallocate(jvmti, jni, (void*)vtname); deallocate(jvmti, jni, (void*)ctname); } @@ -352,6 +352,15 @@ Java_VThreadMonitorTest_checkContendedMonitor(JNIEnv *jni, jclass cls, jthread v deallocate(jvmti, jni, (void*)tname); } +JNIEXPORT void JNICALL +Java_VThreadMonitorTest_checkOwnedMonitor(JNIEnv *jni, jclass cls, jthread vthread, jint expected_count) { + char* tname = get_thread_name(jvmti, jni, vthread); + + check_owned_monitor(jvmti, jni, "checkOwnedMonitor", vthread, tname, JNI_TRUE, expected_count); + + deallocate(jvmti, jni, (void*)tname); +} + JNIEXPORT jint JNICALL Java_VThreadMonitorTest_check(JNIEnv *jni, jclass cls) { return status; diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java index 067a992d44438..d39779c4e4d5d 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,8 +62,8 @@ public static void main(String[] args) throws Exception { tokensMap.put("(a java.lang.Class for LingeredAppWithLock)", "instance of Oop for java/lang/Class"); tokensMap.put("Method*=", "Type is Method"); - tokensMap.put("(a java/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject)", - "instance of Oop for java/util/concurrent/locks/AbstractQueuedSynchronizer\\$ConditionObject"); + tokensMap.put("(a java.lang.ref.ReferenceQueue$Lock)", + "instance of Oop for java/lang/ref/ReferenceQueue\\$Lock"); String[] lines = jstackOutput.split("\\R"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java index 7593b460c2e0c..47e030945abce 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/bcinstr/BI04/bi04t002/newclass02/java.base/java/lang/Object.java @@ -23,116 +23,147 @@ package java.lang; -import jdk.internal.misc.Blocker; +import jdk.internal.vm.annotation.IntrinsicCandidate; import nsk.jvmti.scenarios.bcinstr.BI04.bi04t002a; /** - * Class Object is the root of the class hierarchy. - * Every class has Object as a superclass. All objects, + * Class {@code Object} is the root of the class hierarchy. + * Every class has {@code Object} as a superclass. All objects, * including arrays, implement the methods of this class. * - * @author unascribed - * @version 1.67, 02/03/04 * @see java.lang.Class - * @since JDK1.0 + * @since 1.0 */ public class Object { /** - * Returns the runtime class of an object. That Class - * object is the object that is locked by static synchronized - * methods of the represented class. + * Constructs a new object. + */ + @IntrinsicCandidate + public Object() {} + + /** + * Returns the runtime class of this {@code Object}. The returned + * {@code Class} object is the object that is locked by {@code + * static synchronized} methods of the represented class. + * + *

The actual result type is {@code Class} + * where {@code |X|} is the erasure of the static type of the + * expression on which {@code getClass} is called. For + * example, no cast is required in this code fragment:

* - * @return The java.lang.Class object that represents - * the runtime class of the object. The result is of type - * {@code Class} where X is the - * static type of the expression on which - * getClass is called. + *

+ * {@code Number n = 0; }
+ * {@code Class c = n.getClass(); } + *

+ * + * @return The {@code Class} object that represents the runtime + * class of this object. + * @jls 15.8.2 Class Literals */ - public final native Class getClass(); + @IntrinsicCandidate + public final native Class getClass(); /** - * Returns a hash code value for the object. This method is - * supported for the benefit of hashtables such as those provided by - * java.util.Hashtable. + * {@return a hash code value for this object} This method is + * supported for the benefit of hash tables such as those provided by + * {@link java.util.HashMap}. *

- * The general contract of hashCode is: + * The general contract of {@code hashCode} is: *

    *
  • Whenever it is invoked on the same object more than once during - * an execution of a Java application, the hashCode method + * an execution of a Java application, the {@code hashCode} method * must consistently return the same integer, provided no information - * used in equals comparisons on the object is modified. + * used in {@code equals} comparisons on the object is modified. * This integer need not remain consistent from one execution of an * application to another execution of the same application. - *
  • If two objects are equal according to the equals(Object) - * method, then calling the hashCode method on each of - * the two objects must produce the same integer result. + *
  • If two objects are equal according to the {@link + * #equals(Object) equals} method, then calling the {@code + * hashCode} method on each of the two objects must produce the + * same integer result. *
  • It is not required that if two objects are unequal - * according to the {@link java.lang.Object#equals(java.lang.Object)} - * method, then calling the hashCode method on each of the - * two objects must produce distinct integer results. However, the - * programmer should be aware that producing distinct integer results - * for unequal objects may improve the performance of hashtables. + * according to the {@link #equals(Object) equals} method, then + * calling the {@code hashCode} method on each of the two objects + * must produce distinct integer results. However, the programmer + * should be aware that producing distinct integer results for + * unequal objects may improve the performance of hash tables. *
- *

- * As much as is reasonably practical, the hashCode method defined by - * class Object does return distinct integers for distinct - * objects. (This is typically implemented by converting the internal - * address of the object into an integer, but this implementation - * technique is not required by the - * JavaTM programming language.) * - * @return a hash code value for this object. + * @implSpec + * As far as is reasonably practical, the {@code hashCode} method defined + * by class {@code Object} returns distinct integers for distinct objects. + * + * @apiNote + * The {@link java.util.Objects#hash(Object...) hash} and {@link + * java.util.Objects#hashCode(Object) hashCode} methods of {@link + * java.util.Objects} can be used to help construct simple hash codes. + * * @see java.lang.Object#equals(java.lang.Object) - * @see java.util.Hashtable + * @see java.lang.System#identityHashCode */ + @IntrinsicCandidate public native int hashCode(); /** * Indicates whether some other object is "equal to" this one. *

- * The equals method implements an equivalence relation + * The {@code equals} method implements an equivalence relation * on non-null object references: *

    *
  • It is reflexive: for any non-null reference value - * x, x.equals(x) should return - * true. + * {@code x}, {@code x.equals(x)} should return + * {@code true}. *
  • It is symmetric: for any non-null reference values - * x and y, x.equals(y) - * should return true if and only if - * y.equals(x) returns true. + * {@code x} and {@code y}, {@code x.equals(y)} + * should return {@code true} if and only if + * {@code y.equals(x)} returns {@code true}. *
  • It is transitive: for any non-null reference values - * x, y, and z, if - * x.equals(y) returns true and - * y.equals(z) returns true, then - * x.equals(z) should return true. + * {@code x}, {@code y}, and {@code z}, if + * {@code x.equals(y)} returns {@code true} and + * {@code y.equals(z)} returns {@code true}, then + * {@code x.equals(z)} should return {@code true}. *
  • It is consistent: for any non-null reference values - * x and y, multiple invocations of - * x.equals(y) consistently return true - * or consistently return false, provided no - * information used in equals comparisons on the + * {@code x} and {@code y}, multiple invocations of + * {@code x.equals(y)} consistently return {@code true} + * or consistently return {@code false}, provided no + * information used in {@code equals} comparisons on the * objects is modified. - *
  • For any non-null reference value x, - * x.equals(null) should return false. + *
  • For any non-null reference value {@code x}, + * {@code x.equals(null)} should return {@code false}. *
+ * *

- * The equals method for class Object implements + * An equivalence relation partitions the elements it operates on + * into equivalence classes; all the members of an + * equivalence class are equal to each other. Members of an + * equivalence class are substitutable for each other, at least + * for some purposes. + * + * @implSpec + * The {@code equals} method for class {@code Object} implements * the most discriminating possible equivalence relation on objects; - * that is, for any non-null reference values x and - * y, this method returns true if and only - * if x and y refer to the same object - * (x == y has the value true). - *

- * Note that it is generally necessary to override the hashCode + * that is, for any non-null reference values {@code x} and + * {@code y}, this method returns {@code true} if and only + * if {@code x} and {@code y} refer to the same object + * ({@code x == y} has the value {@code true}). + * + * In other words, under the reference equality equivalence + * relation, each equivalence class only has a single element. + * + * @apiNote + * It is generally necessary to override the {@link #hashCode() hashCode} * method whenever this method is overridden, so as to maintain the - * general contract for the hashCode method, which states + * general contract for the {@code hashCode} method, which states * that equal objects must have equal hash codes. + *

The two-argument {@link java.util.Objects#equals(Object, + * Object) Objects.equals} method implements an equivalence relation + * on two possibly-null object references. * * @param obj the reference object with which to compare. - * @return true if this object is the same as the obj - * argument; false otherwise. + * @return {@code true} if this object is the same as the obj + * argument; {@code false} otherwise. * @see #hashCode() - * @see java.util.Hashtable + * @see java.util.HashMap */ public boolean equals(Object obj) { bi04t002a.instrInvoke(bi04t002a.INSTR_EQUALS); @@ -142,7 +173,7 @@ public boolean equals(Object obj) { /** * Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. The general - * intent is that, for any object x, the expression: + * intent is that, for any object {@code x}, the expression: *

*
      * x.clone() != x
@@ -150,75 +181,89 @@ public boolean equals(Object obj) { *
*
      * x.clone().getClass() == x.getClass()
- * will be true, but these are not absolute requirements. + * will be {@code true}, but these are not absolute requirements. * While it is typically the case that: *
*
      * x.clone().equals(x)
- * will be true, this is not an absolute requirement. + * will be {@code true}, this is not an absolute requirement. *

* By convention, the returned object should be obtained by calling - * super.clone. If a class and all of its superclasses (except - * Object) obey this convention, it will be the case that - * x.clone().getClass() == x.getClass(). + * {@code super.clone}. If a class and all of its superclasses (except + * {@code Object}) obey this convention, it will be the case that + * {@code x.clone().getClass() == x.getClass()}. *

* By convention, the object returned by this method should be independent * of this object (which is being cloned). To achieve this independence, * it may be necessary to modify one or more fields of the object returned - * by super.clone before returning it. Typically, this means + * by {@code super.clone} before returning it. Typically, this means * copying any mutable objects that comprise the internal "deep structure" * of the object being cloned and replacing the references to these * objects with references to the copies. If a class contains only * primitive fields or references to immutable objects, then it is usually - * the case that no fields in the object returned by super.clone + * the case that no fields in the object returned by {@code super.clone} * need to be modified. - *

- * The method clone for class Object performs a + * + * @implSpec + * The method {@code clone} for class {@code Object} performs a * specific cloning operation. First, if the class of this object does - * not implement the interface Cloneable, then a - * CloneNotSupportedException is thrown. Note that all arrays - * are considered to implement the interface Cloneable. + * not implement the interface {@code Cloneable}, then a + * {@code CloneNotSupportedException} is thrown. Note that all arrays + * are considered to implement the interface {@code Cloneable} and that + * the return type of the {@code clone} method of an array type {@code T[]} + * is {@code T[]} where T is any reference or primitive type. * Otherwise, this method creates a new instance of the class of this * object and initializes all its fields with exactly the contents of * the corresponding fields of this object, as if by assignment; the * contents of the fields are not themselves cloned. Thus, this method * performs a "shallow copy" of this object, not a "deep copy" operation. *

- * The class Object does not itself implement the interface - * Cloneable, so calling the clone method on an object - * whose class is Object will result in throwing an + * The class {@code Object} does not itself implement the interface + * {@code Cloneable}, so calling the {@code clone} method on an object + * whose class is {@code Object} will result in throwing an * exception at run time. * * @return a clone of this instance. - * @exception CloneNotSupportedException if the object's class does not - * support the Cloneable interface. Subclasses - * that override the clone method can also + * @throws CloneNotSupportedException if the object's class does not + * support the {@code Cloneable} interface. Subclasses + * that override the {@code clone} method can also * throw this exception to indicate that an instance cannot * be cloned. * @see java.lang.Cloneable */ + @IntrinsicCandidate protected native Object clone() throws CloneNotSupportedException; /** - * Returns a string representation of the object. In general, the - * toString method returns a string that + * {@return a string representation of the object} + * + * Satisfying this method's contract implies a non-{@code null} + * result must be returned. + * + * @apiNote + * In general, the + * {@code toString} method returns a string that * "textually represents" this object. The result should * be a concise but informative representation that is easy for a * person to read. * It is recommended that all subclasses override this method. - *

- * The toString method for class Object + * The string output is not necessarily stable over time or across + * JVM invocations. + * @implSpec + * The {@code toString} method for class {@code Object} * returns a string consisting of the name of the class of which the - * object is an instance, the at-sign character `@', and + * object is an instance, the at-sign character `{@code @}', and * the unsigned hexadecimal representation of the hash code of the * object. In other words, this method returns a string equal to the * value of: - *

- *
+     * {@snippet lang=java :
      * getClass().getName() + '@' + Integer.toHexString(hashCode())
-     * 
- * - * @return a string representation of the object. + * } + * The {@link java.util.Objects#toIdentityString(Object) + * Objects.toIdentityString} method returns the string for an + * object equal to the string that would be returned if neither + * the {@code toString} nor {@code hashCode} methods were + * overridden by the object's class. */ public String toString() { bi04t002a.instrInvoke(bi04t002a.INSTR_TOSTRING); @@ -230,7 +275,7 @@ public String toString() { * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's - * monitor by calling one of the wait methods. + * monitor by calling one of the {@code wait} methods. *

* The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will @@ -244,25 +289,26 @@ public String toString() { * object's monitor in one of three ways: *

    *
  • By executing a synchronized instance method of that object. - *
  • By executing the body of a synchronized statement + *
  • By executing the body of a {@code synchronized} statement * that synchronizes on the object. - *
  • For objects of type Class, by executing a - * synchronized static method of that class. + *
  • For objects of type {@code Class,} by executing a + * static synchronized method of that class. *
*

* Only one thread at a time can own an object's monitor. * - * @exception IllegalMonitorStateException if the current thread is not + * @throws IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() */ + @IntrinsicCandidate public final native void notify(); /** * Wakes up all threads that are waiting on this object's monitor. A * thread waits on an object's monitor by calling one of the - * wait methods. + * {@code wait} methods. *

* The awakened threads will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened threads @@ -272,188 +318,183 @@ public String toString() { * being the next thread to lock this object. *

* This method should only be called by a thread that is the owner - * of this object's monitor. See the notify method for a + * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * - * @exception IllegalMonitorStateException if the current thread is not + * @throws IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notify() * @see java.lang.Object#wait() */ + @IntrinsicCandidate public final native void notifyAll(); /** - * Causes current thread to wait until either another thread invokes the - * {@link java.lang.Object#notify()} method or the - * {@link java.lang.Object#notifyAll()} method for this object, or a - * specified amount of time has elapsed. - *

- * The current thread must own this object's monitor. - *

- * This method causes the current thread (call it T) to - * place itself in the wait set for this object and then to relinquish - * any and all synchronization claims on this object. Thread T - * becomes disabled for thread scheduling purposes and lies dormant - * until one of four things happens: - *

    - *
  • Some other thread invokes the notify method for this - * object and thread T happens to be arbitrarily chosen as - * the thread to be awakened. - *
  • Some other thread invokes the notifyAll method for this - * object. - *
  • Some other thread {@link java.lang.Thread#interrupt() interrupts} - * thread T. - *
  • The specified amount of real time has elapsed, more or less. If - * timeout is zero, however, then real time is not taken into - * consideration and the thread simply waits until notified. - *
- * The thread T is then removed from the wait set for this - * object and re-enabled for thread scheduling. It then competes in the - * usual manner with other threads for the right to synchronize on the - * object; once it has gained control of the object, all its - * synchronization claims on the object are restored to the status quo - * ante - that is, to the situation as of the time that the wait - * method was invoked. Thread T then returns from the - * invocation of the wait method. Thus, on return from the - * wait method, the synchronization state of the object and of - * thread T is exactly as it was when the wait method - * was invoked. + * Causes the current thread to wait until it is awakened, typically + * by being notified or interrupted. *

- * A thread can also wake up without being notified, interrupted, or - * timing out, a so-called spurious wakeup. While this will rarely - * occur in practice, applications must guard against it by testing for - * the condition that should have caused the thread to be awakened, and - * continuing to wait if the condition is not satisfied. In other words, - * waits should always occur in loops, like this one: - *

-     *     synchronized (obj) {
-     *         while (<condition does not hold>)
-     *             obj.wait(timeout);
-     *         ... // Perform action appropriate to condition
-     *     }
-     * 
- * (For more information on this topic, see Section 3.2.3 in Doug Lea's - * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley, - * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming - * Language Guide" (Addison-Wesley, 2001). - *

- * If the current thread is - * {@link java.lang.Thread#interrupt() interrupted} by another thread - * while it is waiting, then an InterruptedException is thrown. - * This exception is not thrown until the lock status of this object has - * been restored as described above. - *

- * Note that the wait method, as it places the current thread - * into the wait set for this object, unlocks only this object; any - * other objects on which the current thread may be synchronized remain - * locked while the thread waits. + * In all respects, this method behaves as if {@code wait(0L, 0)} + * had been called. See the specification of the {@link #wait(long, int)} method + * for details. + * + * @throws IllegalMonitorStateException if the current thread is not + * the owner of the object's monitor + * @throws InterruptedException if any thread interrupted the current thread before or + * while the current thread was waiting. The interrupted status of the + * current thread is cleared when this exception is thrown. + * @see #notify() + * @see #notifyAll() + * @see #wait(long) + * @see #wait(long, int) + */ + public final void wait() throws InterruptedException { + bi04t002a.instrInvoke(bi04t002a.INSTR_WAIT); + wait(0L); + } + + /** + * Causes the current thread to wait until it is awakened, typically + * by being notified or interrupted, or until a + * certain amount of real time has elapsed. *

- * This method should only be called by a thread that is the owner - * of this object's monitor. See the notify method for a - * description of the ways in which a thread can become the owner of - * a monitor. + * In all respects, this method behaves as if {@code wait(timeoutMillis, 0)} + * had been called. See the specification of the {@link #wait(long, int)} method + * for details. * - * @param timeout the maximum time to wait in milliseconds. - * @exception IllegalArgumentException if the value of timeout is - * negative. - * @exception IllegalMonitorStateException if the current thread is not - * the owner of the object's monitor. - * @exception InterruptedException if another thread interrupted the - * current thread before or while the current thread - * was waiting for a notification. The interrupted - * status of the current thread is cleared when - * this exception is thrown. - * @see java.lang.Object#notify() - * @see java.lang.Object#notifyAll() + * @param timeoutMillis the maximum time to wait, in milliseconds + * @throws IllegalArgumentException if {@code timeoutMillis} is negative + * @throws IllegalMonitorStateException if the current thread is not + * the owner of the object's monitor + * @throws InterruptedException if any thread interrupted the current thread before or + * while the current thread was waiting. The interrupted status of the + * current thread is cleared when this exception is thrown. + * @see #notify() + * @see #notifyAll() + * @see #wait() + * @see #wait(long, int) */ public final void wait(long timeoutMillis) throws InterruptedException { - if (!Thread.currentThread().isVirtual()) { - wait0(timeoutMillis); - return; + if (timeoutMillis < 0) { + throw new IllegalArgumentException("timeout value is negative"); } - // virtual thread waiting - boolean attempted = Blocker.begin(); - try { + if (Thread.currentThread() instanceof VirtualThread vthread) { + try { + wait0(timeoutMillis); + } catch (InterruptedException e) { + // virtual thread's interrupt status needs to be cleared + vthread.getAndClearInterrupt(); + throw e; + } + } else { wait0(timeoutMillis); - } catch (InterruptedException e) { - // virtual thread's interrupt status needs to be cleared - Thread.currentThread().getAndClearInterrupt(); - throw e; - } finally { - Blocker.end(attempted); } } + // final modifier so method not in vtable + private final native void wait0(long timeoutMillis) throws InterruptedException; + /** - * Causes current thread to wait until another thread invokes the - * {@link java.lang.Object#notify()} method or the - * {@link java.lang.Object#notifyAll()} method for this object, or - * some other thread interrupts the current thread, or a certain - * amount of real time has elapsed. - *

- * This method is similar to the wait method of one - * argument, but it allows finer control over the amount of time to - * wait for a notification before giving up. The amount of real time, - * measured in nanoseconds, is given by: - *

- *
-     * 1000000*timeout+nanos
- *

- * In all other respects, this method does the same thing as the - * method {@link #wait(long)} of one argument. In particular, - * wait(0, 0) means the same thing as wait(0). - *

- * The current thread must own this object's monitor. The thread - * releases ownership of this monitor and waits until either of the - * following two conditions has occurred: + * Causes the current thread to wait until it is awakened, typically + * by being notified or interrupted, or until a + * certain amount of real time has elapsed. + *

+ * The current thread must own this object's monitor lock. See the + * {@link #notify notify} method for a description of the ways in which + * a thread can become the owner of a monitor lock. + *

+ * This method causes the current thread (referred to here as T) to + * place itself in the wait set for this object and then to relinquish any + * and all synchronization claims on this object. Note that only the locks + * on this object are relinquished; any other objects on which the current + * thread may be synchronized remain locked while the thread waits. + *

+ * Thread T then becomes disabled for thread scheduling purposes + * and lies dormant until one of the following occurs: *

    - *
  • Another thread notifies threads waiting on this object's monitor - * to wake up either through a call to the notify method - * or the notifyAll method. - *
  • The timeout period, specified by timeout - * milliseconds plus nanos nanoseconds arguments, has - * elapsed. + *
  • Some other thread invokes the {@code notify} method for this + * object and thread T happens to be arbitrarily chosen as + * the thread to be awakened. + *
  • Some other thread invokes the {@code notifyAll} method for this + * object. + *
  • Some other thread {@linkplain Thread#interrupt() interrupts} + * thread T. + *
  • The specified amount of real time has elapsed, more or less. + * The amount of real time, in nanoseconds, is given by the expression + * {@code 1000000 * timeoutMillis + nanos}. If {@code timeoutMillis} and {@code nanos} + * are both zero, then real time is not taken into consideration and the + * thread waits until awakened by one of the other causes. + *
  • Thread T is awakened spuriously. (See below.) *
*

- * The thread then waits until it can re-obtain ownership of the - * monitor and resumes execution. + * The thread T is then removed from the wait set for this + * object and re-enabled for thread scheduling. It competes in the + * usual manner with other threads for the right to synchronize on the + * object; once it has regained control of the object, all its + * synchronization claims on the object are restored to the status quo + * ante - that is, to the situation as of the time that the {@code wait} + * method was invoked. Thread T then returns from the + * invocation of the {@code wait} method. Thus, on return from the + * {@code wait} method, the synchronization state of the object and of + * thread {@code T} is exactly as it was when the {@code wait} method + * was invoked. *

- * As in the one argument version, interrupts and spurious wakeups are - * possible, and this method should always be used in a loop: - *

+     * A thread can wake up without being notified, interrupted, or timing out, a
+     * so-called spurious wakeup.  While this will rarely occur in practice,
+     * applications must guard against it by testing for the condition that should
+     * have caused the thread to be awakened, and continuing to wait if the condition
+     * is not satisfied. See the example below.
+     * 

+ * For more information on this topic, see section 14.2, + * "Condition Queues," in Brian Goetz and others' Java Concurrency + * in Practice (Addison-Wesley, 2006) or Item 81 in Joshua + * Bloch's Effective Java, Third Edition (Addison-Wesley, + * 2018). + *

+ * If the current thread is {@linkplain java.lang.Thread#interrupt() interrupted} + * by any thread before or while it is waiting, then an {@code InterruptedException} + * is thrown. The interrupted status of the current thread is cleared when + * this exception is thrown. This exception is not thrown until the lock status of + * this object has been restored as described above. + * + * @apiNote + * The recommended approach to waiting is to check the condition being awaited in + * a {@code while} loop around the call to {@code wait}, as shown in the example + * below. Among other things, this approach avoids problems that can be caused + * by spurious wakeups. + * + * {@snippet lang=java : * synchronized (obj) { - * while (<condition does not hold>) - * obj.wait(timeout, nanos); - * ... // Perform action appropriate to condition + * while ( ) { + * long timeoutMillis = ... ; // recompute timeout values + * int nanos = ... ; + * obj.wait(timeoutMillis, nanos); + * } + * ... // Perform action appropriate to condition or timeout * } - *

- * This method should only be called by a thread that is the owner - * of this object's monitor. See the notify method for a - * description of the ways in which a thread can become the owner of - * a monitor. + * } * - * @param timeout the maximum time to wait in milliseconds. - * @param nanos additional time, in nanoseconds range - * 0-999999. - * @exception IllegalArgumentException if the value of timeout is - * negative or the value of nanos is - * not in the range 0-999999. - * @exception IllegalMonitorStateException if the current thread is not - * the owner of this object's monitor. - * @exception InterruptedException if another thread interrupted the - * current thread before or while the current thread - * was waiting for a notification. The interrupted - * status of the current thread is cleared when - * this exception is thrown. + * @param timeoutMillis the maximum time to wait, in milliseconds + * @param nanos additional time, in nanoseconds, in the range 0-999999 inclusive + * @throws IllegalArgumentException if {@code timeoutMillis} is negative, + * or if the value of {@code nanos} is out of range + * @throws IllegalMonitorStateException if the current thread is not + * the owner of the object's monitor + * @throws InterruptedException if any thread interrupted the current thread before or + * while the current thread was waiting. The interrupted status of the + * current thread is cleared when this exception is thrown. + * @see #notify() + * @see #notifyAll() + * @see #wait() + * @see #wait(long) */ - public final void wait(long timeout, int nanos) throws InterruptedException { + public final void wait(long timeoutMillis, int nanos) throws InterruptedException { bi04t002a.instrInvoke(bi04t002a.INSTR_WAIT_JI); - if (timeout < 0) { - throw new IllegalArgumentException("timeout value is negative"); + if (timeoutMillis < 0) { + throw new IllegalArgumentException("timeoutMillis value is negative"); } if (nanos < 0 || nanos > 999999) { @@ -461,106 +502,114 @@ public final void wait(long timeout, int nanos) throws InterruptedException { "nanosecond timeout value out of range"); } - if (nanos >= 500000 || (nanos != 0 && timeout == 0)) { - timeout++; - } - - wait(timeout); - } - - // final modifier so method not in vtable - private final native void wait0(long timeoutMillis) throws InterruptedException; + if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) { + timeoutMillis++; + } - /** - * Causes current thread to wait until another thread invokes the - * {@link java.lang.Object#notify()} method or the - * {@link java.lang.Object#notifyAll()} method for this object. - * In other words, this method behaves exactly as if it simply - * performs the call wait(0). - *

- * The current thread must own this object's monitor. The thread - * releases ownership of this monitor and waits until another thread - * notifies threads waiting on this object's monitor to wake up - * either through a call to the notify method or the - * notifyAll method. The thread then waits until it can - * re-obtain ownership of the monitor and resumes execution. - *

- * As in the one argument version, interrupts and spurious wakeups are - * possible, and this method should always be used in a loop: - *

-     *     synchronized (obj) {
-     *         while (<condition does not hold>)
-     *             obj.wait();
-     *         ... // Perform action appropriate to condition
-     *     }
-     * 
- * This method should only be called by a thread that is the owner - * of this object's monitor. See the notify method for a - * description of the ways in which a thread can become the owner of - * a monitor. - * - * @exception IllegalMonitorStateException if the current thread is not - * the owner of the object's monitor. - * @exception InterruptedException if another thread interrupted the - * current thread before or while the current thread - * was waiting for a notification. The interrupted - * status of the current thread is cleared when - * this exception is thrown. - * @see java.lang.Object#notify() - * @see java.lang.Object#notifyAll() - */ - public final void wait() throws InterruptedException { - bi04t002a.instrInvoke(bi04t002a.INSTR_WAIT); - wait(0); + wait(timeoutMillis); } /** * Called by the garbage collector on an object when garbage collection * determines that there are no more references to the object. - * A subclass overrides the finalize method to dispose of + * A subclass overrides the {@code finalize} method to dispose of * system resources or to perform other cleanup. *

- * The general contract of finalize is that it is invoked - * if and when the JavaTM virtual + * When running in a Java virtual machine in which finalization has been + * disabled or removed, the garbage collector will never call + * {@code finalize()}. In a Java virtual machine in which finalization is + * enabled, the garbage collector might call {@code finalize} only after an + * indefinite delay. + *

+ * The general contract of {@code finalize} is that it is invoked + * if and when the Java virtual * machine has determined that there is no longer any * means by which this object can be accessed by any thread that has * not yet died, except as a result of an action taken by the * finalization of some other object or class which is ready to be - * finalized. The finalize method may take any action, including + * finalized. The {@code finalize} method may take any action, including * making this object available again to other threads; the usual purpose - * of finalize, however, is to perform cleanup actions before + * of {@code finalize}, however, is to perform cleanup actions before * the object is irrevocably discarded. For example, the finalize method * for an object that represents an input/output connection might perform * explicit I/O transactions to break the connection before the object is * permanently discarded. *

- * The finalize method of class Object performs no + * The {@code finalize} method of class {@code Object} performs no * special action; it simply returns normally. Subclasses of - * Object may override this definition. + * {@code Object} may override this definition. *

* The Java programming language does not guarantee which thread will - * invoke the finalize method for any given object. It is + * invoke the {@code finalize} method for any given object. It is * guaranteed, however, that the thread that invokes finalize will not * be holding any user-visible synchronization locks when finalize is * invoked. If an uncaught exception is thrown by the finalize method, * the exception is ignored and finalization of that object terminates. *

- * After the finalize method has been invoked for an object, no + * After the {@code finalize} method has been invoked for an object, no * further action is taken until the Java virtual machine has again * determined that there is no longer any means by which this object can * be accessed by any thread that has not yet died, including possible * actions by other objects or classes which are ready to be finalized, * at which point the object may be discarded. *

- * The finalize method is never invoked more than once by a Java + * The {@code finalize} method is never invoked more than once by a Java * virtual machine for any given object. *

- * Any exception thrown by the finalize method causes + * Any exception thrown by the {@code finalize} method causes * the finalization of this object to be halted, but is otherwise * ignored. * - * @throws Throwable the Exception raised by this method + * @apiNote + * Classes that embed non-heap resources have many options + * for cleanup of those resources. The class must ensure that the + * lifetime of each instance is longer than that of any resource it embeds. + * {@link java.lang.ref.Reference#reachabilityFence} can be used to ensure that + * objects remain reachable while resources embedded in the object are in use. + *

+ * A subclass should avoid overriding the {@code finalize} method + * unless the subclass embeds non-heap resources that must be cleaned up + * before the instance is collected. + * Finalizer invocations are not automatically chained, unlike constructors. + * If a subclass overrides {@code finalize} it must invoke the superclass + * finalizer explicitly. + * To guard against exceptions prematurely terminating the finalize chain, + * the subclass should use a {@code try-finally} block to ensure + * {@code super.finalize()} is always invoked. For example, + * {@snippet lang="java": + * @Override + * protected void finalize() throws Throwable { + * try { + * ... // cleanup subclass state + * } finally { + * super.finalize(); + * } + * } + * } + * + * @deprecated Finalization is deprecated and subject to removal in a future + * release. The use of finalization can lead to problems with security, + * performance, and reliability. + * See JEP 421 for + * discussion and alternatives. + *

+ * Subclasses that override {@code finalize} to perform cleanup should use + * alternative cleanup mechanisms and remove the {@code finalize} method. + * Use {@link java.lang.ref.Cleaner} and + * {@link java.lang.ref.PhantomReference} as safer ways to release resources + * when an object becomes unreachable. Alternatively, add a {@code close} + * method to explicitly release resources, and implement + * {@code AutoCloseable} to enable use of the {@code try}-with-resources + * statement. + *

+ * This method will remain in place until finalizers have been removed from + * most existing code. + * + * @throws Throwable the {@code Exception} raised by this method + * @see java.lang.ref.WeakReference + * @see java.lang.ref.PhantomReference + * @jls 12.6 Finalization of Class Instances */ + @Deprecated(since="9", forRemoval=true) protected void finalize() throws Throwable { } - } diff --git a/test/jdk/ProblemList-Xcomp.txt b/test/jdk/ProblemList-Xcomp.txt index 02f81b13badc3..680806e6e31b9 100644 --- a/test/jdk/ProblemList-Xcomp.txt +++ b/test/jdk/ProblemList-Xcomp.txt @@ -28,4 +28,5 @@ ############################################################################# java/lang/invoke/MethodHandles/CatchExceptionTest.java 8146623 generic-all +java/lang/reflect/callerCache/ReflectionCallerCacheTest.java 8332028 generic-all com/sun/jdi/InterruptHangTest.java 8043571 generic-all diff --git a/test/jdk/java/lang/Thread/virtual/ActivateSpareCarrier.java b/test/jdk/java/lang/Thread/virtual/ActivateSpareCarrier.java deleted file mode 100644 index 3ffda23082c87..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/ActivateSpareCarrier.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @bug 8292240 - * @summary Test the scenario where a blocking operation pins a virtual thread to its - * carrier thread (cT1) and doesn't activate a spare. Subsequent blocking operations - * that pin a virtual thread to cT1 should attempt to activate a spare. - * @requires vm.continuations - * @run main/othervm - * -Djdk.virtualThreadScheduler.parallelism=1 - * -Djdk.virtualThreadScheduler.maxPoolSize=2 ActivateSpareCarrier 100 - */ - -import java.time.Duration; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.ForkJoinWorkerThread; -import java.util.stream.Collectors; - -public class ActivateSpareCarrier { - - private static final int DEFAULT_ITERTAIONS = 10_000; - - private static final Object LOCK = new Object(); - - public static void main(String[] args) throws Exception { - int iterations; - if (args.length == 0) { - iterations = DEFAULT_ITERTAIONS; - } else { - iterations = Integer.parseInt(args[0]); - } - for (int i = 0; i < iterations; i++) { - test(i); - } - } - - /** - * This method creates 3 virtual threads: - * - thread1 blocks in Object.wait, activating a spare carrier thread - * - thread2 is started and runs on the spare carrier thread - * - thread1 is notified causing it to re-adjust the release count and terminate - * - thread3 is started and should run on the one active thread - * - * This method need invoked at least twice in the same VM. - */ - private static void test(int i) throws Exception { - System.out.printf("---- %d ----%n", i); - - // thread1 blocks in wait, this triggers a tryCompensate to activate a spare thread - Thread thread1 = Thread.ofVirtual().unstarted(() -> { - System.out.println(Thread.currentThread()); - synchronized (LOCK) { - try { - LOCK.wait(); - } catch (InterruptedException e) { } - } - }); - System.out.printf("starting waiter thread #%d%n", thread1.threadId()); - thread1.start(); - - // wait for thread1 to block in Object.wait - while (thread1.getState() != Thread.State.WAITING) { - Thread.sleep(10); - } - - // start another virtual thread, it should run on the spare carrier thread - startAndJoinVirtualThread(); - - // notify thread1, this releases the blocker - synchronized (LOCK) { - LOCK.notifyAll(); - } - joinThread(thread1); - - // start another virtual thread after counts have been re-adjusted - startAndJoinVirtualThread(); - } - - /** - * Start a virtual thread and wait for it to terminate. - */ - private static void startAndJoinVirtualThread() throws InterruptedException { - Thread thread = Thread.ofVirtual().unstarted(() -> { - System.out.println(Thread.currentThread()); - }); - System.out.format("starting #%d%n", thread.threadId()); - thread.start(); - joinThread(thread); - } - - /** - * Wait for the give thread to terminate with diagnostic output if the thread does - * not terminate quickly. - */ - private static void joinThread(Thread thread) throws InterruptedException { - long tid = thread.threadId(); - System.out.printf("Waiting for #%d to terminate%n", tid); - boolean terminated = thread.join(Duration.ofSeconds(2)); - if (!terminated) { - System.out.printf("#%d did not terminate quickly, continue to wait...%n", tid); - printForkJoinWorkerThreads(); - thread.join(); - } - System.out.printf("#%d terminated%n", tid); - } - - /** - * Print the list of ForkJoinWorkerThreads and their stack traces. - */ - private static void printForkJoinWorkerThreads() { - List threads = Thread.getAllStackTraces().keySet().stream() - .filter(t -> t instanceof ForkJoinWorkerThread) - .sorted(Comparator.comparingLong(Thread::threadId)) - .collect(Collectors.toList()); - System.out.println("ForkJoinWorkerThreads:"); - for (Thread t : threads) { - System.out.printf(" %s%n", t); - StackTraceElement[] stack = t.getStackTrace(); - for (StackTraceElement e : stack) { - System.out.printf(" %s%n", e); - } - } - } -} diff --git a/test/jdk/java/lang/Thread/virtual/CancelTimerWithContention.java b/test/jdk/java/lang/Thread/virtual/CancelTimerWithContention.java new file mode 100644 index 0000000000000..840e40453c627 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/CancelTimerWithContention.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test cancelling a timeout task for Object.wait(millis) when there is + * contention on the timer queue + * @requires vm.continuations + * @key randomness + * @run main/othervm + * -Djdk.virtualThreadScheduler.parallelism=2 + * -Djdk.virtualThreadScheduler.timerQueues=1 + * CancelTimerWithContention + */ + +import java.time.Instant; +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +public class CancelTimerWithContention { + + // number of threads + private static final int MIN_THREADS = 100; + private static final int MAX_THREADS = 10_000; + + // number of monitors to enter + private static final int MIN_MONITORS = 2; + private static final int MAX_MONITORS = 8; + + private static final Random RAND = new Random(); + + public static void main(String[] args) { + for (int threadCount = MIN_THREADS; threadCount <= MAX_THREADS; threadCount += 100) { + System.out.format("%s #threads = %d%n", Instant.now(), threadCount); + for (int lockCount = MIN_MONITORS; lockCount <= MAX_MONITORS; lockCount += 2) { + test(threadCount, lockCount); + } + } + } + + /** + * Test threads entering monitors and using Object.wait(millis) to wait. This + * testing scenario leads to a mix of contention (with responsible threads using + * short timeouts), timed-wait, and cancellation of timer tasks. This scenario + * can result in contention of the timer queue. + */ + static void test(int threadCount, int monitorCount) { + var locks = new Object[monitorCount]; + for (int i = 0; i < monitorCount; i++) { + locks[i] = new Object(); + } + + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + var finished = new AtomicInteger(); + + for (int i = 0; i < threadCount; i++) { + Object lock = locks[RAND.nextInt(monitorCount)]; // random lock + + executor.submit(() -> { + synchronized (lock) { + lock.wait(Long.MAX_VALUE); + } + finished.incrementAndGet(); + return null; + }); + + synchronized (lock) { + lock.notify(); + } + } + + // notify at most one thread until all threads are finished + while (finished.get() < threadCount) { + for (Object lock : locks) { + synchronized (lock) { + lock.notify(); + } + } + } + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/JfrEvents.java b/test/jdk/java/lang/Thread/virtual/JfrEvents.java index bc952331f6446..a1671fe3cc5b9 100644 --- a/test/jdk/java/lang/Thread/virtual/JfrEvents.java +++ b/test/jdk/java/lang/Thread/virtual/JfrEvents.java @@ -62,11 +62,8 @@ class JfrEvents { @BeforeAll static void setup() { - int minParallelism = 2; - if (Thread.currentThread().isVirtual()) { - minParallelism++; - } - VThreadRunner.ensureParallelism(minParallelism); + // need at least two carriers to test pinning + VThreadRunner.ensureParallelism(2); } /** @@ -140,6 +137,223 @@ void testParkWhenPinned(boolean timed) throws Exception { } } + /** + * Test jdk.VirtualThreadPinned event when blocking on monitor while pinned. + */ + @Test + void testBlockWhenPinned() throws Exception { + try (Recording recording = new Recording()) { + recording.enable("jdk.VirtualThreadPinned"); + recording.start(); + + Object lock = new Object(); + + var started = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + VThreadPinner.runPinned(() -> { + started.set(true); + synchronized (lock) { } + }); + }); + + try { + synchronized (lock) { + vthread.start(); + // wait for thread to start and block + awaitTrue(started); + await(vthread, Thread.State.BLOCKED); + } + } finally { + vthread.join(); + recording.stop(); + } + + assertContainsPinnedEvent(recording, vthread); + } + } + + /** + * Test jdk.VirtualThreadPinned event when waiting with Object.wait while pinned. + */ + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testObjectWaitWhenPinned(boolean timed) throws Exception { + try (Recording recording = new Recording()) { + recording.enable("jdk.VirtualThreadPinned"); + recording.start(); + + Object lock = new Object(); + + var started = new AtomicBoolean(); + var vthread = Thread.startVirtualThread(() -> { + VThreadPinner.runPinned(() -> { + started.set(true); + synchronized (lock) { + try { + if (timed) { + lock.wait(Long.MAX_VALUE); + } else { + lock.wait(); + } + } catch (InterruptedException e) { + fail(); + } + } + }); + }); + + try { + // wait for thread to start and wait + awaitTrue(started); + await(vthread, timed ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + } finally { + synchronized (lock) { + lock.notifyAll(); + } + vthread.join(); + recording.stop(); + } + + assertContainsPinnedEvent(recording, vthread); + } + } + + /** + * Test jdk.VirtualThreadPinned event when parking in a class initializer. + */ + @Test + void testParkInClassInitializer() throws Exception { + class TestClass { + static { + LockSupport.park(); + } + static void m() { + // do nothing + } + } + + try (Recording recording = new Recording()) { + recording.enable("jdk.VirtualThreadPinned"); + recording.start(); + + var started = new AtomicBoolean(); + Thread vthread = Thread.startVirtualThread(() -> { + started.set(true); + TestClass.m(); + }); + + try { + // wait for it to start and park + awaitTrue(started); + await(vthread, Thread.State.WAITING); + } finally { + LockSupport.unpark(vthread); + vthread.join(); + recording.stop(); + } + + assertContainsPinnedEvent(recording, vthread); + } + } + + /** + * Test jdk.VirtualThreadPinned event when blocking on monitor in a class initializer. + */ + @Test + void testBlockInClassInitializer() throws Exception { + class LockHolder { + static final Object lock = new Object(); + } + class TestClass { + static { + synchronized (LockHolder.lock) { } + } + static void m() { + // no nothing + } + } + + try (Recording recording = new Recording()) { + recording.enable("jdk.VirtualThreadPinned"); + recording.start(); + + var started = new AtomicBoolean(); + Thread vthread = Thread.ofVirtual().unstarted(() -> { + started.set(true); + TestClass.m(); + }); + + try { + synchronized (LockHolder.lock) { + vthread.start(); + // wait for thread to start and block + awaitTrue(started); + await(vthread, Thread.State.BLOCKED); + } + } finally { + vthread.join(); + recording.stop(); + } + + assertContainsPinnedEvent(recording, vthread); + } + } + + /** + * Test jdk.VirtualThreadPinned event when waiting for a class initializer. + */ + @Test + void testWaitingForClassInitializer() throws Exception { + class TestClass { + static { + LockSupport.park(); + } + static void m() { + // do nothing + } + } + + try (Recording recording = new Recording()) { + recording.enable("jdk.VirtualThreadPinned"); + recording.start(); + + var started1 = new AtomicBoolean(); + var started2 = new AtomicBoolean(); + + Thread vthread1 = Thread.ofVirtual().unstarted(() -> { + started1.set(true); + TestClass.m(); + }); + Thread vthread2 = Thread.ofVirtual().unstarted(() -> { + started2.set(true); + TestClass.m(); + }); + + try { + // start first virtual thread and wait for it to start + park + vthread1.start(); + awaitTrue(started1); + await(vthread1, Thread.State.WAITING); + + // start second virtual thread and wait for it to start + vthread2.start(); + awaitTrue(started2); + + // give time for second virtual thread to wait on the MutexLocker + Thread.sleep(3000); + + } finally { + LockSupport.unpark(vthread1); + vthread1.join(); + vthread2.join(); + recording.stop(); + } + + // the recording should have a pinned event for vthread2 + assertContainsPinnedEvent(recording, vthread2); + } + } + /** * Test jdk.VirtualThreadSubmitFailed event. */ diff --git a/src/java.base/share/classes/jdk/internal/event/VirtualThreadPinnedEvent.java b/test/jdk/java/lang/Thread/virtual/LockingMode.java similarity index 62% rename from src/java.base/share/classes/jdk/internal/event/VirtualThreadPinnedEvent.java rename to test/jdk/java/lang/Thread/virtual/LockingMode.java index 73746dc4abc8f..59fde4a5f4928 100644 --- a/src/java.base/share/classes/jdk/internal/event/VirtualThreadPinnedEvent.java +++ b/test/jdk/java/lang/Thread/virtual/LockingMode.java @@ -1,12 +1,10 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -22,10 +20,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.internal.event; -/** - * Event recording that a virtual thread has parked on its carrier thread. - */ -public class VirtualThreadPinnedEvent extends Event { -} +import java.lang.management.ManagementFactory; +import com.sun.management.HotSpotDiagnosticMXBean; + +class LockingMode { + private LockingMode() { } + + /** + * Returns true if using legacy locking mode. + */ + static boolean isLegacy() { + return ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class) + .getVMOption("LockingMode") + .getValue() + .equals("1"); + } +} \ No newline at end of file diff --git a/test/jdk/java/lang/Thread/virtual/MiscMonitorTests.java b/test/jdk/java/lang/Thread/virtual/MiscMonitorTests.java new file mode 100644 index 0000000000000..1f909b214c29c --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/MiscMonitorTests.java @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=default + * @summary Tests for object monitors that have been useful to find bugs + * @library /test/lib + * @requires vm.continuations & vm.opt.LockingMode != 1 + * @modules java.base/java.lang:+open + * @run junit/othervm MiscMonitorTests + */ + +/* + * @test id=Xint + * @library /test/lib + * @requires vm.continuations & vm.opt.LockingMode != 1 + * @modules java.base/java.lang:+open + * @run junit/othervm -Xint MiscMonitorTests + */ + +/* + * @test id=Xcomp + * @library /test/lib + * @requires vm.continuations & vm.opt.LockingMode != 1 + * @modules java.base/java.lang:+open + * @run junit/othervm -Xcomp MiscMonitorTests + */ + +/* + * @test id=Xcomp-TieredStopAtLevel3 + * @library /test/lib + * @requires vm.continuations & vm.opt.LockingMode != 1 + * @modules java.base/java.lang:+open + * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=3 MiscMonitorTests + */ + +/* + * @test id=Xcomp-noTieredCompilation + * @summary Test virtual threads using synchronized + * @library /test/lib + * @requires vm.continuations & vm.opt.LockingMode != 1 + * @modules java.base/java.lang:+open + * @run junit/othervm -Xcomp -XX:-TieredCompilation MiscMonitorTests + */ + +/* + * @test id=gc + * @requires vm.debug == true & vm.continuations & vm.opt.LockingMode != 1 + * @library /test/lib + * @modules java.base/java.lang:+open + * @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:+FullGCALot -XX:FullGCALotInterval=1000 MiscMonitorTests + */ + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.*; +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class MiscMonitorTests { + static final int CARRIER_COUNT = Runtime.getRuntime().availableProcessors(); + + /** + * Test that yielding while holding monitors releases carrier. + */ + @Test + void testReleaseOnYield() throws Exception { + try (var test = new TestReleaseOnYield()) { + test.runTest(); + } + } + + private static class TestReleaseOnYield extends TestBase { + final Object lock = new Object(); + volatile boolean finish; + volatile int counter; + + @Override + void runTest() throws Exception { + int vthreadCount = CARRIER_COUNT; + + startVThreads(() -> foo(), vthreadCount, "Batch1"); + sleep(500); // Give time for threads to reach Thread.yield + startVThreads(() -> bar(), vthreadCount, "Batch2"); + + while (counter != vthreadCount) { + Thread.onSpinWait(); + } + finish = true; + joinVThreads(); + } + + void foo() { + Object lock = new Object(); + synchronized (lock) { + while (!finish) { + Thread.yield(); + } + } + System.err.println("Exiting foo from thread " + Thread.currentThread().getName()); + } + + void bar() { + synchronized (lock) { + counter++; + } + System.err.println("Exiting bar from thread " + Thread.currentThread().getName()); + } + } + + /** + * Test yielding while holding monitors with recursive locking releases carrier. + */ + @Test + void testReleaseOnYieldRecursive() throws Exception { + try (var test = new TestReleaseOnYieldRecursive()) { + test.runTest(); + } + } + + private static class TestReleaseOnYieldRecursive extends TestBase { + final Object lock = new Object(); + volatile boolean finish; + volatile int counter; + + @Override + void runTest() throws Exception { + int vthreadCount = CARRIER_COUNT; + + startVThreads(() -> foo(), vthreadCount, "Batch1"); + sleep(500); // Give time for threads to reach Thread.yield + startVThreads(() -> bar(), vthreadCount, "Batch2"); + + while (counter != 2 * vthreadCount) { + Thread.onSpinWait(); + } + finish = true; + joinVThreads(); + } + + void foo() { + Object lock = new Object(); + synchronized (lock) { + while (!finish) { + Thread.yield(); + } + } + System.err.println("Exiting foo from thread " + Thread.currentThread().getName()); + } + + void bar() { + synchronized (lock) { + counter++; + } + recursive(10); + System.err.println("Exiting bar from thread " + Thread.currentThread().getName()); + }; + + void recursive(int count) { + synchronized (Thread.currentThread()) { + if (count > 0) { + recursive(count - 1); + } else { + synchronized (lock) { + counter++; + Thread.yield(); + } + } + } + } + } + + /** + * Test that contention on monitorenter releases carrier. + */ + @Test + void testReleaseOnContention() throws Exception { + try (var test = new TestReleaseOnContention()) { + test.runTest(); + } + } + + private static class TestReleaseOnContention extends TestBase { + final Object lock = new Object(); + volatile boolean finish; + volatile int counter; + + @Override + void runTest() throws Exception { + int vthreadCount = CARRIER_COUNT * 8; + + startVThreads(() -> foo(), vthreadCount, "VThread"); + sleep(500); // Give time for threads to reach synchronized (lock) + + finish = true; + joinVThreads(); + } + + void foo() { + synchronized (lock) { + while (!finish) { + Thread.yield(); + } + } + System.err.println("Exiting foo from thread " + Thread.currentThread().getName()); + } + } + + /** + * Test contention on monitorenter with extra monitors on stack shared by all threads. + */ + @Test + void testContentionMultipleMonitors() throws Exception { + try (var test = new TestContentionMultipleMonitors()) { + test.runTest(); + } + } + + private static class TestContentionMultipleMonitors extends TestBase { + static int MONITOR_COUNT = 12; + final Object[] lockArray = new Object[MONITOR_COUNT]; + final AtomicInteger workerCount = new AtomicInteger(0); + volatile boolean finish; + + @Override + void runTest() throws Exception { + int vthreadCount = CARRIER_COUNT * 8; + for (int i = 0; i < MONITOR_COUNT; i++) { + lockArray[i] = new Object(); + } + + startVThreads(() -> foo(), vthreadCount, "VThread"); + + sleep(5000); + finish = true; + joinVThreads(); + assertEquals(vthreadCount, workerCount.get()); + } + + void foo() { + while (!finish) { + int lockNumber = ThreadLocalRandom.current().nextInt(0, MONITOR_COUNT - 1); + synchronized (lockArray[lockNumber]) { + recursive1(lockNumber, lockNumber); + } + } + workerCount.getAndIncrement(); + System.err.println("Exiting foo from thread " + Thread.currentThread().getName()); + } + + public void recursive1(int depth, int lockNumber) { + if (depth > 0) { + recursive1(depth - 1, lockNumber); + } else { + if (Math.random() < 0.5) { + Thread.yield(); + } + recursive2(lockNumber); + } + } + + public void recursive2(int lockNumber) { + if (lockNumber + 2 <= MONITOR_COUNT - 1) { + lockNumber += 2; + synchronized (lockArray[lockNumber]) { + Thread.yield(); + recursive2(lockNumber); + } + } + } + } + + /** + * Test contention on monitorenter with extra monitors on stack both local only and shared by all threads. + */ + @Test + void testContentionMultipleMonitors2() throws Exception { + try (var test = new TestContentionMultipleMonitors2()) { + test.runTest(); + } + } + + private static class TestContentionMultipleMonitors2 extends TestBase { + int MONITOR_COUNT = 12; + final Object[] lockArray = new Object[MONITOR_COUNT]; + final AtomicInteger workerCount = new AtomicInteger(0); + volatile boolean finish; + + @Override + void runTest() throws Exception { + int vthreadCount = CARRIER_COUNT * 8; + for (int i = 0; i < MONITOR_COUNT; i++) { + lockArray[i] = new Object(); + } + + startVThreads(() -> foo(), vthreadCount, "VThread"); + + sleep(5000); + finish = true; + joinVThreads(); + assertEquals(vthreadCount, workerCount.get()); + } + + void foo() { + Object[] myLockArray = new Object[MONITOR_COUNT]; + for (int i = 0; i < MONITOR_COUNT; i++) { + myLockArray[i] = new Object(); + } + + while (!finish) { + int lockNumber = ThreadLocalRandom.current().nextInt(0, MONITOR_COUNT - 1); + synchronized (myLockArray[lockNumber]) { + synchronized (lockArray[lockNumber]) { + recursive1(lockNumber, lockNumber, myLockArray); + } + } + } + workerCount.getAndIncrement(); + System.err.println("Exiting foo from thread " + Thread.currentThread().getName()); + } + + public void recursive1(int depth, int lockNumber, Object[] myLockArray) { + if (depth > 0) { + recursive1(depth - 1, lockNumber, myLockArray); + } else { + if (Math.random() < 0.5) { + Thread.yield(); + } + recursive2(lockNumber, myLockArray); + } + } + + public void recursive2(int lockNumber, Object[] myLockArray) { + if (lockNumber + 2 <= MONITOR_COUNT - 1) { + lockNumber += 2; + synchronized (myLockArray[lockNumber]) { + if (Math.random() < 0.5) { + Thread.yield(); + } + synchronized (lockArray[lockNumber]) { + Thread.yield(); + recursive2(lockNumber, myLockArray); + } + } + } + } + } + + + /** + * Test contention on monitorenter with synchronized methods. + */ + @Test + void testContentionWithSyncMethods() throws Exception { + try (var test = new TestContentionWithSyncMethods()) { + test.runTest(); + } + } + + private static class TestContentionWithSyncMethods extends TestBase { + static final int MONITOR_COUNT = 12; + final Object[] lockArray = new Object[MONITOR_COUNT]; + final AtomicInteger workerCount = new AtomicInteger(0); + volatile boolean finish; + + @Override + void runTest() throws Exception { + int vthreadCount = CARRIER_COUNT * 8; + for (int i = 0; i < MONITOR_COUNT; i++) { + lockArray[i] = new Object(); + } + + startVThreads(() -> foo(), vthreadCount, "VThread"); + + sleep(5000); + finish = true; + joinVThreads(); + assertEquals(vthreadCount, workerCount.get()); + } + + void foo() { + Object myLock = new Object(); + + while (!finish) { + int lockNumber = ThreadLocalRandom.current().nextInt(0, MONITOR_COUNT - 1); + synchronized (myLock) { + synchronized (lockArray[lockNumber]) { + recursive(lockNumber, myLock); + } + } + } + workerCount.getAndIncrement(); + System.err.println("Exiting foo from thread " + Thread.currentThread().getName()); + }; + + synchronized void recursive(int depth, Object myLock) { + if (depth > 0) { + recursive(depth - 1, myLock); + } else { + if (Math.random() < 0.5) { + Thread.yield(); + } else { + synchronized (myLock) { + Thread.yield(); + } + } + } + } + } + + /** + * Test wait/notify mechanism. + */ + @Test + void waitNotifyTest() throws Exception { + int threadCount = 1000; + int waitTime = 50; + Thread[] vthread = new Thread[threadCount]; + long start = System.currentTimeMillis(); + + while (System.currentTimeMillis() - start < 5000) { + CountDownLatch latchStart = new CountDownLatch(threadCount); + CountDownLatch latchFinish = new CountDownLatch(threadCount); + Object object = new Object(); + + for (int i = 0; i < threadCount; i++) { + vthread[i] = Thread.ofVirtual().start(() -> { + synchronized (object) { + try { + latchStart.countDown(); + object.wait(waitTime); + } catch (InterruptedException e) { + //do nothing; + } + } + latchFinish.countDown(); + }); + } + + try { + latchStart.await(); + synchronized (object) { + object.notifyAll(); + } + latchFinish.await(); + for (int i = 0; i < threadCount; i++) { + vthread[i].join(); + } + } catch (InterruptedException e) { + //do nothing; + } + } + } + + private static abstract class TestBase implements AutoCloseable { + final ExecutorService scheduler = Executors.newFixedThreadPool(CARRIER_COUNT); + final List vthreadList = new ArrayList<>(); + + abstract void runTest() throws Exception; + + void startVThreads(Runnable r, int count, String name) { + Thread vthreads[] = new Thread[count]; + for (int i = 0; i < count; i++) { + vthreads[i] = VThreadScheduler.virtualThreadBuilder(scheduler).name(name + "-" + i).start(r); + } + vthreadList.add(vthreads); + } + + void joinVThreads() throws Exception { + for (Thread[] vthreads : vthreadList) { + for (Thread vthread : vthreads) { + vthread.join(); + } + } + } + + void sleep(long ms) throws Exception { + Thread.sleep(ms); + } + + @Override + public void close() { + scheduler.close(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java b/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java index c2389ab7ddd3e..f095fb2cf9843 100644 --- a/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java +++ b/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java @@ -26,9 +26,90 @@ * @summary Test virtual thread with monitor enter/exit * @modules java.base/java.lang:+open jdk.management * @library /test/lib + * @build LockingMode * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorEnterExit */ +/* + * @test id=LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xint-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xint -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xint-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xint -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xcomp-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xcomp-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xcomp-TieredStopAtLevel1-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xcomp-TieredStopAtLevel1-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xcomp-noTieredCompilation-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +/* + * @test id=Xcomp-noTieredCompilation-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -48,23 +129,22 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.condition.DisabledIf; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.api.condition.*; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.*; class MonitorEnterExit { + static final int MAX_VTHREAD_COUNT = 4 * Runtime.getRuntime().availableProcessors(); static final int MAX_ENTER_DEPTH = 256; @BeforeAll static void setup() { - // need >=2 carriers for testing pinning when main thread is a virtual thread - if (Thread.currentThread().isVirtual()) { - VThreadRunner.ensureParallelism(2); - } + // need >=2 carriers for tests that pin + VThreadRunner.ensureParallelism(2); } /** @@ -150,6 +230,53 @@ private void testReenter(Object lock, int depth) { } } + /** + * Test monitor reenter when there are other threads blocked trying to enter. + */ + @Test + @DisabledIf("LockingMode#isLegacy") + void testReenterWithContention() throws Exception { + var lock = new Object(); + VThreadRunner.run(() -> { + List threads = new ArrayList<>(); + testReenter(lock, 0, threads); + + // wait for threads to terminate + for (Thread vthread : threads) { + vthread.join(); + } + }); + } + + private void testReenter(Object lock, int depth, List threads) throws Exception { + if (depth < MAX_ENTER_DEPTH) { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + + // start platform or virtual thread that blocks waiting to enter + var started = new CountDownLatch(1); + ThreadFactory factory = ThreadLocalRandom.current().nextBoolean() + ? Thread.ofPlatform().factory() + : Thread.ofVirtual().factory(); + var thread = factory.newThread(() -> { + started.countDown(); + synchronized (lock) { + /* do nothing */ + } + }); + thread.start(); + + // wait for thread to start and block + started.await(); + await(thread, Thread.State.BLOCKED); + threads.add(thread); + + // test reenter + testReenter(lock, depth + 1, threads); + } + } + } + /** * Test monitor enter when pinned. */ @@ -197,13 +324,7 @@ void testContendedEnterWhenPinnedHeldByPlatformThread() throws Exception { */ @Test void testContendedEnterWhenPinnedHeldByVirtualThread() throws Exception { - // need at least two carrier threads - int previousParallelism = VThreadRunner.ensureParallelism(2); - try { - VThreadRunner.run(this::testEnterWithContentionWhenPinned); - } finally { - VThreadRunner.setParallelism(previousParallelism); - } + VThreadRunner.run(this::testEnterWithContentionWhenPinned); } /** @@ -234,6 +355,79 @@ private void testEnterWithContentionWhenPinned() throws Exception { assertTrue(entered.get()); } + /** + * Test that blocking waiting to enter a monitor releases the carrier. + */ + @Test + @DisabledIf("LockingMode#isLegacy") + void testReleaseWhenBlocked() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + var lock = new Object(); + + // thread enters monitor + var started = new CountDownLatch(1); + var vthread1 = factory.newThread(() -> { + started.countDown(); + synchronized (lock) { + } + }); + + try { + synchronized (lock) { + // start thread and wait for it to block + vthread1.start(); + started.await(); + await(vthread1, Thread.State.BLOCKED); + + // carrier should be released, use it for another thread + var executed = new AtomicBoolean(); + var vthread2 = factory.newThread(() -> { + executed.set(true); + }); + vthread2.start(); + vthread2.join(); + assertTrue(executed.get()); + } + } finally { + vthread1.join(); + } + } + } + + /** + * Test lots of virtual threads blocked waiting to enter a monitor. If the number + * of virtual threads exceeds the number of carrier threads this test will hang if + * carriers aren't released. + */ + @Test + @DisabledIf("LockingMode#isLegacy") + void testManyBlockedThreads() throws Exception { + Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; + var lock = new Object(); + synchronized (lock) { + for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); + synchronized (lock) { + } + }); + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + vthreads[i] = vthread; + } + } + + // cleanup + for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { + vthreads[i].join(); + } + } + /** * Returns a stream of elements that are ordered pairs of platform and virtual thread * counts. 0,2,4,..16 platform threads. 2,4,6,..32 virtual threads. diff --git a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java index 81f1c1ea97f54..98bdc7fdedd00 100644 --- a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java +++ b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java @@ -26,9 +26,90 @@ * @summary Test virtual threads using Object.wait/notifyAll * @modules java.base/java.lang:+open jdk.management * @library /test/lib + * @build LockingMode * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify */ +/* + * @test id=LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xint-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xint -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xint-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xint -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xcomp-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xcomp-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xcomp-TieredStopAtLevel1-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xcomp-TieredStopAtLevel1-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xcomp-noTieredCompilation-LM_LEGACY + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + +/* + * @test id=Xcomp-noTieredCompilation-LM_LIGHTWEIGHT + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify + */ + import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -50,7 +131,10 @@ import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.*; @@ -203,6 +287,129 @@ void testNotifyBeforeWait(int timeout) throws Exception { thread.join(); } } + + /** + * Returns a stream of elements that are ordered pairs of platform and virtual thread + * counts. 0,2,4,..8 platform threads. 2,4,6,..16 virtual threads. + */ + static Stream threadCounts() { + return IntStream.range(0, 9) + .filter(i -> i % 2 == 0) + .mapToObj(i -> i) + .flatMap(np -> IntStream.range(2, 17) + .filter(i -> i % 2 == 0) + .mapToObj(vp -> Arguments.of(np, vp))); + } + + /** + * Test notify wakes only one thread when platform and virtual threads are waiting. + */ + @ParameterizedTest + @MethodSource("threadCounts") + @DisabledIf("LockingMode#isLegacy") + void testNotifyOneThread(int nPlatformThreads, int nVirtualThreads) throws Exception { + int nThreads = nPlatformThreads + nVirtualThreads; + + var lock = new Object(); + var ready = new CountDownLatch(nThreads); + var notified = new AtomicInteger(); + + Runnable waitTask = () -> { + synchronized (lock) { + try { + ready.countDown(); + lock.wait(); + notified.incrementAndGet(); + } catch (InterruptedException e) { } + } + }; + + var threads = new ArrayList(); + try { + for (int i = 0; i < nPlatformThreads; i++) { + threads.add(Thread.ofPlatform().start(waitTask)); + } + for (int i = 0; i < nVirtualThreads; i++) { + threads.add(Thread.ofVirtual().start(waitTask)); + } + + // wait for all threads to wait + ready.await(); + + // wake threads, one by one + for (int i = 0; i < threads.size(); i++) { + + // wake one thread + synchronized (lock) { + lock.notify(); + } + + // one thread should have awoken + int expectedWakeups = i + 1; + while (notified.get() < expectedWakeups) { + Thread.sleep(10); + } + assertEquals(expectedWakeups, notified.get()); + } + } finally { + for (Thread t : threads) { + t.interrupt(); + t.join(); + } + } + } + + /** + * Test notifyAll wakes all threads. + */ + @ParameterizedTest + @MethodSource("threadCounts") + @DisabledIf("LockingMode#isLegacy") + void testNotifyAllThreads(int nPlatformThreads, int nVirtualThreads) throws Exception { + int nThreads = nPlatformThreads + nVirtualThreads; + + var lock = new Object(); + var ready = new CountDownLatch(nThreads); + var notified = new CountDownLatch(nThreads); + + Runnable waitTask = () -> { + synchronized (lock) { + try { + ready.countDown(); + lock.wait(); + notified.countDown(); + } catch (InterruptedException e) { } + } + }; + + var threads = new ArrayList(); + try { + for (int i = 0; i < nPlatformThreads; i++) { + threads.add(Thread.ofPlatform().start(waitTask)); + } + for (int i = 0; i < nVirtualThreads; i++) { + threads.add(Thread.ofVirtual().start(waitTask)); + } + + // wait for all threads to wait + ready.await(); + + // wakeup all threads + synchronized (lock) { + lock.notifyAll(); + } + + // wait for all threads to have awoken + notified.await(); + + } finally { + for (Thread t : threads) { + t.interrupt(); + t.join(); + } + } + } + /** * Test duration of timed Object.wait. */ @@ -489,6 +696,97 @@ void testParkingPermitNotOffered() throws Exception { assertTrue(completed.get()); } + /** + * Test that Object.wait releases the carrier. This test uses a custom scheduler + * with one carrier thread. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + @DisabledIf("LockingMode#isLegacy") + void testReleaseWhenWaiting1(int timeout) throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + var lock = new Object(); + var ready = new AtomicBoolean(); + var completed = new AtomicBoolean(); + + var vthread1 = factory.newThread(() -> { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { + fail("wait interrupted"); + } + } + completed.set(true); + }); + vthread1.start(); + + // wait for vthread1 to start and wait + awaitTrue(ready); + await(vthread1, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // carrier should be released, use it for another thread + var executed = new AtomicBoolean(); + var vthread2 = factory.newThread(() -> { + executed.set(true); + }); + vthread2.start(); + vthread2.join(); + assertTrue(executed.get()); + + // wakeup vthread1 + synchronized (lock) { + lock.notifyAll(); + } + + vthread1.join(); + assertTrue(completed.get()); + } + } + + /** + * Test that Object.wait releases the carrier. This test arranges for 4*ncores - 1 + * virtual threads to wait. For long timeout and no timeout cases, all virtual threads + * will wait until they are notified. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 10, 20, 100, 500, 30000, Integer.MAX_VALUE }) + @DisabledIf("LockingMode#isLegacy") + void testReleaseWhenWaiting2(int timeout) throws Exception { + int VTHREAD_COUNT = 4 * Runtime.getRuntime().availableProcessors(); + CountDownLatch latch = new CountDownLatch(VTHREAD_COUNT); + Object lock = new Object(); + AtomicInteger counter = new AtomicInteger(0); + + for (int i = 0; i < VTHREAD_COUNT; i++) { + Thread.ofVirtual().name("vthread-" + i).start(() -> { + synchronized (lock) { + if (counter.incrementAndGet() == VTHREAD_COUNT) { + lock.notifyAll(); + } else { + try { + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) {} + } + } + latch.countDown(); + }); + } + latch.await(); + } + /** * Test that wait(long) throws IAE when timeout is negative. */ diff --git a/test/jdk/java/lang/Thread/virtual/Parking.java b/test/jdk/java/lang/Thread/virtual/Parking.java index 674f14744a68e..41f7f283bdd06 100644 --- a/test/jdk/java/lang/Thread/virtual/Parking.java +++ b/test/jdk/java/lang/Thread/virtual/Parking.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,23 +21,60 @@ * questions. */ -/** - * @test +/* + * @test id=default * @summary Test virtual threads using park/unpark + * @modules java.base/java.lang:+open jdk.management * @library /test/lib + * @build LockingMode * @run junit Parking */ +/* + * @test id=Xint + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xint Parking + */ + +/* + * @test id=Xcomp + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp Parking + */ + +/* + * @test id=Xcomp-noTieredCompilation + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @build LockingMode + * @run junit/othervm -Xcomp -XX:-TieredCompilation Parking + */ + import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; class Parking { - private static final Object lock = new Object(); + static final int MAX_VTHREAD_COUNT = 4 * Runtime.getRuntime().availableProcessors(); + static final Object lock = new Object(); /** * Park, unparked by platform thread. @@ -343,6 +380,91 @@ void testParkNanos11() throws Exception { }); } + /** + * Test that parking while holding a monitor releases the carrier. + */ + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @DisabledIf("LockingMode#isLegacy") + void testParkWhenHoldingMonitor(boolean reenter) throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + var lock = new Object(); + + // thread enters (and maybe reenters) a monitor and parks + var started = new CountDownLatch(1); + var vthread1 = factory.newThread(() -> { + started.countDown(); + synchronized (lock) { + if (reenter) { + synchronized (lock) { + LockSupport.park(); + } + } else { + LockSupport.park(); + } + } + }); + + vthread1.start(); + try { + // wait for thread to start and park + started.await(); + await(vthread1, Thread.State.WAITING); + + // carrier should be released, use it for another thread + var executed = new AtomicBoolean(); + var vthread2 = factory.newThread(() -> { + executed.set(true); + }); + vthread2.start(); + vthread2.join(); + assertTrue(executed.get()); + } finally { + LockSupport.unpark(vthread1); + vthread1.join(); + } + } + } + + /** + * Test lots of virtual threads parked while holding a monitor. If the number of + * virtual threads exceeds the number of carrier threads then this test will hang if + * parking doesn't release the carrier. + */ + @Test + @DisabledIf("LockingMode#isLegacy") + void testManyParkedWhenHoldingMonitor() throws Exception { + Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; + var done = new AtomicBoolean(); + for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { + var lock = new Object(); + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); + synchronized (lock) { + while (!done.get()) { + LockSupport.park(); + } + } + }); + // wait for thread to start and park + started.await(); + await(vthread, Thread.State.WAITING); + vthreads[i] = vthread; + } + + // cleanup + done.set(true); + for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { + var vthread = vthreads[i]; + LockSupport.unpark(vthread); + vthread.join(); + } + } + /** * Schedule a thread to be interrupted after a delay. */ @@ -357,4 +479,16 @@ private static void scheduleInterrupt(Thread thread, long delay) { }; new Thread(interruptTask).start(); } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } } diff --git a/test/jdk/java/lang/Thread/virtual/RetryMonitorEnterWhenPinned.java b/test/jdk/java/lang/Thread/virtual/RetryMonitorEnterWhenPinned.java new file mode 100644 index 0000000000000..1d889b7236925 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/RetryMonitorEnterWhenPinned.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test that a virtual thread waiting to enter a monitor, while pinning its + * carrier, will retry until it enters the monitor. This avoids starvation when the + * monitor is exited, an unmounted thread is the chosen successor, and the successor + * can't continue because there are no carriers available. + * @modules java.base/java.lang:+open + * @library /test/lib + * @requires vm.opt.LockingMode != 1 + * @run main/othervm --enable-native-access=ALL-UNNAMED RetryMonitorEnterWhenPinned + */ + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import jdk.test.lib.thread.VThreadPinner; + +public class RetryMonitorEnterWhenPinned { + public static void main(String[] args) throws Exception { + int iterations = (args.length > 0) ? Integer.parseInt(args[0]) : 10; + for (int i = 1; i <= iterations; i++) { + System.out.printf("%s -- iteration %d --%n", Instant.now(), i); + run(); + System.out.println(); + } + } + + static void run() throws Exception { + var threads = new ArrayList(); + + Object lock = new Object(); + synchronized (lock) { + + // start virtual threads that block on monitorenter + for (int i = 0; i < 100; i++) { + var started = new CountDownLatch(1); + Thread thread = Thread.startVirtualThread(() -> { + started.countDown(); + synchronized (lock) { + spin(20); + } + }); + + // wait for thread to start and block + started.await(); + await(thread, Thread.State.BLOCKED); + threads.add(thread); + } + + // start virtual threads that block on monitorenter while pinned + int carriersAvailable = Runtime.getRuntime().availableProcessors(); + if (Thread.currentThread().isVirtual()) { + carriersAvailable--; + } + for (int i = 0; i < 100; i++) { + var started = new CountDownLatch(1); + Thread thread = Thread.startVirtualThread(() -> { + started.countDown(); + VThreadPinner.runPinned(() -> { + synchronized (lock) { + spin(20); + } + }); + }); + + // if there are carriers available when wait until the thread blocks. + if (carriersAvailable > 0) { + System.out.printf("%s waiting for thread #%d to block%n", + Instant.now(), thread.threadId()); + started.await(); + await(thread, Thread.State.BLOCKED); + carriersAvailable--; + } + threads.add(thread); + } + + } // exit monitor + + // wait for all threads to terminate + int threadsRemaining = threads.size(); + while (threadsRemaining > 0) { + System.out.printf("%s waiting for %d threads to terminate%n", + Instant.now(), threadsRemaining); + int terminated = 0; + for (Thread t : threads) { + if (t.join(Duration.ofSeconds(1))) { + terminated++; + } + } + threadsRemaining = threads.size() - terminated; + } + System.out.printf("%s done%n", Instant.now()); + } + + /** + * Spin for the given number of milliseconds. + */ + static void spin(long millis) { + long nanos = TimeUnit.MILLISECONDS.toNanos(millis); + long start = System.nanoTime(); + while ((System.nanoTime() - start) < nanos) { + Thread.onSpinWait(); + } + } + + /** + * Wait for a thread to reach the expected state. + */ + static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assert state != Thread.State.TERMINATED : "Thread has terminated"; + Thread.sleep(10); + state = thread.getState(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/SynchronizedNative.java b/test/jdk/java/lang/Thread/virtual/SynchronizedNative.java new file mode 100644 index 0000000000000..578aefe6a3054 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/SynchronizedNative.java @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=default + * @summary Test virtual threads with a synchronized native method and a native method + * that enter/exits a monitor with JNI MonitorEnter/MonitorExit + * @requires vm.continuations + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @run junit/othervm --enable-native-access=ALL-UNNAMED SynchronizedNative + */ + +/* + * @test id=Xint + * @requires vm.continuations + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @run junit/othervm -Xint --enable-native-access=ALL-UNNAMED SynchronizedNative + */ + +/* + * @test id=Xcomp-TieredStopAtLevel1 + * @requires vm.continuations + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 --enable-native-access=ALL-UNNAMED SynchronizedNative + */ + +/* + * @test id=Xcomp-noTieredCompilation + * @requires vm.continuations + * @modules java.base/java.lang:+open jdk.management + * @library /test/lib + * @run junit/othervm -Xcomp -XX:-TieredCompilation --enable-native-access=ALL-UNNAMED SynchronizedNative + */ + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Phaser; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import jdk.test.lib.thread.VThreadPinner; +import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management +import jdk.test.lib.thread.VThreadScheduler; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; + +class SynchronizedNative { + + @BeforeAll + static void setup() throws Exception { + // need at least two carriers to test pinning + VThreadRunner.ensureParallelism(2); + System.loadLibrary("SynchronizedNative"); + } + + /** + * Test entering a monitor with a synchronized native method, no contention. + */ + @Test + void testEnter() throws Exception { + Object lock = this; + VThreadRunner.run(() -> { + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + }); + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test reentering a monitor with synchronized native method, no contention. + */ + @Test + void testReenter() throws Exception { + Object lock = this; + VThreadRunner.run(() -> { + + // enter, reenter with a synchronized native method + synchronized (lock) { + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + }); + assertTrue(Thread.holdsLock(lock)); + } + assertFalse(Thread.holdsLock(lock)); + + // enter with synchronized native method, renter with synchronized statement + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertTrue(Thread.holdsLock(lock)); + }); + assertFalse(Thread.holdsLock(lock)); + + // enter with synchronized native method, reenter with synchronized native method + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + }); + assertTrue(Thread.holdsLock(lock)); + }); + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test entering a monitor with a synchronized native method and with contention. + */ + @Test + void testEnterWithContention() throws Exception { + var lock = this; + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + entered.set(true); + }); + }); + try { + synchronized (lock) { + vthread.start(); + + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + + assertFalse(entered.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Returns a stream of elements that are ordered pairs of platform and virtual thread + * counts. 0,2,4 platform threads. 2,4,6,8 virtual threads. + */ + static Stream threadCounts() { + return IntStream.range(0, 5) + .filter(i -> i % 2 == 0) + .mapToObj(i -> i) + .flatMap(np -> IntStream.range(2, 9) + .filter(i -> i % 2 == 0) + .mapToObj(vp -> Arguments.of(np, vp))); + } + + /** + * Execute a task concurrently from both platform and virtual threads. + */ + private void executeConcurrently(int nPlatformThreads, + int nVirtualThreads, + Runnable task) throws Exception { + int parallism = nVirtualThreads; + if (Thread.currentThread().isVirtual()) { + parallism++; + } + int previousParallelism = VThreadRunner.ensureParallelism(parallism); + try { + int nthreads = nPlatformThreads + nVirtualThreads; + var phaser = new Phaser(nthreads + 1); + + // start all threads + var threads = new Thread[nthreads]; + int index = 0; + for (int i = 0; i < nPlatformThreads; i++) { + threads[index++] = Thread.ofPlatform().start(() -> { + phaser.arriveAndAwaitAdvance(); + task.run(); + }); + } + for (int i = 0; i < nVirtualThreads; i++) { + threads[index++] = Thread.ofVirtual().start(() -> { + phaser.arriveAndAwaitAdvance(); + task.run(); + }); + } + + // wait for all threads to start + phaser.arriveAndAwaitAdvance(); + System.err.printf(" %d threads started%n", nthreads); + + // wait for all threads to terminate + for (Thread thread : threads) { + if (thread != null) { + System.err.printf(" join %s ...%n", thread); + thread.join(); + } + } + } finally { + // reset parallelism + VThreadRunner.setParallelism(previousParallelism); + } + } + + + /** + * Test entering a monitor with a synchronized native method from many threads + * at the same time. + */ + @ParameterizedTest + @MethodSource("threadCounts") + void testEnterConcurrently(int nPlatformThreads, int nVirtualThreads) throws Exception { + var counter = new Object() { + int value; + int value() { return value; } + void increment() { value++; } + }; + var lock = this; + executeConcurrently(nPlatformThreads, nVirtualThreads, () -> { + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + counter.increment(); + LockSupport.parkNanos(100_000_000); // 100ms + }); + }); + synchronized (lock) { + assertEquals(nPlatformThreads + nVirtualThreads, counter.value()); + } + } + + /** + * Test entering a monitor with JNI MonitorEnter. + */ + @Test + void testEnterInNative() throws Exception { + Object lock = new Object(); + VThreadRunner.run(() -> { + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + }); + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test reentering a monitor with JNI MonitorEnter. + */ + @Test + void testReenterInNative() throws Exception { + Object lock = new Object(); + VThreadRunner.run(() -> { + + // enter, reenter with JNI MonitorEnter + synchronized (lock) { + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + }); + assertTrue(Thread.holdsLock(lock)); + } + assertFalse(Thread.holdsLock(lock)); + + // enter with JNI MonitorEnter, renter with synchronized statement + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertTrue(Thread.holdsLock(lock)); + }); + assertFalse(Thread.holdsLock(lock)); + + // enter with JNI MonitorEnter, renter with JNI MonitorEnter + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + }); + assertTrue(Thread.holdsLock(lock)); + }); + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test entering a monitor with JNI MonitorEnter and with contention. + */ + @Test + void testEnterInNativeWithContention() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + entered.set(true); + }); + }); + try { + synchronized (lock) { + vthread.start(); + + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + + assertFalse(entered.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Test entering a monitor with JNI MonitorEnter from many threads at the same time. + */ + @ParameterizedTest + @MethodSource("threadCounts") + void testEnterInNativeConcurrently(int nPlatformThreads, int nVirtualThreads) throws Exception { + var counter = new Object() { + int value; + int value() { return value; } + void increment() { value++; } + }; + var lock = counter; + executeConcurrently(nPlatformThreads, nVirtualThreads, () -> { + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(Thread.holdsLock(lock)); + counter.increment(); + LockSupport.parkNanos(100_000_000); // 100ms + }); + }); + synchronized (lock) { + assertEquals(nPlatformThreads + nVirtualThreads, counter.value()); + } + } + + /** + * Test parking with synchronized native method on stack. + */ + @Test + void testParkingWhenPinned() throws Exception { + var lock = this; + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var done = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + entered.set(true); + while (!done.get()) { + LockSupport.park(); + } + }); + }); + try { + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.WAITING); + } finally { + done.set(true); + LockSupport.unpark(vthread); + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Test blocking with synchronized native method on stack. + */ + @Test + void testBlockingWhenPinned() throws Exception { + var lock1 = this; + var lock2 = new Object(); + + var started = new CountDownLatch(1); + var entered1 = new AtomicBoolean(); // set to true when vthread enters lock1 + var entered2 = new AtomicBoolean(); // set to true when vthread enters lock2 + + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock1)); + entered1.set(true); + synchronized (lock2) { // should block + assertTrue(Thread.holdsLock(lock2)); + entered2.set(true); + } + }); + }); + try { + synchronized (lock2) { + // start thread and wait for it to block trying to enter lock2 + vthread.start(); + started.await(); + await(vthread, Thread.State.BLOCKED); + + assertTrue(entered1.get()); + assertFalse(entered2.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered2.get()); + } + + /** + * Test that blocking on synchronized native method releases the carrier. + */ + //@Test + void testReleaseWhenBlocked() throws Exception { + assertTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + var lock = this; + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); // set to true when vthread enters lock + + var vthread1 = factory.newThread(() -> { + started.countDown(); + runWithSynchronizedNative(() -> { + assertTrue(Thread.holdsLock(lock)); + entered.set(true); + }); + assertFalse(Thread.holdsLock(lock)); + }); + + vthread1.start(); + try { + synchronized (this) { + // start thread and wait for it to block + vthread1.start(); + started.await(); + await(vthread1, Thread.State.BLOCKED); + + // carrier should be released, use it for another thread + var executed = new AtomicBoolean(); + var vthread2 = factory.newThread(() -> { + executed.set(true); + }); + vthread2.start(); + vthread2.join(); + assertTrue(executed.get()); + } + } finally { + vthread1.join(); + } + } + } + + /** + * Invokes the given task's run method while holding the monitor for "this". + */ + private synchronized native void runWithSynchronizedNative(Runnable task); + + /** + * Invokes the given task's run method while holding the monitor for the given + * object. The monitor is entered with JNI MonitorEnter, and exited with JNI MonitorExit. + */ + private native void runWithMonitorEnteredInNative(Object lock, Runnable task); + + /** + * Called from native methods to run the given task. + */ + private void run(Runnable task) { + task.run(); + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java index cda302f391f42..d40fafc67bff7 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java +++ b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java @@ -27,6 +27,7 @@ * @summary Test Thread API with virtual threads * @modules java.base/java.lang:+open jdk.management * @library /test/lib + * @build LockingMode * @run junit/othervm --enable-native-access=ALL-UNNAMED ThreadAPI */ @@ -35,6 +36,7 @@ * @requires vm.continuations * @modules java.base/java.lang:+open jdk.management * @library /test/lib + * @build LockingMode * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations * --enable-native-access=ALL-UNNAMED ThreadAPI */ @@ -68,6 +70,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.condition.DisabledIf; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; @@ -1082,7 +1085,7 @@ void testSetDaemon2() throws Exception { * Test Thread.yield releases carrier thread. */ @Test - void testYield1() throws Exception { + void testYieldReleasesCarrier() throws Exception { assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); var list = new CopyOnWriteArrayList(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { @@ -1106,10 +1109,41 @@ void testYield1() throws Exception { } /** - * Test Thread.yield when thread is pinned by native frame. + * Test Thread.yield releases carrier thread when virtual thread holds a monitor. */ @Test - void testYield2() throws Exception { + @DisabledIf("LockingMode#isLegacy") + void testYieldReleasesCarrierWhenHoldingMonitor() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); + var list = new CopyOnWriteArrayList(); + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + var lock = new Object(); + var thread = factory.newThread(() -> { + list.add("A"); + var child = factory.newThread(() -> { + list.add("B"); + synchronized (lock) { + Thread.yield(); + } + list.add("B"); + }); + child.start(); + Thread.yield(); + list.add("A"); + try { child.join(); } catch (InterruptedException e) { } + }); + thread.start(); + thread.join(); + } + assertEquals(List.of("A", "B", "A", "B"), list); + } + + /** + * Test Thread.yield when thread is pinned. + */ + @Test + void testYieldWhenPinned() throws Exception { assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); var list = new CopyOnWriteArrayList(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { @@ -1136,7 +1170,7 @@ void testYield2() throws Exception { * Test Thread.yield does not consume the thread's parking permit. */ @Test - void testYield3() throws Exception { + void testYieldDoesNotConsumParkingPermit() throws Exception { var thread = Thread.ofVirtual().start(() -> { LockSupport.unpark(Thread.currentThread()); Thread.yield(); @@ -1149,7 +1183,7 @@ void testYield3() throws Exception { * Test Thread.yield does not make available the thread's parking permit. */ @Test - void testYield4() throws Exception { + void testYieldDoesNotOfferParkingPermit() throws Exception { var thread = Thread.ofVirtual().start(() -> { Thread.yield(); LockSupport.park(); // should park @@ -1971,6 +2005,38 @@ void testHoldsLock2() throws Exception { }); } + /** + * Test Thread.holdsLock when lock held by carrier thread. + */ + @Disabled + @Test + void testHoldsLock3() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); + + Object lock = new Object(); + + // carrier thread runs all tasks while holding the lock + ThreadFactory carrierThreadFactory = task -> Thread.ofPlatform().unstarted(() -> { + synchronized (lock) { + task.run(); + } + }); + try (ExecutorService pool = Executors.newSingleThreadExecutor(carrierThreadFactory)) { + Executor scheduler = task -> pool.submit(task::run); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + // start virtual that tests if it holds the lock + var result = new AtomicReference(); + Thread vthread = factory.newThread(() -> { + result.set(Thread.holdsLock(lock)); + }); + vthread.start(); + vthread.join(); + boolean holdsLock = result.get(); + assertFalse(holdsLock, "Thread.holdsLock should return false"); + } + } + /** * Test Thread::getStackTrace on unstarted thread. */ diff --git a/test/jdk/java/lang/Thread/virtual/TracePinnedThreads.java b/test/jdk/java/lang/Thread/virtual/TracePinnedThreads.java deleted file mode 100644 index 5db29c631a323..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/TracePinnedThreads.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @bug 8284161 8289284 8322846 - * @summary Basic test of debugging option to trace pinned threads - * @requires vm.continuations - * @library /test/lib - * @run junit/othervm -Djdk.tracePinnedThreads=full TracePinnedThreads - * @run junit/othervm -Djdk.tracePinnedThreads=short TracePinnedThreads - */ - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.time.Duration; -import java.util.concurrent.Executors; -import java.util.concurrent.locks.LockSupport; - -import jdk.test.lib.thread.VThreadRunner; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class TracePinnedThreads { - static final Object lock = new Object(); - - /** - * Parks current thread for 1 second. - */ - private static void park() { - long nanos = Duration.ofSeconds(1).toNanos(); - LockSupport.parkNanos(nanos); - } - - /** - * Invokes the park method through a native frame to park the current - * thread for 1 second. - */ - private static native void invokePark(); - - /** - * Test parking inside synchronized block. - */ - @Test - void testPinnedCausedBySynchronizedBlock() throws Exception { - String output = run(() -> { - synchronized (lock) { - park(); - } - }); - assertContains(output, "reason:MONITOR"); - assertContains(output, "<== monitors:1"); - } - - /** - * Test parking with native frame on stack. - */ - @Test - void testPinnedCausedByNativeMethod() throws Exception { - System.loadLibrary("TracePinnedThreads"); - String output = run(() -> invokePark()); - assertContains(output, "reason:NATIVE"); - assertContains(output, "(Native Method)"); - } - - /** - * Test parking in class initializer. - */ - @Test - void testPinnedCausedByClassInitializer() throws Exception { - class C { - static { - park(); - } - } - String output = run(C::new); - assertContains(output, "reason:NATIVE"); - assertContains(output, ""); - } - - /** - * Test contention writing to System.out when pinned. The test creates four threads - * that write to System.out when pinned, this is enough to potentially deadlock - * without the changes in JDK-8322846. - */ - @Test - void testContention() throws Exception { - // use several classes to avoid duplicate stack traces - class C1 { - synchronized void print() { - System.out.println("hello"); - } - } - class C2 { - synchronized void print() { - System.out.println("hello"); - } - } - class C3 { - synchronized void print() { - System.out.println("hello"); - } - } - class C4 { - synchronized void print() { - System.out.println("hello"); - } - } - - try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { - executor.submit(() -> { - new C1().print(); - }); - executor.submit(() -> { - new C2().print(); - }); - executor.submit(() -> { - new C3().print(); - }); - executor.submit(() -> { - new C4().print(); - }); - } - } - - /** - * Run a task in a virtual thread, returning a String with any output printed - * to standard output. - */ - private static String run(Runnable task) throws Exception { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintStream original = System.out; - System.setOut(new PrintStream(baos, true)); - try { - VThreadRunner.run(task::run); - } finally { - System.setOut(original); - } - String output = new String(baos.toByteArray()); - System.out.println(output); - return output; - } - - /** - * Tests that s1 contains s2. - */ - private static void assertContains(String s1, String s2) { - assertTrue(s1.contains(s2), s2 + " not found!!!"); - } - - /** - * Tests that s1 does not contain s2. - */ - private static void assertDoesNotContain(String s1, String s2) { - assertFalse(s1.contains(s2), s2 + " found!!"); - } -} diff --git a/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java b/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java deleted file mode 100644 index 2d8226554280e..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @summary Test parking when pinned and emitting the JFR VirtualThreadPinnedEvent throws - * @modules java.base/java.lang:+open java.base/jdk.internal.event jdk.management - * @library /test/lib - * @compile/module=java.base jdk/internal/event/VirtualThreadPinnedEvent.java - * @run junit/othervm --enable-native-access=ALL-UNNAMED VirtualThreadPinnedEventThrows - */ - -import java.lang.ref.Reference; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.LockSupport; -import jdk.internal.event.VirtualThreadPinnedEvent; - -import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management -import jdk.test.lib.thread.VThreadPinner; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeAll; -import static org.junit.jupiter.api.Assertions.*; - -class VirtualThreadPinnedEventThrows { - - @BeforeAll - static void setup() { - // need >=2 carriers for testing pinning when main thread is a virtual thread - if (Thread.currentThread().isVirtual()) { - VThreadRunner.ensureParallelism(2); - } - } - - /** - * Test parking when pinned and creating the VirtualThreadPinnedEvent fails with OOME. - */ - @Test - void testVirtualThreadPinnedEventCreateThrows() throws Exception { - VirtualThreadPinnedEvent.setCreateThrows(true); - try { - testParkWhenPinned(); - } finally { - VirtualThreadPinnedEvent.setCreateThrows(false); - } - } - - /** - * Test parking when pinned and VirtualThreadPinnedEvent.begin fails with OOME. - */ - @Test - void testVirtualThreadPinnedEventBeginThrows() throws Exception { - VirtualThreadPinnedEvent.setBeginThrows(true); - try { - testParkWhenPinned(); - } finally { - VirtualThreadPinnedEvent.setBeginThrows(false); - } - } - - /** - * Test parking when pinned and VirtualThreadPinnedEvent.commit fails with OOME. - */ - @Test - void testVirtualThreadPinnedEventCommitThrows() throws Exception { - VirtualThreadPinnedEvent.setCommitThrows(true); - try { - testParkWhenPinned(); - } finally { - VirtualThreadPinnedEvent.setCommitThrows(false); - } - } - - /** - * Test parking a virtual thread when pinned. - */ - private void testParkWhenPinned() throws Exception { - var exception = new AtomicReference(); - var done = new AtomicBoolean(); - Thread thread = Thread.startVirtualThread(() -> { - try { - VThreadPinner.runPinned(() -> { - while (!done.get()) { - LockSupport.park(); - } - }); - } catch (Throwable e) { - exception.set(e); - } - }); - try { - // wait for thread to park - Thread.State state; - while ((state = thread.getState()) != Thread.State.WAITING) { - assertTrue(state != Thread.State.TERMINATED); - Thread.sleep(10); - } - } finally { - done.set(true); - LockSupport.unpark(thread); - thread.join(); - } - assertNull(exception.get()); - } -} diff --git a/test/jdk/java/lang/Thread/virtual/java.base/jdk/internal/event/VirtualThreadPinnedEvent.java b/test/jdk/java/lang/Thread/virtual/java.base/jdk/internal/event/VirtualThreadPinnedEvent.java deleted file mode 100644 index f267191b3078e..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/java.base/jdk/internal/event/VirtualThreadPinnedEvent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.internal.event; - -/** - * VirtualThreadPinnedEvent to optionally throw OOME at create, begin or commit time. - */ -public class VirtualThreadPinnedEvent extends Event { - private static boolean throwOnCreate; - private static boolean throwOnBegin; - private static boolean throwOnCommit; - - public static void setCreateThrows(boolean value) { - throwOnCreate = value; - } - - public static void setBeginThrows(boolean value) { - throwOnBegin = value; - } - - public static void setCommitThrows(boolean value) { - throwOnCommit = value; - } - - public VirtualThreadPinnedEvent() { - if (throwOnCreate) { - throw new OutOfMemoryError(); - } - } - - @Override - public void begin() { - if (throwOnBegin) { - throw new OutOfMemoryError(); - } - } - - @Override - public void commit() { - if (throwOnCommit) { - throw new OutOfMemoryError(); - } - } -} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/VirtualThreadPinnedEvent.java b/test/jdk/java/lang/Thread/virtual/libSynchronizedNative.c similarity index 50% rename from src/jdk.jfr/share/classes/jdk/jfr/events/VirtualThreadPinnedEvent.java rename to test/jdk/java/lang/Thread/virtual/libSynchronizedNative.c index 83547125ac53e..16e5c61d3840c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/VirtualThreadPinnedEvent.java +++ b/test/jdk/java/lang/Thread/virtual/libSynchronizedNative.c @@ -1,12 +1,10 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -23,15 +21,23 @@ * questions. */ -package jdk.jfr.events; +#include "jni.h" -import jdk.jfr.Category; -import jdk.jfr.Label; -import jdk.jfr.Name; -import jdk.jfr.internal.MirrorEvent; +JNIEXPORT void JNICALL +Java_SynchronizedNative_runWithSynchronizedNative(JNIEnv *env, jobject obj, jobject task) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "(Ljava/lang/Runnable;)V"); + if (mid != NULL) { + (*env)->CallVoidMethod(env, obj, mid, task); + } +} -@Category("Java Application") -@Label("Virtual Thread Pinned") -@Name("jdk.VirtualThreadPinned") -public final class VirtualThreadPinnedEvent extends MirrorEvent { +JNIEXPORT void JNICALL +Java_SynchronizedNative_runWithMonitorEnteredInNative(JNIEnv *env, jobject obj, jobject lock, jobject task) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "(Ljava/lang/Runnable;)V"); + if (mid != NULL && (*env)->MonitorEnter(env, lock) == 0) { + (*env)->CallVoidMethod(env, obj, mid, task); + (*env)->MonitorExit(env, lock); // can be called with pending exception + } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWithTimedWait.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWithTimedWait.java new file mode 100644 index 0000000000000..3f8f1c464a61a --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWithTimedWait.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Stress test Thread.getStackTrace on a virtual thread in timed-Object.wait + * @requires vm.debug != true + * @run main/othervm GetStackTraceALotWithTimedWait 100000 + */ + +/* + * @test + * @requires vm.debug == true + * @run main/othervm GetStackTraceALotWithTimedWait 50000 + */ + +import java.time.Instant; +import java.util.ArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class GetStackTraceALotWithTimedWait { + + public static void main(String[] args) throws Exception { + int iterations = args.length > 0 ? Integer.parseInt(args[0]) : 100_000; + + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + var done = new AtomicBoolean(); + var threads = new ArrayList(); + + // start threads that invoke Object.wait with a short timeout + int nthreads = Runtime.getRuntime().availableProcessors(); + for (int i = 0; i < nthreads; i++) { + var ref = new AtomicReference(); + var lock = new Object(); + executor.submit(() -> { + ref.set(Thread.currentThread()); + while (!done.get()) { + synchronized (lock) { + int delay = 1 + ThreadLocalRandom.current().nextInt(20); + lock.wait(delay); + } + } + return null; + }); + Thread thread; + while ((thread = ref.get()) == null) { + Thread.sleep(20); + } + threads.add(thread); + } + + // hammer on Thread.getStackTrace + try { + for (int i = 1; i <= iterations; i++) { + if ((i % 1000) == 0) { + System.out.println(Instant.now() + " => " + i + " of " + iterations); + } + for (Thread thread : threads) { + thread.getStackTrace(); + } + } + } finally { + done.set(true); + } + } + } +} + diff --git a/test/jdk/java/lang/Thread/virtual/stress/LotsOfContendedMonitorEnter.java b/test/jdk/java/lang/Thread/virtual/stress/LotsOfContendedMonitorEnter.java new file mode 100644 index 0000000000000..86096156b9127 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/LotsOfContendedMonitorEnter.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=default + * @summary Test virtual threads entering a lot of monitors with contention + * @requires vm.opt.LockingMode != 1 + * @library /test/lib + * @run main/othervm LotsOfContendedMonitorEnter + */ + +/* + * @test id=LM_LIGHTWEIGHT + * @requires vm.opt.LockingMode != 1 + * @library /test/lib + * @run main/othervm -XX:LockingMode=2 LotsOfContendedMonitorEnter + */ + +import java.util.concurrent.CountDownLatch; +import jdk.test.lib.thread.VThreadRunner; + +public class LotsOfContendedMonitorEnter { + + public static void main(String[] args) throws Exception { + int depth; + if (args.length > 0) { + depth = Integer.parseInt(args[0]); + } else { + depth = 1024; + } + VThreadRunner.run(() -> testContendedEnter(depth)); + } + + /** + * Enter the monitor for a new object, racing with another virtual thread that + * attempts to enter around the same time, then repeat to the given depth. + */ + private static void testContendedEnter(int depthRemaining) throws Exception { + if (depthRemaining > 0) { + var lock = new Object(); + + // start thread to enter monitor for brief period, then enters again when signalled + var started = new CountDownLatch(1); + var signal = new CountDownLatch(1); + var thread = Thread.ofVirtual().start(() -> { + started.countDown(); + + // enter, may be contended + synchronized (lock) { + Thread.onSpinWait(); + } + + // wait to be signalled + try { + signal.await(); + } catch (InterruptedException e) { } + + // enter again, this will block until the main thread releases + synchronized (lock) { + // do nothing + } + }); + try { + // wait for thread to start + started.await(); + + // enter, may be contended + synchronized (lock) { + // signal thread to enter monitor again, it should block + signal.countDown(); + await(thread, Thread.State.BLOCKED); + testContendedEnter(depthRemaining - 1); + } + } finally { + thread.join(); + } + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private static void await(Thread thread, Thread.State expectedState) { + Thread.State state = thread.getState(); + while (state != expectedState) { + assert state != Thread.State.TERMINATED : "Thread has terminated"; + Thread.yield(); + state = thread.getState(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/LotsOfUncontendedMonitorEnter.java b/test/jdk/java/lang/Thread/virtual/stress/LotsOfUncontendedMonitorEnter.java new file mode 100644 index 0000000000000..e72fc12eb88da --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/LotsOfUncontendedMonitorEnter.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=default + * @summary Test virtual thread entering (and reentering) a lot of monitors with no contention + * @library /test/lib + * @run main/othervm LotsOfUncontendedMonitorEnter + */ + +/* + * @test id=LM_LEGACY + * @library /test/lib + * @run main/othervm -XX:LockingMode=1 LotsOfUncontendedMonitorEnter + */ + +/* + * @test id=LM_LIGHTWEIGHT + * @library /test/lib + * @run main/othervm -XX:LockingMode=2 LotsOfUncontendedMonitorEnter + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import jdk.test.lib.thread.VThreadRunner; + +public class LotsOfUncontendedMonitorEnter { + + public static void main(String[] args) throws Exception { + int depth; + if (args.length > 0) { + depth = Integer.parseInt(args[0]); + } else { + depth = 24; // 33554430 enters + } + VThreadRunner.run(() -> { + testEnter(List.of(), depth); + }); + } + + /** + * Enter the monitor for a new object, reenter a monitor that is already held, and + * repeat to the given depth. + */ + private static void testEnter(List ownedMonitors, int depthRemaining) { + if (depthRemaining > 0) { + var lock = new Object(); + synchronized (lock) { + // new list of owned monitors + var monitors = concat(ownedMonitors, lock); + testEnter(monitors, depthRemaining - 1); + + // reenter a monitor that is already owned + int index = ThreadLocalRandom.current().nextInt(monitors.size()); + var otherLock = monitors.get(index); + + synchronized (otherLock) { + testEnter(monitors, depthRemaining - 1); + } + } + } + } + + /** + * Adds an element to a list, returning a new list. + */ + private static List concat(List list, T object) { + var newList = new ArrayList<>(list); + newList.add(object); + return newList; + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/Skynet100kWithMonitors.java b/test/jdk/java/lang/Thread/virtual/stress/Skynet100kWithMonitors.java new file mode 100644 index 0000000000000..9487de6b862f9 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/Skynet100kWithMonitors.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Stress test virtual threads with a variation of the Skynet 1M benchmark that uses + * a channel implementation based on object monitors. This variant uses a reduced number of + * 100k virtual threads at the final level. + * @requires vm.debug != true & vm.continuations & vm.opt.LockingMode != 1 + * @run main/othervm/timeout=300 Skynet100kWithMonitors 50 + */ + +/* + * @test + * @requires vm.debug == true & vm.continuations & vm.opt.LockingMode != 1 + * @run main/othervm/timeout=300 Skynet100kWithMonitors 10 + */ + +public class Skynet100kWithMonitors { + + public static void main(String[] args) { + int iterations = (args.length) > 0 ? Integer.parseInt(args[0]) : 10; + for (int i = 0; i < iterations; i++) { + skynet(100_000, 4999950000L); + } + } + + static void skynet(int num, long expected) { + long start = System.currentTimeMillis(); + var chan = new Channel(); + + Thread.startVirtualThread(() -> skynet(chan, 0, num, 10)); + + long sum = chan.receive(); + long end = System.currentTimeMillis(); + System.out.format("Result: %d in %s ms%n", sum, (end-start)); + if (sum != expected) + throw new RuntimeException("Expected " + expected); + } + + static void skynet(Channel result, int num, int size, int div) { + if (size == 1) { + result.send((long)num); + } else { + var chan = new Channel(); + for (int i = 0; i < div; i++) { + int subNum = num + i * (size / div); + Thread.startVirtualThread(() -> skynet(chan, subNum, size / div, div)); + } + long sum = 0; + for (int i = 0; i < div; i++) { + sum += chan.receive(); + } + result.send(sum); + } + } + + static class Channel { + private final Object lock = new Object(); + private T element; + + Channel() { + } + + void send(T e) { + boolean interrupted = false; + synchronized (lock) { + while (element != null) { + try { + lock.wait(); + } catch (InterruptedException x) { + interrupted = true; + } + } + element = e; + lock.notifyAll(); + } + if (interrupted) + Thread.currentThread().interrupt(); + } + + T receive() { + T e; + boolean interrupted = false; + synchronized (lock) { + while ((e = element) == null) { + try { + lock.wait(); + } catch (InterruptedException x) { + interrupted = true; + } + } + element = null; + lock.notifyAll(); + } + if (interrupted) + Thread.currentThread().interrupt(); + return e; + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/TEST.properties b/test/jdk/java/lang/Thread/virtual/stress/TEST.properties deleted file mode 100644 index d721b4f7d4bd4..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/stress/TEST.properties +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -exclusiveAccess.dirs=. diff --git a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java index 47dfa5a019dc1..b640a5e215029 100644 --- a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java +++ b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/java/nio/channels/vthread/SelectorOps.java b/test/jdk/java/nio/channels/vthread/SelectorOps.java index 950bdaf0671f0..1abb5c8bc5f60 100644 --- a/test/jdk/java/nio/channels/vthread/SelectorOps.java +++ b/test/jdk/java/nio/channels/vthread/SelectorOps.java @@ -28,6 +28,14 @@ * @run junit/othervm --enable-native-access=ALL-UNNAMED SelectorOps */ +/* + * @test id=poller-modes + * @requires (os.family == "linux") | (os.family == "mac") + * @library /test/lib + * @run junit/othervm -Djdk.pollerMode=1 --enable-native-access=ALL-UNNAMED SelectorOps + * @run junit/othervm -Djdk.pollerMode=2 --enable-native-access=ALL-UNNAMED SelectorOps + */ + /* * @test id=no-vmcontinuations * @requires vm.continuations diff --git a/test/jdk/jdk/internal/vm/Continuation/Basic.java b/test/jdk/jdk/internal/vm/Continuation/Basic.java index 14a7058f2a67b..e13fa175ad5a5 100644 --- a/test/jdk/jdk/internal/vm/Continuation/Basic.java +++ b/test/jdk/jdk/internal/vm/Continuation/Basic.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * @summary Basic tests for jdk.internal.vm.Continuation * @requires vm.continuations * @modules java.base/jdk.internal.vm +* @library /test/lib * @build java.base/java.lang.StackWalkerHelper * * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xint Basic @@ -44,6 +45,7 @@ * @requires vm.continuations * @requires vm.debug * @modules java.base/jdk.internal.vm +* @library /test/lib * @build java.base/java.lang.StackWalkerHelper * * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -XX:+VerifyStack -Xint Basic @@ -54,12 +56,17 @@ import jdk.internal.vm.Continuation; import jdk.internal.vm.ContinuationScope; +import jdk.test.lib.Platform; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.sun.management.HotSpotDiagnosticMXBean; +import java.lang.management.ManagementFactory; + import org.testng.annotations.Test; import org.testng.annotations.DataProvider; import static org.testng.Assert.*; @@ -276,6 +283,8 @@ static String barMany(long b, @Test public void testPinnedMonitor() { + if (!legacyLockingMode()) return; + // Test pinning due to held monitor final AtomicReference res = new AtomicReference<>(); @@ -410,4 +419,9 @@ static int nativeBaz(int b) { static { System.loadLibrary("BasicJNI"); } + + static boolean legacyLockingMode() { + return ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class) + .getVMOption("LockingMode").getValue().equals("1"); + } } diff --git a/test/jdk/jdk/internal/vm/Continuation/Fuzz.java b/test/jdk/jdk/internal/vm/Continuation/Fuzz.java index b6b936078c970..52730c9523b10 100644 --- a/test/jdk/jdk/internal/vm/Continuation/Fuzz.java +++ b/test/jdk/jdk/internal/vm/Continuation/Fuzz.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,6 +75,9 @@ import jdk.test.lib.Platform; import jtreg.SkippedException; +import com.sun.management.HotSpotDiagnosticMXBean; +import java.lang.management.ManagementFactory; + public class Fuzz implements Runnable { static final boolean VERIFY_STACK = true; // could add significant time static final boolean FILE = true; @@ -470,7 +473,7 @@ void verifyResult(int result) { } boolean shouldPin() { - return traceHas(Op.PIN::contains); + return traceHas(Op.PIN::contains) && legacyLockingMode(); } void verifyPin(boolean yieldResult) { @@ -1029,4 +1032,9 @@ int com_mny(int depth, return log((int)res); } + + static boolean legacyLockingMode() { + return ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class) + .getVMOption("LockingMode").getValue().equals("1"); + } } From 2989d8734c70e1db87d2a708719fd2d966903a93 Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Tue, 12 Nov 2024 15:26:19 +0000 Subject: [PATCH 002/112] 8343805: RISC-V: JVM crashes on startup when disabling compressed instructions Reviewed-by: mli --- src/hotspot/cpu/riscv/stubRoutines_riscv.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp index 46b5461d7770f..3d1f4a61f005c 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp @@ -39,7 +39,7 @@ enum platform_dependent_constants { // simply increase sizes if too small (assembler will crash if too small) _initial_stubs_code_size = 10000, _continuation_stubs_code_size = 2000, - _compiler_stubs_code_size = 25000 ZGC_ONLY(+5000), + _compiler_stubs_code_size = 45000, _final_stubs_code_size = 20000 ZGC_ONLY(+10000) }; From e5eaa7f1eb0cb072d02bc18e23b0daaee875b077 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 12 Nov 2024 15:34:50 +0000 Subject: [PATCH 003/112] 8343946: JFR: Wildcard should only work with COUNT for 'jfr view' Reviewed-by: mgronlun --- .../share/classes/jdk/jfr/internal/query/QueryResolver.java | 4 ++-- src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryResolver.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryResolver.java index cd39fb7b6e8ae..781661a427f2e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryResolver.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryResolver.java @@ -144,7 +144,7 @@ private void resolveSelect() throws QueryException { field.aggregator = expression.aggregator(); FieldBuilder.configureAggregator(field); expression.alias().ifPresent(alias -> fieldAliases.put(alias, field)); - if (field.name.equals("*") && field.aggregator != Aggregator.COUNT) { + if (expression.name().equals("*") && field.aggregator != Aggregator.COUNT) { throw new QuerySyntaxException("Wildcard ('*') can only be used with aggregator function COUNT"); } } @@ -259,7 +259,7 @@ private List resolveFields(String name, List types) { List fields = new ArrayList<>(); if (name.equals("*")) { - // Used with COUNT(*) and UNIQUE(*) + // Used with COUNT(*) // All events should have a start time name = "startTime"; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini index b21b99e2dad91..51dc2ca11bff9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini @@ -178,7 +178,7 @@ table = "COLUMN 'StackTrace', 'Count', 'Avg.', 'Max.' [application.contention-by-address] label = "Contention by Monitor Address" table = "COLUMN 'Monitor Address', 'Class', 'Threads', 'Max Duration' - SELECT address, FIRST(monitorClass), UNIQUE(*), MAX(duration) AS M + SELECT address, FIRST(monitorClass), UNIQUE(eventThread), MAX(duration) AS M FROM JavaMonitorEnter GROUP BY monitorClass ORDER BY M" From 81752c4bcc384a8dd1e87b71a0de86877a0b661d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Tue, 12 Nov 2024 15:45:23 +0000 Subject: [PATCH 004/112] 8338565: Test crashed: assert(is_path_empty()) failed: invariant Reviewed-by: egahlin --- src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index 0759d02cb7c23..eaee79497bb4d 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -109,8 +109,6 @@ static void close_emergency_dump_file() { } static const char* create_emergency_dump_path() { - assert(is_path_empty(), "invariant"); - const size_t path_len = get_dump_directory(); if (path_len == 0) { return nullptr; From c12b386d1916af9a04b4c6698838c2b40c6cdd86 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Tue, 12 Nov 2024 15:52:30 +0000 Subject: [PATCH 005/112] 8338007: [JVMCI] ResolvedJavaMethod.reprofile can crash ciMethodData Reviewed-by: dnsimon, kvn --- src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 3 +- src/hotspot/share/oops/methodData.cpp | 33 +++++++++++++++++-- src/hotspot/share/oops/methodData.hpp | 10 +++++- src/hotspot/share/runtime/vmOperation.hpp | 3 +- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 7922a6816393b..b85d2227fc7f5 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -1382,7 +1382,8 @@ C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, ARGUMENT_PAIR(method))) if (method_data == nullptr) { method_data = get_profiling_method_data(method, CHECK); } else { - method_data->initialize(); + CompilerThreadCanCallJava canCallJava(THREAD, true); + method_data->reinitialize(); } C2V_END diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index af19d59f28123..bc2649ba0ed3e 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -59,9 +59,14 @@ bool DataLayout::needs_array_len(u1 tag) { // Perform generic initialization of the data. More specific // initialization occurs in overrides of ProfileData::post_initialize. void DataLayout::initialize(u1 tag, u2 bci, int cell_count) { - _header._bits = (intptr_t)0; - _header._struct._tag = tag; - _header._struct._bci = bci; + DataLayout temp; + temp._header._bits = (intptr_t)0; + temp._header._struct._tag = tag; + temp._header._struct._bci = bci; + // Write the header using a single intptr_t write. This ensures that if the layout is + // reinitialized readers will never see the transient state where the header is 0. + _header = temp._header; + for (int i = 0; i < cell_count; i++) { set_cell_at(i, (intptr_t)0); } @@ -1224,6 +1229,28 @@ MethodData::MethodData(const methodHandle& method) initialize(); } +// Reinitialize the storage of an existing MDO at a safepoint. Doing it this way will ensure it's +// not being accessed while the contents are being rewritten. +class VM_ReinitializeMDO: public VM_Operation { + private: + MethodData* _mdo; + public: + VM_ReinitializeMDO(MethodData* mdo): _mdo(mdo) {} + VMOp_Type type() const { return VMOp_ReinitializeMDO; } + void doit() { + // The extra data is being zero'd, we'd like to acquire the extra_data_lock but it can't be held + // over a safepoint. This means that we don't actually need to acquire the lock. + _mdo->initialize(); + } + bool allow_nested_vm_operations() const { return true; } +}; + +void MethodData::reinitialize() { + VM_ReinitializeMDO op(this); + VMThread::execute(&op); +} + + void MethodData::initialize() { Thread* thread = Thread::current(); NoSafepointVerifier no_safepoint; // init function atomic wrt GC diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index 5071c29f1d774..36fcf8ce5fd60 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -1949,6 +1949,7 @@ class MethodData : public Metadata { friend class ProfileData; friend class TypeEntriesAtCall; friend class ciMethodData; + friend class VM_ReinitializeMDO; // If you add a new field that points to any metaspace object, you // must add this field to MethodData::metaspace_pointers_do(). @@ -1965,11 +1966,18 @@ class MethodData : public Metadata { Mutex _extra_data_lock; MethodData(const methodHandle& method); + + void initialize(); + public: static MethodData* allocate(ClassLoaderData* loader_data, const methodHandle& method, TRAPS); virtual bool is_methodData() const { return true; } - void initialize(); + + // Safely reinitialize the data in the MDO. This is intended as a testing facility as the + // reinitialization is performed at a safepoint so it's isn't cheap and it doesn't ensure that all + // readers will see consistent profile data. + void reinitialize(); // Whole-method sticky bits and flags enum { diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index eede52f00d566..50d859444858d 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -114,7 +114,8 @@ template(GTestStopSafepoint) \ template(JFROldObject) \ template(JvmtiPostObjectFree) \ - template(RendezvousGCThreads) + template(RendezvousGCThreads) \ + template(ReinitializeMDO) class Thread; class outputStream; From db85090553ab14a84c3ed0a2604dd56c5b6e6982 Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Tue, 12 Nov 2024 17:16:15 +0000 Subject: [PATCH 006/112] 8338411: Implement JEP 486: Permanently Disable the Security Manager Co-authored-by: Sean Mullan Co-authored-by: Alan Bateman Co-authored-by: Weijun Wang Co-authored-by: Aleksei Efimov Co-authored-by: Brian Burkhalter Co-authored-by: Daniel Fuchs Co-authored-by: Harshitha Onkar Co-authored-by: Joe Wang Co-authored-by: Jorn Vernee Co-authored-by: Justin Lu Co-authored-by: Kevin Walls Co-authored-by: Lance Andersen Co-authored-by: Naoto Sato Co-authored-by: Roger Riggs Co-authored-by: Brent Christian Co-authored-by: Stuart Marks Co-authored-by: Ian Graves Co-authored-by: Phil Race Co-authored-by: Erik Gahlin Co-authored-by: Jaikiran Pai Reviewed-by: kevinw, aivanov, rriggs, lancea, coffeys, dfuchs, ihse, erikj, cjplummer, coleenp, naoto, mchung, prr, weijun, joehw, azvegint, psadhukhan, bchristi, sundar, attila --- make/modules/java.base/Copy.gmk | 40 - make/modules/java.rmi/Launcher.gmk | 1 - src/hotspot/share/classfile/dictionary.cpp | 62 - src/hotspot/share/classfile/javaClasses.cpp | 27 +- src/hotspot/share/classfile/javaClasses.hpp | 3 - src/hotspot/share/classfile/vmSymbols.hpp | 2 - src/hotspot/share/include/jvm.h | 10 - src/hotspot/share/prims/jvm.cpp | 81 - src/hotspot/share/runtime/globals.hpp | 3 - src/java.base/share/classes/java/io/File.java | 204 -- .../classes/java/io/FileInputStream.java | 26 - .../classes/java/io/FileOutputStream.java | 40 - .../share/classes/java/io/FilePermission.java | 38 +- .../classes/java/io/ObjectInputFilter.java | 4 - .../classes/java/io/ObjectInputStream.java | 45 +- .../classes/java/io/ObjectOutputStream.java | 43 +- .../share/classes/java/io/PrintStream.java | 28 - .../share/classes/java/io/PrintWriter.java | 28 - .../classes/java/io/RandomAccessFile.java | 29 - .../java/io/SerializablePermission.java | 55 +- .../share/classes/java/lang/Boolean.java | 2 - .../share/classes/java/lang/Class.java | 389 +-- .../share/classes/java/lang/ClassLoader.java | 128 +- .../share/classes/java/lang/Integer.java | 6 - .../share/classes/java/lang/Long.java | 6 - .../share/classes/java/lang/Module.java | 13 +- .../share/classes/java/lang/ModuleLayer.java | 38 - .../share/classes/java/lang/Process.java | 9 +- .../classes/java/lang/ProcessBuilder.java | 66 +- .../classes/java/lang/ProcessHandle.java | 18 +- .../share/classes/java/lang/Runtime.java | 83 +- .../classes/java/lang/RuntimePermission.java | 357 +-- .../classes/java/lang/SecurityException.java | 9 +- .../classes/java/lang/SecurityManager.java | 1450 ++--------- .../share/classes/java/lang/StackWalker.java | 28 - .../share/classes/java/lang/System.java | 399 +-- .../share/classes/java/lang/Thread.java | 244 +- .../share/classes/java/lang/ThreadGroup.java | 109 +- .../classes/java/lang/VirtualThread.java | 2 - .../java/lang/invoke/LambdaMetafactory.java | 8 +- .../java/lang/invoke/MethodHandleProxies.java | 5 - .../java/lang/invoke/MethodHandles.java | 128 +- .../classes/java/lang/invoke/MethodType.java | 3 - .../java/lang/module/Configuration.java | 11 +- .../java/lang/module/ModuleFinder.java | 17 +- .../java/lang/module/ModuleReader.java | 19 +- .../java/lang/module/ModuleReference.java | 10 +- .../share/classes/java/lang/package-info.java | 10 +- .../share/classes/java/lang/ref/Cleaner.java | 10 +- .../java/lang/reflect/AccessibleObject.java | 25 +- .../java/lang/reflect/Constructor.java | 6 +- .../classes/java/lang/reflect/Field.java | 1 - .../classes/java/lang/reflect/Method.java | 1 - .../classes/java/lang/reflect/Proxy.java | 48 +- .../java/lang/reflect/ReflectPermission.java | 46 +- .../java/lang/reflect/package-info.java | 5 +- .../share/classes/java/net/Authenticator.java | 79 +- .../classes/java/net/ContentHandler.java | 6 +- .../share/classes/java/net/CookieHandler.java | 8 +- .../classes/java/net/DatagramSocket.java | 129 +- .../classes/java/net/HttpURLConnection.java | 24 - .../share/classes/java/net/InetAddress.java | 76 +- .../classes/java/net/InetSocketAddress.java | 9 +- .../classes/java/net/MulticastSocket.java | 58 - .../share/classes/java/net/NetPermission.java | 155 +- .../classes/java/net/NetworkInterface.java | 25 +- .../share/classes/java/net/ProxySelector.java | 9 +- .../share/classes/java/net/ResponseCache.java | 10 +- .../share/classes/java/net/ServerSocket.java | 104 +- .../share/classes/java/net/Socket.java | 106 +- .../classes/java/net/SocketPermission.java | 26 +- src/java.base/share/classes/java/net/URL.java | 35 +- .../classes/java/net/URLClassLoader.java | 68 +- .../share/classes/java/net/URLConnection.java | 18 +- .../share/classes/java/net/URLPermission.java | 13 +- .../net/spi/InetAddressResolverProvider.java | 5 +- .../net/spi/URLStreamHandlerProvider.java | 6 +- .../nio/channels/AsynchronousFileChannel.java | 16 - .../AsynchronousServerSocketChannel.java | 27 +- .../channels/AsynchronousSocketChannel.java | 33 +- .../java/nio/channels/DatagramChannel.java | 83 +- .../java/nio/channels/FileChannel.java | 16 - .../java/nio/channels/MulticastChannel.java | 10 +- .../java/nio/channels/NetworkChannel.java | 6 +- .../nio/channels/ServerSocketChannel.java | 50 +- .../java/nio/channels/SocketChannel.java | 50 +- .../spi/AsynchronousChannelProvider.java | 15 +- .../nio/channels/spi/SelectorProvider.java | 20 +- .../java/nio/charset/spi/CharsetProvider.java | 14 - .../java/nio/file/AccessDeniedException.java | 7 +- .../classes/java/nio/file/FileSystem.java | 27 +- .../classes/java/nio/file/FileSystems.java | 28 +- .../share/classes/java/nio/file/Files.java | 385 +-- .../classes/java/nio/file/LinkPermission.java | 38 +- .../share/classes/java/nio/file/Path.java | 31 +- .../share/classes/java/nio/file/Paths.java | 5 +- .../java/nio/file/SecureDirectoryStream.java | 31 +- .../classes/java/nio/file/Watchable.java | 10 +- .../file/attribute/AclFileAttributeView.java | 12 +- .../attribute/BasicFileAttributeView.java | 10 +- .../file/attribute/DosFileAttributeView.java | 19 +- .../attribute/FileOwnerAttributeView.java | 14 +- .../attribute/PosixFileAttributeView.java | 20 +- .../UserDefinedFileAttributeView.java | 38 +- .../attribute/UserPrincipalLookupService.java | 10 +- .../java/nio/file/spi/FileSystemProvider.java | 166 +- .../java/nio/file/spi/FileTypeDetector.java | 23 +- .../java/security/AccessControlContext.java | 740 +----- .../java/security/AccessControlException.java | 22 +- .../java/security/AccessController.java | 1040 ++------ .../classes/java/security/AllPermission.java | 18 +- .../classes/java/security/AuthProvider.java | 17 +- .../classes/java/security/DomainCombiner.java | 62 +- .../share/classes/java/security/Identity.java | 59 +- .../classes/java/security/IdentityScope.java | 12 +- .../share/classes/java/security/KeyStore.java | 20 +- .../classes/java/security/Permission.java | 27 +- .../share/classes/java/security/Policy.java | 506 +--- .../classes/java/security/PolicySpi.java | 61 +- .../java/security/ProtectionDomain.java | 320 +-- .../share/classes/java/security/Provider.java | 153 -- .../java/security/SecureClassLoader.java | 32 +- .../share/classes/java/security/Security.java | 83 - .../java/security/SecurityPermission.java | 291 +-- .../share/classes/java/security/Signer.java | 21 +- .../java/security/UnresolvedPermission.java | 61 +- .../classes/java/security/package-info.java | 8 - .../share/classes/java/util/Formatter.java | 40 - .../share/classes/java/util/Locale.java | 17 - .../classes/java/util/PropertyPermission.java | 29 +- .../classes/java/util/ResourceBundle.java | 10 - .../classes/java/util/ServiceLoader.java | 11 +- .../share/classes/java/util/TimeZone.java | 4 - .../java/util/concurrent/ExecutorService.java | 22 - .../java/util/concurrent/Executors.java | 97 +- .../java/util/concurrent/ForkJoinPool.java | 41 - .../util/concurrent/ForkJoinWorkerThread.java | 36 +- .../ScheduledThreadPoolExecutor.java | 3 - .../util/concurrent/ThreadLocalRandom.java | 7 - .../util/concurrent/ThreadPoolExecutor.java | 4 - .../share/classes/java/util/jar/JarFile.java | 12 - .../java/util/spi/LocaleServiceProvider.java | 4 - .../share/classes/java/util/zip/ZipFile.java | 39 - .../javax/net/ServerSocketFactory.java | 26 +- .../classes/javax/net/SocketFactory.java | 28 - .../javax/net/ssl/HttpsURLConnection.java | 11 +- .../classes/javax/net/ssl/SSLContext.java | 5 +- .../classes/javax/net/ssl/SSLPermission.java | 56 +- .../javax/net/ssl/SSLServerSocket.java | 26 +- .../classes/javax/net/ssl/SSLSession.java | 13 +- .../classes/javax/net/ssl/SSLSocket.java | 28 - .../javax/security/auth/AuthPermission.java | 98 +- .../javax/security/auth/Destroyable.java | 5 +- .../auth/PrivateCredentialPermission.java | 58 +- .../javax/security/auth/Refreshable.java | 6 +- .../classes/javax/security/auth/Subject.java | 503 +--- .../security/auth/SubjectDomainCombiner.java | 18 +- .../security/auth/login/Configuration.java | 20 +- .../security/auth/login/ConfigurationSpi.java | 5 +- .../security/auth/login/LoginContext.java | 67 +- .../jdk/internal/access/JavaLangAccess.java | 16 - .../internal/access/JavaSecurityAccess.java | 16 +- .../jdk/internal/misc/CarrierThread.java | 18 - .../jdk/internal/misc/InnocuousThread.java | 11 +- .../classes/jdk/internal/module/Modules.java | 5 +- src/java.base/share/classes/module-info.java | 1 - .../sun/security/provider/PolicyFile.java | 2250 ----------------- .../sun/security/provider/PolicyParser.java | 839 +----- .../sun/security/provider/PolicySpiFile.java | 82 - .../sun/security/provider/SunEntries.java | 9 +- .../classes/sun/security/util/Debug.java | 18 - .../classes/sun/security/util/Resources.java | 37 +- src/java.base/share/conf/security/java.policy | 46 - .../share/conf/security/java.security | 62 +- .../share/lib/security/default.policy | 210 -- .../share/native/libjava/AccessController.c | 70 - .../share/native/libjava/SecurityManager.c | 78 - .../windows/lib/security/default.policy | 16 - .../share/classes/javax/tools/FileObject.java | 5 +- .../tools/ForwardingJavaFileManager.java | 1 - .../classes/javax/tools/JavaFileManager.java | 4 - .../classes/javax/tools/JavaFileObject.java | 5 +- .../share/classes/java/applet/Applet.java | 7 +- .../share/classes/java/awt/AWTPermission.java | 132 +- .../share/classes/java/awt/Desktop.java | 108 +- .../share/classes/java/awt/Dialog.java | 22 +- .../share/classes/java/awt/Font.java | 10 +- .../share/classes/java/awt/Graphics2D.java | 14 - .../classes/java/awt/GraphicsDevice.java | 8 +- .../java/awt/KeyboardFocusManager.java | 106 +- .../share/classes/java/awt/MouseInfo.java | 12 +- .../share/classes/java/awt/Robot.java | 21 +- .../share/classes/java/awt/SystemTray.java | 11 +- .../share/classes/java/awt/Taskbar.java | 24 +- .../share/classes/java/awt/Toolkit.java | 121 +- .../share/classes/java/awt/TrayIcon.java | 19 +- .../share/classes/java/awt/Window.java | 46 +- .../classes/java/awt/color/ICC_Profile.java | 2 - .../java/awt/doc-files/AWTThreadIssues.html | 3 +- .../classes/java/awt/print/PrinterJob.java | 8 +- .../share/classes/java/beans/Beans.java | 26 +- .../share/classes/java/beans/Expression.java | 4 +- .../classes/java/beans/Introspector.java | 9 +- .../java/beans/PropertyEditorManager.java | 17 +- .../share/classes/java/beans/Statement.java | 4 +- .../accessibility/AccessibilityProvider.java | 5 +- .../share/classes/javax/imageio/ImageIO.java | 4 +- .../imageio/stream/FileImageInputStream.java | 4 +- .../imageio/stream/FileImageOutputStream.java | 4 +- .../javax/print/PrintServiceLookup.java | 15 +- .../classes/javax/sound/midi/MidiDevice.java | 4 +- .../javax/sound/sampled/AudioPermission.java | 42 +- .../javax/sound/sampled/AudioSystem.java | 20 +- .../classes/javax/sound/sampled/Clip.java | 6 +- .../classes/javax/sound/sampled/Line.java | 10 +- .../classes/javax/sound/sampled/Mixer.java | 8 +- .../javax/sound/sampled/SourceDataLine.java | 6 +- .../javax/sound/sampled/TargetDataLine.java | 6 +- .../classes/javax/swing/FocusManager.java | 16 +- .../share/classes/javax/swing/JDialog.java | 11 +- .../share/classes/javax/swing/JFrame.java | 6 +- .../share/classes/javax/swing/JTable.java | 11 - .../share/classes/javax/swing/UIManager.java | 6 +- .../classes/javax/swing/WindowConstants.java | 7 +- .../swing/filechooser/FileSystemView.java | 6 +- .../javax/swing/text/JTextComponent.java | 8 +- .../java/util/logging/FileHandler.java | 31 +- .../classes/java/util/logging/Handler.java | 31 +- .../classes/java/util/logging/LogManager.java | 56 +- .../classes/java/util/logging/Logger.java | 60 +- .../java/util/logging/LoggingMXBean.java | 5 +- .../java/util/logging/LoggingPermission.java | 20 +- .../java/util/logging/MemoryHandler.java | 11 +- .../java/util/logging/SocketHandler.java | 7 +- .../java/util/logging/StreamHandler.java | 15 +- .../management/remote/rmi/RMIConnection.java | 51 +- .../remote/rmi/RMIConnectionImpl.java | 88 +- .../MBeanServerFileAccessController.java | 17 +- .../lang/management/ClassLoadingMXBean.java | 6 +- .../lang/management/ManagementFactory.java | 4 - .../lang/management/ManagementPermission.java | 57 +- .../java/lang/management/MemoryMXBean.java | 6 +- .../lang/management/MemoryPoolMXBean.java | 14 +- .../management/OperatingSystemMXBean.java | 17 +- .../management/PlatformLoggingMXBean.java | 5 +- .../java/lang/management/RuntimeMXBean.java | 55 +- .../java/lang/management/ThreadMXBean.java | 51 +- .../javax/management/MBeanPermission.java | 6 +- .../classes/javax/management/MBeanServer.java | 188 +- .../javax/management/MBeanServerFactory.java | 28 +- .../management/MBeanServerPermission.java | 6 +- .../management/MBeanTrustPermission.java | 10 +- .../javax/management/monitor/Monitor.java | 26 +- .../remote/SubjectDelegationPermission.java | 6 +- .../naming/ldap/spi/LdapDnsProvider.java | 6 +- .../javax/naming/spi/NamingManager.java | 16 +- .../classes/java/net/http/HttpClient.java | 45 +- .../classes/java/net/http/HttpRequest.java | 13 +- .../classes/java/net/http/HttpResponse.java | 65 - .../classes/java/net/http/WebSocket.java | 8 +- .../classes/java/util/prefs/Preferences.java | 17 +- .../classes/java/rmi/RMISecurityManager.java | 35 +- .../java/rmi/server/RMIClassLoader.java | 103 +- .../java/rmi/server/RMIClassLoaderSpi.java | 15 +- .../java/rmi/server/RMISocketFactory.java | 20 +- .../classes/java/rmi/server/RemoteServer.java | 10 +- .../sun/rmi/registry/RegistryImpl.java | 168 +- .../classes/sun/rmi/server/LoaderHandler.java | 762 +----- .../auth/kerberos/DelegationPermission.java | 21 +- .../security/auth/kerberos/KerberosKey.java | 9 - .../auth/kerberos/KerberosPrincipal.java | 26 +- .../auth/kerberos/KerberosTicket.java | 14 +- .../javax/security/auth/kerberos/KeyTab.java | 14 +- .../auth/kerberos/ServicePermission.java | 44 +- .../classes/org/ietf/jgss/GSSContext.java | 29 +- .../classes/org/ietf/jgss/GSSManager.java | 45 +- .../share/classes/org/ietf/jgss/GSSName.java | 13 +- .../classes/org/ietf/jgss/package-info.java | 4 +- .../sql/rowset/serial/SerialJavaObject.java | 9 +- .../javax/sql/rowset/spi/SyncFactory.java | 36 +- .../share/classes/java/sql/Connection.java | 26 +- .../share/classes/java/sql/DriverManager.java | 24 +- .../share/classes/java/sql/SQLPermission.java | 76 +- .../share/classes/java/sql/package-info.java | 12 +- .../share/classes/javax/xml/XMLConstants.java | 6 +- .../javax/xml/catalog/CatalogManager.java | 4 +- .../sun/tools/attach/AttachPermission.java | 53 +- .../com/sun/tools/attach/VirtualMachine.java | 14 +- .../sun/tools/attach/spi/AttachProvider.java | 19 +- .../jdk/dynalink/CallSiteDescriptor.java | 11 +- .../jdk/dynalink/DynamicLinkerFactory.java | 5 +- .../jdk/dynalink/SecureLookupSupplier.java | 18 +- .../linker/GuardingDynamicLinkerExporter.java | 21 +- .../sun/net/httpserver/SimpleFileServer.java | 8 +- .../httpserver/spi/HttpServerProvider.java | 6 +- .../share/classes/com/sun/jdi/Bootstrap.java | 7 +- .../classes/com/sun/jdi/JDIPermission.java | 45 +- .../share/classes/jdk/jfr/Configuration.java | 5 +- .../share/classes/jdk/jfr/EventFactory.java | 9 +- .../share/classes/jdk/jfr/FlightRecorder.java | 28 +- .../jdk/jfr/FlightRecorderPermission.java | 45 +- .../share/classes/jdk/jfr/Recording.java | 22 +- .../classes/jdk/jfr/ValueDescriptor.java | 9 +- .../classes/jdk/jfr/consumer/EventStream.java | 13 +- .../jdk/jfr/consumer/RecordingFile.java | 12 +- .../jdk/jfr/consumer/RecordingStream.java | 12 +- .../management/jfr/FlightRecorderMXBean.java | 64 +- .../management/jfr/RemoteRecordingStream.java | 13 +- .../management/HotSpotDiagnosticMXBean.java | 16 +- .../com/sun/management/ThreadMXBean.java | 6 +- .../classes/jdk/net/NetworkPermission.java | 6 +- .../share/classes/jdk/net/Sockets.java | 23 - .../share/classes/jdk/nio/Channels.java | 10 +- .../classes/com/sun/nio/sctp/SctpChannel.java | 24 +- .../com/sun/nio/sctp/SctpMultiChannel.java | 28 - .../com/sun/nio/sctp/SctpServerChannel.java | 21 +- .../sun/security/auth/login/ConfigFile.java | 3 - .../security/auth/module/LdapLoginModule.java | 44 +- .../sun/security/jgss/ExtendedGSSContext.java | 7 - .../jgss/InquireSecContextPermission.java | 9 +- test/hotspot/jtreg/ProblemList.txt | 3 + .../compiler/exceptions/ExceptionInInit.java | 96 - .../jvmci/SecurityRestrictionsTest.java | 197 -- .../Nestmates/protectionDomain/Host.java | 44 - .../TestDifferentProtectionDomains.java | 124 - .../attach004/TestDescription.java | 62 - .../AttachOnDemand/attach004/attach004.policy | 9 - .../attach004/attach004Agent00.java | 58 - .../attach004/attach004Agent00.mf | 2 - .../catalog/CatalogReferCircularityTest.java | 5 +- .../catalog/DefaultFeaturesTest.java | 5 +- .../functional/catalog/DeferFeatureTest.java | 11 +- .../catalog/DelegatePublicTest.java | 5 +- .../catalog/DelegateSystemTest.java | 5 +- .../functional/catalog/DelegateUriTest.java | 5 +- .../jaxp/functional/catalog/GroupTest.java | 5 +- .../functional/catalog/LoadCatalogTest.java | 5 +- .../functional/catalog/NextCatalogTest.java | 5 +- .../functional/catalog/NormalizationTest.java | 5 +- .../functional/catalog/PreferFeatureTest.java | 5 +- .../jaxp/functional/catalog/PreferTest.java | 5 +- .../functional/catalog/PublicFamilyTest.java | 5 +- .../jaxp/functional/catalog/PublicTest.java | 5 +- .../catalog/ResolveFeatureTest.java | 5 +- .../functional/catalog/RewriteSystemTest.java | 5 +- .../functional/catalog/RewriteUriTest.java | 5 +- .../catalog/SpecifyCatalogTest.java | 5 +- .../functional/catalog/SystemFamilyTest.java | 5 +- .../functional/catalog/SystemSuffixTest.java | 5 +- .../jaxp/functional/catalog/SystemTest.java | 5 +- .../functional/catalog/UriFamilyTest.java | 5 +- .../functional/catalog/UriSuffixTest.java | 5 +- .../xml/jaxp/functional/catalog/UriTest.java | 5 +- .../functional/catalog/UrnUnwrappingTest.java | 5 +- .../catalog/ValidateCatalogTest.java | 5 +- .../xml/datatype/ptests/DurationTest.java | 5 +- .../ptests/FactoryNewInstanceTest.java | 5 +- .../ptests/XMLGregorianCalendarTest.java | 5 +- .../xml/parsers/ptests/DBFNamespaceTest.java | 5 +- .../ptests/DocumentBuilderFactoryTest.java | 5 +- .../parsers/ptests/DocumentBuilderImpl01.java | 5 +- .../parsers/ptests/FactoryConfErrorTest.java | 5 +- .../ptests/SAXFactoryNewInstanceTest.java | 5 +- .../xml/parsers/ptests/SAXParserFactTest.java | 5 +- .../xml/parsers/ptests/SAXParserTest.java | 15 +- .../xml/parsers/ptests/SAXParserTest02.java | 5 +- .../xml/parsers/ptests/SAXParserTest03.java | 5 +- .../XMLEventFactoryNewInstanceTest.java | 5 +- .../XMLInputFactoryNewInstanceTest.java | 5 +- .../XMLOutputFactoryNewInstanceTest.java | 5 +- .../xml/transform/ptests/Bug6384418Test.java | 5 +- .../xml/transform/ptests/DOMResultTest.java | 5 +- .../transform/ptests/ErrorListenerTest.java | 5 +- .../xml/transform/ptests/SAXSourceTest.java | 5 +- .../xml/transform/ptests/SAXTFactoryTest.java | 5 +- .../transform/ptests/StreamResultTest.java | 5 +- .../transform/ptests/TfClearParamTest.java | 5 +- .../xml/transform/ptests/TransformTest.java | 5 +- .../transform/ptests/TransformerExcpTest.java | 5 +- .../ptests/TransformerFactoryTest.java | 5 +- .../xml/transform/ptests/TransformerTest.java | 5 +- .../transform/ptests/TransformerTest02.java | 5 +- .../transform/ptests/TransformerTest03.java | 5 +- .../xml/transform/ptests/URIResolverTest.java | 5 +- .../ptests/othervm/TFCErrorTest.java | 5 +- .../validation/ptests/SchemaFactoryTest.java | 5 +- .../ptests/TypeInfoProviderTest.java | 5 +- .../ptests/ValidatorHandlerTest.java | 5 +- .../xml/validation/ptests/ValidatorTest.java | 5 +- .../ptests/XPathEvaluationResultTest.java | 5 +- .../xml/xpath/ptests/XPathExpressionTest.java | 5 +- .../xml/xpath/ptests/XPathFactoryTest.java | 5 +- .../ptests/XPathFunctionResolverTest.java | 5 +- .../javax/xml/xpath/ptests/XPathTest.java | 5 +- .../org/w3c/dom/ptests/AttrTest.java | 5 +- .../org/w3c/dom/ptests/CommentTest.java | 5 +- .../org/w3c/dom/ptests/DocumentTest.java | 5 +- .../org/w3c/dom/ptests/DocumentTypeTest.java | 5 +- .../w3c/dom/ptests/DomImplementationTest.java | 5 +- .../org/w3c/dom/ptests/ElementTest.java | 5 +- .../org/w3c/dom/ptests/EntityChildTest.java | 5 +- .../org/w3c/dom/ptests/NamedNodeMapTest.java | 5 +- .../org/w3c/dom/ptests/NodeListTest.java | 5 +- .../org/w3c/dom/ptests/NodeTest.java | 10 +- .../org/w3c/dom/ptests/NotationTest.java | 5 +- .../functional/org/w3c/dom/ptests/PITest.java | 5 +- .../org/w3c/dom/ptests/TextTest.java | 5 +- .../org/w3c/dom/ptests/TypeInfoTest.java | 5 +- .../org/xml/sax/ptests/AttrImplTest.java | 5 +- .../org/xml/sax/ptests/AttributesNSTest.java | 5 +- .../org/xml/sax/ptests/AttributesTest.java | 5 +- .../xml/sax/ptests/ContentHandlerTest.java | 5 +- .../xml/sax/ptests/DefaultHandlerTest.java | 5 +- .../org/xml/sax/ptests/EHFatalTest.java | 5 +- .../org/xml/sax/ptests/NSSupportTest.java | 5 +- .../org/xml/sax/ptests/NSTableTest.java | 5 +- .../org/xml/sax/ptests/ParserAdapterTest.java | 5 +- .../org/xml/sax/ptests/ResolverTest.java | 5 +- .../xml/sax/ptests/SAXParserNSTableTest.java | 5 +- .../org/xml/sax/ptests/XMLFilterCBTest.java | 5 +- .../org/xml/sax/ptests/XMLFilterTest.java | 5 +- .../xml/sax/ptests/XMLReaderAdapterTest.java | 5 +- .../xml/sax/ptests/XMLReaderFactoryTest.java | 5 +- .../xml/sax/ptests/XMLReaderNSTableTest.java | 5 +- .../org/xml/sax/ptests/XMLReaderTest.java | 5 +- .../jaxp/functional/test/astro/AstroTest.java | 5 +- .../functional/test/astro/DocumentLSTest.java | 5 +- .../test/astro/NamespaceContextTest.java | 5 +- .../functional/test/astro/SAX201Test.java | 5 +- .../test/astro/SchemaValidationTest.java | 5 +- .../functional/test/astro/XPathAPITest.java | 5 +- .../test/auctionportal/AuctionController.java | 5 +- .../auctionportal/AuctionItemRepository.java | 5 +- .../test/auctionportal/UserController.java | 5 +- .../functional/test/gaptest/Bug4511326.java | 5 +- .../functional/test/gaptest/Bug4512806.java | 5 +- .../functional/test/gaptest/Bug4515047.java | 5 +- .../functional/test/gaptest/Bug4515660.java | 5 +- .../functional/test/gaptest/Bug4693341.java | 5 +- .../functional/test/gaptest/Bug4848653.java | 5 +- .../functional/test/gaptest/Bug4858685.java | 5 +- .../jaxp/libs/jaxp/library/BasePolicy.java | 75 - .../jaxp/libs/jaxp/library/FilePolicy.java | 51 - .../libs/jaxp/library/InternalAPIPolicy.java | 43 - .../libs/jaxp/library/JAXPPolicyManager.java | 317 --- .../libs/jaxp/library/JAXPTestPolicy.java | 60 - .../libs/jaxp/library/JAXPTestUtilities.java | 136 +- .../libs/jaxp/library/NetAccessPolicy.java | 43 - .../unittest/catalog/CatalogAccessTest.java | 76 - .../catalog/CatalogFileInputTest.java | 4 +- .../unittest/catalog/CatalogResolverTest.java | 4 +- .../jaxp/unittest/catalog/CatalogSupport.java | 5 +- .../unittest/catalog/CatalogSupport1.java | 5 +- .../unittest/catalog/CatalogSupport2.java | 5 +- .../unittest/catalog/CatalogSupport3.java | 5 +- .../unittest/catalog/CatalogSupport4.java | 5 +- .../unittest/catalog/CatalogSupport5.java | 5 +- .../jaxp/unittest/catalog/CatalogTest.java | 5 +- .../xml/jaxp/unittest/catalog/GroupTest.java | 4 +- .../xml/jaxp/unittest/common/Bug6350682.java | 11 +- .../jaxp/unittest/common/Bug6723276Test.java | 5 +- .../jaxp/unittest/common/Bug7143711Test.java | 115 - .../unittest/common/CDataChunkSizeTest.java | 5 +- .../common/EncodingErrorsReportingTest.java | 5 +- .../xml/jaxp/unittest/common/Sources.java | 8 +- .../common/TransformationWarningsTest.java | 5 +- .../common/ValidationWarningsTest.java | 5 +- .../unittest/common/WarningsTestBase.java | 12 +- .../common/prettyprint/PrettyPrintTest.java | 5 +- .../jaxp/unittest/datatype/Bug6320118.java | 5 +- .../unittest/datatype/Bug6937951Test.java | 5 +- .../unittest/datatype/Bug6937964Test.java | 5 +- .../unittest/datatype/Bug7042647Test.java | 5 +- .../datatype/DatatypeFactoryTest.java | 5 +- .../jaxp/unittest/datatype/DurationTest.java | 5 +- .../unittest/datatype/FactoryFindTest.java | 13 +- .../unittest/datatype/JDK8068839Test.java | 5 +- .../datatype/XMLGregorianCalendarTest.java | 5 +- .../xml/jaxp/unittest/dom/Bug4915524.java | 5 +- .../xml/jaxp/unittest/dom/Bug4915748.java | 5 +- .../xml/jaxp/unittest/dom/Bug4966082.java | 5 +- .../xml/jaxp/unittest/dom/Bug4966138.java | 5 +- .../xml/jaxp/unittest/dom/Bug4966142.java | 5 +- .../xml/jaxp/unittest/dom/Bug4966143.java | 5 +- .../xml/jaxp/unittest/dom/Bug6339023.java | 5 +- .../xml/jaxp/unittest/dom/Bug6355326.java | 5 +- .../xml/jaxp/unittest/dom/Bug6367542.java | 5 +- .../xml/jaxp/unittest/dom/Bug6520131.java | 5 +- .../xml/jaxp/unittest/dom/Bug6521260.java | 5 +- .../xml/jaxp/unittest/dom/Bug6582545Test.java | 5 +- .../xml/jaxp/unittest/dom/Bug6879614Test.java | 5 +- .../xml/jaxp/unittest/dom/CR6333993Test.java | 5 +- .../xml/jaxp/unittest/dom/CR6517707Test.java | 5 +- .../xml/jaxp/unittest/dom/CR6517717Test.java | 5 +- .../xml/jaxp/unittest/dom/CR6909336Test.java | 5 +- .../unittest/dom/DOMConfigurationTest.java | 5 +- .../xml/jaxp/unittest/dom/DOMFeatureTest.java | 4 +- .../xml/jaxp/unittest/dom/DOMXPathTest.java | 5 +- .../xml/jaxp/unittest/dom/DocumentTest.java | 4 +- .../jaxp/unittest/dom/ElementTraversal.java | 5 +- .../xml/jaxp/unittest/dom/JdkXmlDomTest.java | 11 +- .../jaxp/unittest/dom/TCKEncodingTest.java | 5 +- .../xml/jaxp/unittest/dom/ls/Bug4973153.java | 5 +- .../xml/jaxp/unittest/dom/ls/Bug6290947.java | 5 +- .../xml/jaxp/unittest/dom/ls/Bug6354955.java | 5 +- .../xml/jaxp/unittest/dom/ls/Bug6376823.java | 5 +- .../jaxp/unittest/dom/ls/Bug6710741Test.java | 5 +- .../jaxp/unittest/dom/ls/LSParserTCKTest.java | 5 +- .../jaxp/unittest/dom/ls/LSParserTest.java | 5 +- .../unittest/dom/ls/LSSerializerTest.java | 5 +- .../unittest/parsers/BaseParsingTest.java | 5 +- .../parsers/Bug4674384_MAX_OCCURS_Test.java | 5 +- .../xml/jaxp/unittest/parsers/Bug4934208.java | 5 +- .../xml/jaxp/unittest/parsers/Bug4967002.java | 5 +- .../xml/jaxp/unittest/parsers/Bug4985486.java | 5 +- .../xml/jaxp/unittest/parsers/Bug4991020.java | 5 +- .../xml/jaxp/unittest/parsers/Bug4991946.java | 5 +- .../xml/jaxp/unittest/parsers/Bug5010072.java | 5 +- .../xml/jaxp/unittest/parsers/Bug5025825.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6309988.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6341770.java | 25 +- .../xml/jaxp/unittest/parsers/Bug6361283.java | 5 +- .../jaxp/unittest/parsers/Bug6506304Test.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6518733.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6564400.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6573786.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6594813.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6608841.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6690015.java | 5 +- .../xml/jaxp/unittest/parsers/Bug6760982.java | 5 +- .../jaxp/unittest/parsers/Bug6849942Test.java | 5 +- .../jaxp/unittest/parsers/Bug7157608Test.java | 5 +- .../jaxp/unittest/parsers/Bug7166896Test.java | 5 +- .../jaxp/unittest/parsers/Bug8003147Test.java | 75 +- .../xml/jaxp/unittest/parsers/Bug8073385.java | 13 +- .../unittest/parsers/FactoryFindTest.java | 13 +- .../jaxp/unittest/parsers/HandleError.java | 5 +- .../unittest/parsers/ParseEmptyStream.java | 5 +- .../unittest/parsers/SupplementaryChars.java | 3 - .../parsers/xinclude/Bug6794483Test.java | 5 +- .../unittest/sax/Attributes2ImplTest.java | 5 +- .../xml/jaxp/unittest/sax/Bug6889654Test.java | 5 +- .../xml/jaxp/unittest/sax/Bug6925410Test.java | 5 +- .../xml/jaxp/unittest/sax/Bug6949607Test.java | 5 +- .../xml/jaxp/unittest/sax/Bug6992561Test.java | 5 +- .../xml/jaxp/unittest/sax/Bug7057778Test.java | 13 +- .../unittest/sax/DefaultHandler2Test.java | 5 +- .../jaxp/unittest/sax/IssueTracker56Test.java | 5 +- .../xml/jaxp/unittest/sax/NSSupportTest.java | 5 +- .../xml/jaxp/unittest/sax/SAXParserTest.java | 4 +- .../unittest/sax/SymbolTableResetTest.java | 14 +- .../xml/jaxp/unittest/sax/XMLReaderTest.java | 5 +- .../AttributeLocalNameTest.java | 5 +- .../xml/jaxp/unittest/stream/Bug6370703.java | 5 +- .../xml/jaxp/unittest/stream/Bug6378422.java | 5 +- .../xml/jaxp/unittest/stream/Bug6380870.java | 5 +- .../xml/jaxp/unittest/stream/Bug6489502.java | 5 +- .../xml/jaxp/unittest/stream/Bug6509774.java | 5 +- .../jaxp/unittest/stream/Bug6688002Test.java | 5 +- .../jaxp/unittest/stream/Bug6976938Test.java | 5 +- .../stream/CoalesceTest/CoalesceTest.java | 5 +- .../stream/EntitiesTest/EntityTest.java | 5 +- .../stream/EventReaderDelegateTest.java | 5 +- .../stream/EventsTest/Issue41Test.java | 5 +- .../stream/EventsTest/Issue48Test.java | 5 +- .../stream/EventsTest/Issue53Test.java | 5 +- .../stream/EventsTest/Issue58Test.java | 5 +- .../jaxp/unittest/stream/FactoryFindTest.java | 17 +- .../stream/IgnoreExternalDTDTest.java | 5 +- .../ProcessingInstructionTest.java | 5 +- .../stream/StreamReaderDelegateTest.java | 5 +- .../unittest/stream/XMLEventLocationTest.java | 5 +- .../stream/XMLEventReaderTest/Bug6489890.java | 5 +- .../stream/XMLEventReaderTest/Bug6555001.java | 98 +- .../XMLEventReaderTest/Bug6586466Test.java | 5 +- .../XMLEventReaderTest/Bug6613059Test.java | 5 +- .../XMLEventReaderTest/Bug6668115Test.java | 5 +- .../XMLEventReaderTest/Bug6846133Test.java | 5 +- .../stream/XMLEventReaderTest/Bug8153781.java | 5 +- .../XMLEventReaderTest/EventReaderTest.java | 7 +- .../XMLEventReaderTest/Issue40Test.java | 5 +- .../stream/XMLEventReaderTest/JDK8201138.java | 5 +- .../stream/XMLEventReaderTest/JDK8209615.java | 5 +- .../ReaderToWriterTest.java | 5 +- .../XMLEventWriterTest.java | 5 +- .../XMLInputFactoryTest/Bug6756677Test.java | 17 +- .../XMLInputFactoryTest/Bug6909759Test.java | 5 +- .../XMLInputFactoryTest/IssueTracker38.java | 5 +- .../XMLOutputFactoryTest/Bug6846132Test.java | 5 +- .../DuplicateNSDeclarationTest.java | 5 +- .../StreamResultTest.java | 5 +- .../XMLResolverTest/XMLResolverTest.java | 5 +- .../ExceptionCauseTest.java | 5 +- .../XMLStreamExceptionTest/ExceptionTest.java | 5 +- .../XMLStreamFilterTest/Bug6481615.java | 5 +- .../XMLStreamFilterTest/Bug6481678.java | 5 +- .../XMLStreamFilterTest/HasNextTest.java | 5 +- .../stream/XMLStreamReaderTest/BOMTest.java | 5 +- .../XMLStreamReaderTest/Bug6388460.java | 5 +- .../XMLStreamReaderTest/Bug6472982Test.java | 5 +- .../XMLStreamReaderTest/Bug6767322Test.java | 5 +- .../XMLStreamReaderTest/Bug6847819Test.java | 5 +- .../stream/XMLStreamReaderTest/BugTest.java | 5 +- .../DefaultAttributeTest.java | 5 +- .../XMLStreamReaderTest/DoubleXmlnsTest.java | 5 +- .../XMLStreamReaderTest/IsValidatingTest.java | 5 +- .../XMLStreamReaderTest/Issue44Test.java | 5 +- .../XMLStreamReaderTest/Issue47Test.java | 5 +- .../XMLStreamReaderTest/IssueTracker24.java | 5 +- .../XMLStreamReaderTest/IssueTracker35.java | 5 +- .../XMLStreamReaderTest/IssueTracker70.java | 5 +- .../Jsr173MR1Req5Test.java | 5 +- .../Jsr173MR1Req8Test.java | 5 +- .../XMLStreamReaderTest/NamespaceTest.java | 5 +- .../XMLStreamReaderTest/StreamReaderTest.java | 5 +- .../XMLStreamReaderTest/SupportDTDTest.java | 5 +- .../XMLStreamReaderTest/VoiceXMLDTDTest.java | 5 +- .../stream/XMLStreamReaderTest/XML11Test.java | 5 +- .../AttributeEscapeTest.java | 5 +- .../XMLStreamWriterTest/Bug6452107.java | 5 +- .../XMLStreamWriterTest/Bug6600882Test.java | 5 +- .../XMLStreamWriterTest/Bug6675332Test.java | 5 +- .../XMLStreamWriterTest/Bug7037352Test.java | 5 +- .../XMLStreamWriterTest/DomUtilTest.java | 5 +- .../XMLStreamWriterTest/EmptyElementTest.java | 5 +- .../XMLStreamWriterTest/EncodingTest.java | 5 +- .../XMLStreamWriterTest/NamespaceTest.java | 5 +- .../NullUriDetectionTest.java | 5 +- .../XMLStreamWriterTest/SqeLinuxTest.java | 5 +- .../XMLStreamWriterTest/SurrogatesTest.java | 5 +- .../UnprefixedNameTest.java | 5 +- .../XMLStreamWriterTest/WriterTest.java | 5 +- .../XMLStreamWriterTest.java | 5 +- .../unittest/transform/Bug4693341Test.java | 5 +- .../jaxp/unittest/transform/Bug4892774.java | 5 +- .../jaxp/unittest/transform/Bug5073477.java | 5 +- .../jaxp/unittest/transform/Bug6175602.java | 5 +- .../jaxp/unittest/transform/Bug6206491.java | 5 +- .../unittest/transform/Bug6216226Test.java | 13 +- .../jaxp/unittest/transform/Bug6311448.java | 5 +- .../jaxp/unittest/transform/Bug6384805.java | 5 +- .../jaxp/unittest/transform/Bug6465722.java | 5 +- .../jaxp/unittest/transform/Bug6467808.java | 5 +- .../jaxp/unittest/transform/Bug6490380.java | 5 +- .../jaxp/unittest/transform/Bug6490921.java | 5 +- .../jaxp/unittest/transform/Bug6513892.java | 5 +- .../jaxp/unittest/transform/Bug6537167.java | 5 +- .../jaxp/unittest/transform/Bug6540545.java | 5 +- .../jaxp/unittest/transform/Bug6551616.java | 5 +- .../jaxp/unittest/transform/Bug6559595.java | 5 +- .../jaxp/unittest/transform/Bug6565260.java | 5 +- .../jaxp/unittest/transform/Bug6940416.java | 5 +- .../unittest/transform/BugDB12665704Test.java | 5 +- .../xml/jaxp/unittest/transform/CLITest.java | 26 +- .../unittest/transform/CR6401137Test.java | 5 +- .../unittest/transform/CR6551600Test.java | 76 +- .../unittest/transform/CR6577667Test.java | 5 +- .../unittest/transform/CR6652519Test.java | 5 +- .../unittest/transform/CR6689809Test.java | 5 +- .../unittest/transform/CR6905829Test.java | 5 +- .../unittest/transform/CR6935697Test.java | 5 +- .../unittest/transform/CR6941869Test.java | 5 +- .../unittest/transform/CR6957215Test.java | 5 +- .../unittest/transform/CR7098746Test.java | 5 +- .../unittest/transform/DOMResultTest.java | 5 +- .../unittest/transform/FactoryFindTest.java | 13 +- .../unittest/transform/Issue2204Test.java | 5 +- .../unittest/transform/Issue2290Test.java | 5 +- .../jaxp/unittest/transform/JDK8207760.java | 5 +- .../transform/NamespacePrefixTest.java | 12 +- .../jaxp/unittest/transform/SAX2DOMTest.java | 5 +- .../transform/SecureProcessingTest.java | 5 +- .../jaxp/unittest/transform/SourceTest.java | 5 +- .../unittest/transform/StAXSourceTest.java | 5 +- .../unittest/transform/StylesheetTest.java | 6 +- .../unittest/transform/SurrogateTest.java | 15 +- .../unittest/transform/TemplatesTest.java | 5 +- .../transform/TransformerFactoryTest.java | 5 +- .../unittest/transform/TransformerTest.java | 5 +- .../unittest/transform/XSLTFunctionsTest.java | 16 +- .../unittest/transform/sax/Bug6451633.java | 5 +- .../unittest/transform/sort/SortTest.java | 5 +- .../unittest/transform/util/StreamUtil.java | 11 +- .../unittest/validation/AnyElementTest.java | 5 +- .../jaxp/unittest/validation/Bug4966232.java | 5 +- .../jaxp/unittest/validation/Bug4966254.java | 5 +- .../jaxp/unittest/validation/Bug4969042.java | 5 +- .../jaxp/unittest/validation/Bug4969089.java | 5 +- .../jaxp/unittest/validation/Bug4969110.java | 5 +- .../jaxp/unittest/validation/Bug4969689.java | 5 +- .../jaxp/unittest/validation/Bug4969692.java | 5 +- .../jaxp/unittest/validation/Bug4969693.java | 5 +- .../jaxp/unittest/validation/Bug4969695.java | 5 +- .../jaxp/unittest/validation/Bug4969732.java | 5 +- .../jaxp/unittest/validation/Bug4970380.java | 5 +- .../jaxp/unittest/validation/Bug4970383.java | 5 +- .../jaxp/unittest/validation/Bug4970400.java | 5 +- .../jaxp/unittest/validation/Bug4970402.java | 5 +- .../jaxp/unittest/validation/Bug4970951.java | 5 +- .../jaxp/unittest/validation/Bug4971605.java | 5 +- .../jaxp/unittest/validation/Bug4971607.java | 5 +- .../jaxp/unittest/validation/Bug4972882.java | 5 +- .../jaxp/unittest/validation/Bug4986844.java | 5 +- .../jaxp/unittest/validation/Bug4987574.java | 5 +- .../jaxp/unittest/validation/Bug4988267.java | 5 +- .../jaxp/unittest/validation/Bug4988268.java | 5 +- .../jaxp/unittest/validation/Bug4988387.java | 5 +- .../jaxp/unittest/validation/Bug4996446.java | 5 +- .../jaxp/unittest/validation/Bug4997818.java | 5 +- .../jaxp/unittest/validation/Bug5011500.java | 5 +- .../jaxp/unittest/validation/Bug5072946.java | 5 +- .../jaxp/unittest/validation/Bug6378043.java | 5 +- .../jaxp/unittest/validation/Bug6449797.java | 5 +- .../jaxp/unittest/validation/Bug6457662.java | 5 +- .../unittest/validation/Bug6467424Test.java | 5 +- .../jaxp/unittest/validation/Bug6483188.java | 5 +- .../jaxp/unittest/validation/Bug6493687.java | 5 +- .../jaxp/unittest/validation/Bug6509668.java | 5 +- .../jaxp/unittest/validation/Bug6526547.java | 5 +- .../jaxp/unittest/validation/Bug6531160.java | 5 +- .../unittest/validation/Bug6695843Test.java | 5 +- .../unittest/validation/Bug6773084Test.java | 11 +- .../jaxp/unittest/validation/Bug6859210.java | 5 +- .../unittest/validation/Bug6925531Test.java | 166 +- .../unittest/validation/Bug6946312Test.java | 5 +- .../unittest/validation/Bug6954738_Test.java | 5 +- .../unittest/validation/CR6708840Test.java | 5 +- .../jaxp/unittest/validation/CR6740048.java | 5 +- .../unittest/validation/Issue682Test.java | 5 +- .../unittest/validation/IssueTracker30.java | 5 +- .../unittest/validation/JaxpIssue43Test.java | 5 +- .../jaxp/unittest/validation/JaxpIssue49.java | 5 +- .../validation/LargeMaxOccursTest.java | 5 +- .../unittest/validation/MultiOccursTest.java | 5 +- .../validation/MultiOccursUnboundedTest.java | 5 +- .../jaxp/unittest/validation/OccursTest.java | 5 +- .../validation/OccursUnboundedTest.java | 5 +- .../validation/OccursWildcardTest.java | 5 +- .../validation/OccursWildcardUnbounded.java | 5 +- .../validation/ParticlesId005Test.java | 5 +- .../validation/ParticlesIg004Test.java | 5 +- .../validation/ParticlesQ013Test.java | 5 +- .../unittest/validation/TCKGroupA008Test.java | 5 +- .../unittest/validation/ValidationTest.java | 4 +- .../unittest/validation/ValidatorTest.java | 17 +- .../validation/tck/Bug6943252Test.java | 5 +- .../validation/tck/Bug6963124Test.java | 5 +- .../validation/tck/Bug6963468Test.java | 5 +- .../validation/tck/Bug6964720Test.java | 5 +- .../validation/tck/Bug6967214Test.java | 5 +- .../validation/tck/Bug6970890Test.java | 5 +- .../validation/tck/Bug6971190Test.java | 5 +- .../validation/tck/Bug6974551Test.java | 5 +- .../validation/tck/Bug6975265Test.java | 5 +- .../validation/tck/Bug6977201Test.java | 5 +- .../validation/tck/Bug6989956Test.java | 5 +- .../validation/tck/Bug7014246Test.java | 5 +- .../unittest/validation/tck/ParticleTest.java | 5 +- .../unittest/validation/tck/RegexWord.java | 5 +- .../xml/jaxp/unittest/xpath/Bug4991857.java | 5 +- .../xml/jaxp/unittest/xpath/Bug4991939.java | 5 +- .../xml/jaxp/unittest/xpath/Bug4992788.java | 5 +- .../xml/jaxp/unittest/xpath/Bug4992793.java | 5 +- .../xml/jaxp/unittest/xpath/Bug4992805.java | 5 +- .../jaxp/unittest/xpath/ClassLoaderTest.java | 12 +- .../unittest/xpath/SecureProcessingTest.java | 5 +- .../jaxp/unittest/xpath/XPathAnyTypeTest.java | 5 +- .../unittest/xpath/XPathExpAnyTypeTest.java | 5 +- test/jdk/ProblemList.txt | 6 + test/jdk/TEST.groups | 6 +- .../TestAESWithProviderChange.java | 3 +- .../TestAESWithRemoveAddProvider.java | 3 +- .../Cipher/AES/TestAESCiphers/testAES.policy | 7 - .../util/8051626/Bug8051626.java | 78 - .../sun/jndi/ldap/LdapDnsProviderTest.java | 52 +- .../DcmdMBeanPermissionsTest.java | 246 -- ...rTest.java => RootDirPermissionsTest.java} | 129 +- .../SecurityManagerTestNoRead.policy | 39 - .../SecurityManagerTestRead.policy | 43 - .../security/auth/UnixPrincipalHashCode.java | 4 +- .../login/ConfigFile/InnerClassConfig.java | 4 +- .../login/ConfigFile/InnerClassConfig.policy | 8 - .../login/ConfigFile/PropertyExpansion.java | 57 +- .../login/ConfigFile/PropertyExpansion.policy | 8 - .../module/LdapLoginModule/CheckConfigs.java | 8 +- .../LdapLoginModule/CheckConfigs.policy | 10 - test/jdk/com/sun/security/auth/uphc.policy | 6 - .../com/sun/tools/attach/PermissionTest.java | 136 - .../com/sun/tools/attach/java.policy.allow | 22 - .../jdk/com/sun/tools/attach/java.policy.deny | 18 - .../java/awt/Debug/DumpOnKey/DumpOnKey.java | 4 +- test/jdk/java/awt/Debug/DumpOnKey/dump.policy | 3 - .../DefaultPermissions.java | 39 - .../Desktop/DefaultPermissions/java.policy | 1 - .../SecurityTest/DesktopSecurityTest.java | 192 -- .../awt/Desktop/SecurityTest/desktop.policy | 7 - .../ModalDialogPermission.java | 61 - .../Dialog/ModalDialogPermission/java.policy | 3 - .../PropertyPermissionOnEDT.java | 106 - .../PropertyPermissionOnEDT/java.policy | 4 - .../CloseDialogActivateOwnerTest.java | 110 - .../CloseDialogActivateOwnerTest/java.policy | 3 - .../awt/FontClass/CreateFont/BigFont.java | 160 -- .../CreateFont/fileaccess/FontFile.java | 104 - .../CreateFont/fileaccess/TestFontFile.sh | 80 - test/jdk/java/awt/FontClass/FontAccess.java | 48 - .../jdk/java/awt/FontClass/FontPrivilege.java | 41 - .../awt/FullScreen/SetFSWindow/FSFrame.java | 53 +- .../PrintJob/Security/SecurityDialogTest.java | 106 - test/jdk/java/awt/PrintJob/Security/policy | 32 - .../java/awt/Toolkit/GetImage/bug8078165.java | 18 +- .../Toolkit/SecurityTest/SecurityTest2.java | 53 - .../FunctionalityCheck.java | 11 +- .../FunctionalityCheck/tray.policy | 23 - .../NoPermissionTest/NoPermissionTest.java | 65 - .../NoPermissionTest/tray.policy | 3 - .../PermissionTest/PermissionTest.java | 51 - .../SecurityCheck/PermissionTest/tray.policy | 3 - .../jdk/java/awt/color/LoadProfileWithSM.java | 43 - .../java/awt/color/StandardProfileTest.java | 55 - .../java/awt/color/StandardProfileTest.policy | 30 - .../DragInterceptorAppletTest.java | 153 -- .../DragInterceptorFrame.java | 128 - .../InterprocessMessages.java | 32 - .../SourceFrame.java | 68 - .../RegisterKeyStroke/TestAWTKeyStroke.java | 6 +- .../event/KeyEvent/RegisterKeyStroke/policy | 2 - test/jdk/java/awt/image/mlib/MlibOpsTest.java | 7 +- .../java/awt/image/mlib/mlib.security.policy | 3 - .../print/PrintServicesSecurityManager.java | 49 - .../awt/print/PrinterJob/CheckAccess.java | 95 - .../awt/print/PrinterJob/GetUserNameTest.java | 49 - .../print/PrinterJob/GetUserNameTest.policy | 28 - .../process/ProcessCommunicator.java | 3 +- test/jdk/java/awt/security/Permissions.java | 105 - .../WarningWindowDisposeCrashTest.java | 54 - .../WarningWindowDisposeTest.java | 97 - .../security/WarningWindowDisposeTest/policy | 3 - test/jdk/java/beans/Beans/Test4080522.java | 99 - .../java/beans/EventHandler/Test6277246.java | 60 - .../java/beans/EventHandler/Test6277266.java | 65 - .../Introspector/7084904/Test7084904.java | 5 +- .../java/beans/Introspector/Test4683761.java | 6 +- .../java/beans/Introspector/Test6277246.java | 55 - .../PropertyEditor/TestBooleanClass.java | 40 - .../beans/PropertyEditor/TestBooleanType.java | 40 - .../beans/PropertyEditor/TestByteClass.java | 40 - .../beans/PropertyEditor/TestByteType.java | 40 - .../beans/PropertyEditor/TestColorClass.java | 43 - .../beans/PropertyEditor/TestDoubleClass.java | 40 - .../beans/PropertyEditor/TestDoubleType.java | 40 - .../beans/PropertyEditor/TestEnumClass.java | 42 - .../PropertyEditor/TestEnumSubclass.java | 54 - .../beans/PropertyEditor/TestFloatClass.java | 40 - .../beans/PropertyEditor/TestFloatType.java | 40 - .../beans/PropertyEditor/TestFontClass.java | 43 - .../PropertyEditor/TestIntegerClass.java | 40 - .../beans/PropertyEditor/TestIntegerType.java | 40 - .../beans/PropertyEditor/TestLongClass.java | 40 - .../beans/PropertyEditor/TestLongType.java | 40 - .../beans/PropertyEditor/TestShortClass.java | 40 - .../beans/PropertyEditor/TestShortType.java | 40 - .../beans/PropertyEditor/TestStringClass.java | 40 - .../LoadingStandardIcons.java | 4 +- .../LoadingStandardIcons/java.policy | 1 - .../Statement/ClassForName/ClassForName.java | 3 +- .../beans/Statement/ClassForName/java.policy | 1 - .../jdk/java/beans/Statement/Test6224433.java | 56 - .../beans/XMLDecoder/spec/AbstractTest.java | 11 +- .../java/beans/XMLDecoder/spec/TestArray.java | 6 +- .../beans/XMLDecoder/spec/TestBoolean.java | 6 +- .../java/beans/XMLDecoder/spec/TestByte.java | 6 +- .../java/beans/XMLDecoder/spec/TestChar.java | 6 +- .../java/beans/XMLDecoder/spec/TestClass.java | 6 +- .../beans/XMLDecoder/spec/TestDouble.java | 6 +- .../java/beans/XMLDecoder/spec/TestFalse.java | 6 +- .../java/beans/XMLDecoder/spec/TestField.java | 6 +- .../java/beans/XMLDecoder/spec/TestFloat.java | 6 +- .../java/beans/XMLDecoder/spec/TestInt.java | 6 +- .../java/beans/XMLDecoder/spec/TestJava.java | 6 +- .../java/beans/XMLDecoder/spec/TestLong.java | 6 +- .../beans/XMLDecoder/spec/TestMethod.java | 6 +- .../java/beans/XMLDecoder/spec/TestNew.java | 6 +- .../java/beans/XMLDecoder/spec/TestNull.java | 6 +- .../beans/XMLDecoder/spec/TestObject.java | 6 +- .../beans/XMLDecoder/spec/TestProperty.java | 6 +- .../java/beans/XMLDecoder/spec/TestShort.java | 6 +- .../beans/XMLDecoder/spec/TestString.java | 6 +- .../java/beans/XMLDecoder/spec/TestTrue.java | 6 +- .../java/beans/XMLDecoder/spec/TestVar.java | 6 +- .../4741757/TestSecurityManager.java | 47 - .../beans/XMLEncoder/6777487/TestBox.java | 5 +- .../6777487/TestCheckedCollection.java | 5 +- .../XMLEncoder/6777487/TestCheckedList.java | 5 +- .../XMLEncoder/6777487/TestCheckedMap.java | 5 +- .../6777487/TestCheckedRandomAccessList.java | 5 +- .../XMLEncoder/6777487/TestCheckedSet.java | 5 +- .../6777487/TestCheckedSortedMap.java | 5 +- .../6777487/TestCheckedSortedSet.java | 5 +- .../beans/XMLEncoder/6777487/TestEncoder.java | 4 +- .../beans/XMLEncoder/6777487/TestEnumMap.java | 5 +- .../beans/XMLEncoder/6777487/TestEnumSet.java | 5 +- .../java/beans/XMLEncoder/AbstractTest.java | 11 +- .../XMLEncoder/ReferenceToNonStaticField.java | 6 +- .../java/beans/XMLEncoder/Test4631471.java | 14 +- .../java/beans/XMLEncoder/Test4652928.java | 6 +- .../java/beans/XMLEncoder/Test4679556.java | 6 +- .../java/beans/XMLEncoder/Test4903007.java | 6 +- .../java/beans/XMLEncoder/Test4935607.java | 6 +- .../java/beans/XMLEncoder/Test4936682.java | 6 +- .../java/beans/XMLEncoder/Test4993777.java | 6 +- .../java/beans/XMLEncoder/Test4994637.java | 6 +- .../java/beans/XMLEncoder/Test5023550.java | 6 +- .../java/beans/XMLEncoder/Test5023552.java | 6 +- .../java/beans/XMLEncoder/Test5023557.java | 6 +- .../java/beans/XMLEncoder/Test5023559.java | 6 +- .../java/beans/XMLEncoder/Test6176120.java | 6 +- .../java/beans/XMLEncoder/Test6187118.java | 6 +- .../java/beans/XMLEncoder/Test6256805.java | 6 +- .../java/beans/XMLEncoder/Test6437265.java | 6 +- .../java/beans/XMLEncoder/Test6501431.java | 6 +- .../java/beans/XMLEncoder/Test6505888.java | 6 +- .../java/beans/XMLEncoder/Test6531597.java | 6 +- .../java/beans/XMLEncoder/Test6570354.java | 6 +- .../java/beans/XMLEncoder/Test6852574.java | 6 +- .../java/beans/XMLEncoder/Test6921644.java | 6 +- .../java/beans/XMLEncoder/Test6989223.java | 6 +- .../java/beans/XMLEncoder/Test7080156.java | 6 +- .../java/beans/XMLEncoder/Test7092744.java | 6 +- .../java/beans/XMLEncoder/Test7169395.java | 6 +- .../java/beans/XMLEncoder/Test8013416.java | 6 +- .../java/beans/XMLEncoder/Test8013557.java | 6 +- .../java/beans/XMLEncoder/Test8016545.java | 6 +- .../java/beans/XMLEncoder/Test8027066.java | 6 +- .../XMLEncoder/java_awt_AWTKeyStroke.java | 6 +- .../XMLEncoder/java_awt_BasicStroke.java | 6 +- .../XMLEncoder/java_awt_BorderLayout.java | 6 +- .../beans/XMLEncoder/java_awt_CardLayout.java | 6 +- .../java/beans/XMLEncoder/java_awt_Color.java | 6 +- .../beans/XMLEncoder/java_awt_Component.java | 6 +- .../beans/XMLEncoder/java_awt_Cursor.java | 6 +- .../beans/XMLEncoder/java_awt_Dimension.java | 6 +- .../java/beans/XMLEncoder/java_awt_Font.java | 6 +- .../XMLEncoder/java_awt_GradientPaint.java | 6 +- .../java_awt_GridBagConstraints.java | 6 +- .../XMLEncoder/java_awt_GridBagLayout.java | 8 +- .../beans/XMLEncoder/java_awt_Insets.java | 6 +- .../java_awt_LinearGradientPaint.java | 11 +- .../XMLEncoder/java_awt_MenuShortcut.java | 6 +- .../java/beans/XMLEncoder/java_awt_Point.java | 6 +- .../java_awt_RadialGradientPaint.java | 11 +- .../beans/XMLEncoder/java_awt_Rectangle.java | 6 +- .../beans/XMLEncoder/java_awt_ScrollPane.java | 6 +- .../java_awt_geom_AffineTransform.java | 6 +- .../XMLEncoder/java_beans_EventHandler.java | 6 +- .../XMLEncoder/java_beans_Expression.java | 6 +- .../XMLEncoder/java_beans_Statement.java | 6 +- .../beans/XMLEncoder/java_lang_Character.java | 6 +- .../beans/XMLEncoder/java_lang_Class.java | 6 +- .../java/beans/XMLEncoder/java_lang_Enum.java | 6 +- .../beans/XMLEncoder/java_lang_String.java | 6 +- .../XMLEncoder/java_lang_reflect_Field.java | 6 +- .../XMLEncoder/java_lang_reflect_Method.java | 6 +- .../java/beans/XMLEncoder/java_net_URI.java | 6 +- .../java/beans/XMLEncoder/java_sql_Date.java | 6 +- .../java/beans/XMLEncoder/java_sql_Time.java | 6 +- .../beans/XMLEncoder/java_sql_Timestamp.java | 6 +- .../beans/XMLEncoder/java_util_ArrayList.java | 6 +- .../java_util_Collections_EmptyList.java | 6 +- .../java_util_Collections_EmptyMap.java | 6 +- .../java_util_Collections_EmptySet.java | 6 +- .../java_util_Collections_SingletonList.java | 6 +- .../java_util_Collections_SingletonMap.java | 6 +- .../java_util_Collections_SingletonSet.java | 6 +- ...il_Collections_SynchronizedCollection.java | 6 +- ...ava_util_Collections_SynchronizedList.java | 6 +- ...java_util_Collections_SynchronizedMap.java | 6 +- ...lections_SynchronizedRandomAccessList.java | 6 +- ...java_util_Collections_SynchronizedSet.java | 6 +- ...til_Collections_SynchronizedSortedMap.java | 6 +- ...til_Collections_SynchronizedSortedSet.java | 6 +- ...il_Collections_UnmodifiableCollection.java | 6 +- ...ava_util_Collections_UnmodifiableList.java | 6 +- ...java_util_Collections_UnmodifiableMap.java | 6 +- ...lections_UnmodifiableRandomAccessList.java | 6 +- ...java_util_Collections_UnmodifiableSet.java | 6 +- ...til_Collections_UnmodifiableSortedMap.java | 6 +- ...til_Collections_UnmodifiableSortedSet.java | 6 +- .../java/beans/XMLEncoder/java_util_Date.java | 6 +- .../beans/XMLEncoder/java_util_HashMap.java | 6 +- .../beans/XMLEncoder/javax_swing_Box.java | 6 +- .../XMLEncoder/javax_swing_BoxLayout.java | 6 +- .../XMLEncoder/javax_swing_Box_Filler.java | 6 +- .../javax_swing_DefaultCellEditor.java | 6 +- .../beans/XMLEncoder/javax_swing_JButton.java | 6 +- .../XMLEncoder/javax_swing_JComponent.java | 6 +- .../XMLEncoder/javax_swing_JLayeredPane.java | 6 +- .../XMLEncoder/javax_swing_JSplitPane.java | 6 +- .../beans/XMLEncoder/javax_swing_JTree.java | 6 +- .../XMLEncoder/javax_swing_KeyStroke.java | 6 +- .../XMLEncoder/javax_swing_OverlayLayout.java | 6 +- .../javax_swing_border_BevelBorder.java | 6 +- .../javax_swing_border_CompoundBorder.java | 6 +- .../javax_swing_border_EmptyBorder.java | 6 +- .../javax_swing_border_EtchedBorder.java | 6 +- .../javax_swing_border_LineBorder.java | 6 +- .../javax_swing_border_MatteBorder.java | 6 +- .../javax_swing_border_SoftBevelBorder.java | 6 +- .../javax_swing_border_StrokeBorder.java | 6 +- .../javax_swing_border_TitledBorder.java | 6 +- ...orderUIResource_BevelBorderUIResource.java | 6 +- ...erUIResource_CompoundBorderUIResource.java | 7 +- ...orderUIResource_EmptyBorderUIResource.java | 6 +- ...rderUIResource_EtchedBorderUIResource.java | 6 +- ...BorderUIResource_LineBorderUIResource.java | 6 +- ...orderUIResource_MatteBorderUIResource.java | 6 +- ...rderUIResource_TitledBorderUIResource.java | 6 +- .../javax_swing_plaf_ColorUIResource.java | 6 +- .../javax_swing_plaf_FontUIResource.java | 6 +- .../javax_swing_tree_DefaultTreeModel.java | 6 +- .../XMLEncoder/javax_swing_tree_TreePath.java | 6 +- .../sun_swing_PrintColorUIResource.java | 6 +- test/jdk/java/foreign/TestLinker.java | 5 +- test/jdk/java/foreign/security.policy | 7 - .../java/io/Console/SecurityManagerTest.java | 35 - test/jdk/java/io/Console/test.policy | 3 - test/jdk/java/io/File/CheckPermission.java | 380 --- test/jdk/java/io/File/GetXSpace.java | 111 +- .../io/File/createTempFile/SecurityTest.java | 47 - .../java/io/File/createTempFile/java.policy | 2 - .../jdk/java/io/FilePermission/MergeName.java | 91 - .../io/FilePermission/ReadFileOnPath.java | 93 - .../java/io/FilePermission/m/module-info.java | 3 - test/jdk/java/io/FilePermission/m/p/App.java | 44 - .../AuditStreamSubclass.java | 85 +- .../records/AbsentStreamValuesTest.java | 3 +- .../Serializable/records/BasicRecordSer.java | 3 +- .../records/ConstructorAccessTest.java | 3 +- .../records/ConstructorPermissionTest.java | 138 - .../io/Serializable/records/CycleTest.java | 3 +- .../records/DifferentStreamFieldsTest.java | 3 +- .../Serializable/records/ReadResolveTest.java | 4 +- .../Serializable/records/RecordClassTest.java | 3 +- .../records/SerialVersionUIDTest.java | 3 +- .../records/ThrowingConstructorTest.java | 3 +- .../records/WriteReplaceTest.java | 3 +- .../records/empty_security.policy | 25 - .../serialFilter/CheckInputOrderTest.java | 3 +- .../FilterWithSecurityManagerTest.java | 105 - .../serialFilter/GlobalFilterTest.java | 64 +- .../serialFilter/MixedFiltersTest.java | 2 +- .../serialFilter/SerialFilterFactoryTest.java | 80 +- .../Serializable/serialFilter/security.policy | 13 - .../security.policy.without.globalFilter | 9 - .../io/Serializable/subclass/Allow.policy | 9 - .../Serializable/subclass/SubclassTest.java | 20 +- .../Serializable/subclassGC/SubclassGC.java | 9 +- .../Serializable/subclassGC/security.policy | 8 - .../Class/forName/modules/TestDriver.java | 48 +- .../lang/Class/forName/modules/TestLayer.java | 12 +- .../java/lang/Class/forName/modules/policy | 7 - .../lang/Class/forName/modules/policy.denied | 11 - .../forName/modules/src/m3/module-info.java | 25 - .../forName/modules/src/m3/p3/NoAccess.java | 112 - .../src/m3/p3/NoGetClassLoaderAccess.java | 67 - .../modules/src/m3/p3/internal/Foo.java | 27 - .../ClassDeclaredFieldsTest.java | 159 +- .../FieldSetAccessibleTest.java | 184 +- ...closingConstructorWithSecurityManager.java | 50 - .../EnclosingMethodWithSecurityManager.java | 50 - .../jdk/java/lang/Class/getResource/Main.java | 13 +- .../lang/Class/getResource/ResourcesTest.java | 3 +- .../lang/ClassLoader/UninitializedParent.java | 69 - .../forNameLeak/ClassForNameLeak.java | 4 +- .../lang/ClassLoader/forNameLeak/test.policy | 7 - .../ClassLoader/getResource/modules/Main.java | 14 +- .../getResource/modules/ResourcesTest.java | 3 +- .../securityManager/ClassLoaderTest.java | 299 --- .../securityManager/TestClassLoader.java | 34 - .../securityManager/TestClient.java | 34 - .../securityManager/malformed.policy | 4 - .../ClassLoader/securityManager/valid.policy | 4 - .../lang/ModuleTests/WithSecurityManager.java | 145 -- test/jdk/java/lang/ModuleTests/allow.policy | 28 - test/jdk/java/lang/ProcessBuilder/Basic.java | 195 +- .../ProcessBuilder/SecurityManagerClinit.java | 91 - .../lang/ProcessHandle/PermissionTest.java | 245 -- .../lang/RuntimeTests/exec/ExecCommand.java | 97 +- test/jdk/java/lang/SecurityManager/Basic.java | 65 + .../CheckAccessClassInPackagePermissions.java | 128 - .../SecurityManager/CheckPackageAccess.java | 192 -- .../SecurityManager/CheckPackageMatching.java | 542 ---- .../CheckSecurityProvider.java | 9 +- .../SecurityManager/PackageAccessTest.java | 98 - .../java/lang/SecurityManager/empty.policy | 1 - .../modules/CustomSecurityManagerTest.java | 84 - .../modules/m/module-info.java | 25 - .../modules/m/p/CustomSecurityManager.java | 29 - .../lang/SecurityManager/modules/test.policy | 3 - .../jdk/java/lang/SecurityManager/test.policy | 3 - .../CallerSensitiveMethod/Main.java | 3 +- .../csm/jdk/test/CallerSensitiveTest.java | 25 +- .../lang/StackWalker/GetCallerClassTest.java | 21 +- .../lang/StackWalker/SecurityExceptions.java | 49 - .../java/lang/StackWalker/StackWalkTest.java | 4 +- .../lang/StackWalker/VerifyStackTrace.java | 10 +- test/jdk/java/lang/StackWalker/noperms.policy | 5 - .../java/lang/StackWalker/stackwalk.policy | 4 - .../lang/StackWalker/stackwalktest.policy | 5 - .../String/concat/WithSecurityManager.java | 79 - .../lang/System/AllowSecurityManager.java | 38 +- .../System/IgnoreNullSecurityManager.java | 37 - .../Logger/custom/CustomLoggerTest.java | 197 +- .../Logger/default/DefaultLoggerTest.java | 241 +- .../BaseLoggerFinderTest.java | 282 +-- .../DefaultLoggerFinderTest.java | 288 +-- .../BaseDefaultLoggerFinderTest.java | 222 +- .../BaseLoggerBridgeTest.java | 361 +-- .../BasePlatformLoggerTest.java | 226 +- .../BootstrapLogger/BootstrapLoggerTest.java | 228 +- .../LoggerBridgeTest/LoggerBridgeTest.java | 261 +- .../LoggerFinderLoaderTest.java | 209 +- .../PlatformLoggerBridgeTest.java | 237 +- .../DefaultLoggerBridgeTest.java | 307 +-- .../DefaultPlatformLoggerTest.java | 117 +- .../lang/System/SecurityManagerWarnings.java | 86 +- test/jdk/java/lang/System/SecurityRace.java | 202 -- test/jdk/java/lang/System/System.policy | 40 - .../lang/annotation/ParameterAnnotations.java | 40 +- .../CustomRepeatingWithSecurityManager.java | 110 - .../RepeatingWithSecurityManager.java | 67 - .../methodTypeDesc/ResolveConstantDesc.java | 107 - .../jdk.unsupported/sun/misc/Test.java | 31 - .../lang/constant/methodTypeDesc/test.policy | 6 - .../appendToClassLoaderSearch/run_tests.sh | 4 - .../lang/invoke/7196190/GetUnsafeTest.java | 111 - .../lang/invoke/7196190/jtreg.security.policy | 9 - .../java/lang/invoke/8076596/Test8076596.java | 48 - .../8076596/Test8076596.security.policy | 8 - .../lang/invoke/FindClassSecurityManager.java | 43 - .../lang/invoke/InvokeDynamicPrintArgs.java | 38 +- .../lang/invoke/MethodHandleConstants.java | 31 +- .../WithSecurityManagerTest.java | 68 - .../MethodHandleProxies/jtreg.security.policy | 9 - .../invoke/MethodTypeSecurityManager.java | 77 - .../java/lang/invoke/RevealDirectTest.java | 33 +- .../java/lang/invoke/TestPrivateMember.java | 64 - .../lang/invoke/callerSensitive/Main.java | 5 +- .../csm/jdk/test/MethodInvokeTest.java | 23 +- .../java/lang/invoke/getclassloader.policy | 10 - .../java/lang/invoke/jtreg.security.policy | 9 - .../LambdaAccessControlDoPrivilegedTest.java | 6 +- .../lambda/LambdaAccessControlTest.java | 48 - .../lambda/LogGeneratedClassesTest.java | 7 - .../ModuleFinderWithSecurityManager.java | 70 - test/jdk/java/lang/module/java.policy | 4 - .../reflect/Nestmates/TestReflectionAPI.java | 3 +- .../Nestmates/TestSecurityManagerChecks.java | 110 - .../java/lang/reflect/Nestmates/empty.policy | 5 - .../lang/reflect/Nestmates/testPkg/Host.java | 30 - .../reflect/Nestmates/testPkg/Singleton.java | 28 - .../nonPublicProxy/NonPublicProxyClass.java | 105 +- .../Proxy/nonPublicProxy/SimpleProxy.java | 18 +- .../lang/reflect/records/IsRecordTest.java | 3 +- .../records/RecordPermissionsTest.java | 212 -- .../reflect/records/RecordReflectionTest.java | 1 - .../reflect/records/allPermissions.policy | 27 - .../TestSecurityManagerChecks.java | 275 -- .../java/lang/runtime/ObjectMethodsTest.java | 3 +- test/jdk/java/lang/runtime/empty.policy | 26 - .../Authenticator/GetAuthenticatorTest.java | 16 +- .../net/DatagramSocket/ConnectPortZero.java | 46 +- .../java/net/DatagramSocket/SendPortZero.java | 39 +- .../net/DatagramSocket/TimeoutWithSM.java | 118 - .../net/IPSupport/MinimumPermissions.java | 40 - .../net/IPSupport/MinimumPermissions.policy | 29 - .../net/InetAddress/GetLocalHostWithSM.java | 89 - .../net/MulticastSocket/SendPortZero.java | 40 +- .../jdk/java/net/NetworkInterface/Equals.java | 6 +- .../net/ServerSocket/TestLocalAddress.java | 289 --- .../SetFactoryPermission.java | 88 - test/jdk/java/net/Socket/UdpSocket.java | 82 +- .../java/net/SocketOption/OptionsTest.java | 3 +- test/jdk/java/net/SocketOption/options.policy | 27 - .../SocketPermissionTest.java | 355 --- test/jdk/java/net/URL/OpenConnection.java | 5 +- test/jdk/java/net/URL/TestIPv6Addresses.java | 20 +- .../getresourceasstream/TestDriver.java | 15 - .../URLClassLoader/getresourceasstream/policy | 26 - test/jdk/java/net/URLPermission/OpenURL.java | 77 +- test/jdk/java/net/URLPermission/URLTest.java | 160 +- .../net/URLPermission/nstest/LookupTest.java | 51 +- .../net/httpclient/AsFileDownloadTest.java | 7 +- .../net/httpclient/AsFileDownloadTest.policy | 72 - .../net/httpclient/ConnectExceptionTest.java | 15 +- .../net/httpclient/DependentActionsTest.java | 4 +- .../DependentPromiseActionsTest.java | 12 +- .../FilePublisher/FilePublisherPermsTest.java | 104 +- .../FilePublisherPermsTest1.policy | 87 - .../FilePublisherPermsTest2.policy | 92 - .../FilePublisherPermsTest3.policy | 95 - .../FilePublisher/FilePublisherTest.java | 7 +- .../FilePublisher/FilePublisherTest.policy | 80 - .../httpclient/HttpClientLocalAddrTest.java | 11 +- .../BodyHandlerOfFileDownloadTest.java | 3 +- .../PathSubscriber/BodyHandlerOfFileTest.java | 4 +- .../BodySubscriberOfFileTest.java | 12 +- .../httpclient/PathSubscriber/ofFile.policy | 95 - .../PathSubscriber/ofFileDownload.policy | 92 - .../java/net/httpclient/RequestBodyTest.java | 14 +- .../net/httpclient/RequestBodyTest.policy | 62 - test/jdk/java/net/httpclient/dependent.policy | 74 - .../httpclient-localaddr-security.policy | 70 - .../java/net/httpclient/noPermissions.policy | 24 - .../jdk/java/net/httpclient/security/0.policy | 47 - .../jdk/java/net/httpclient/security/1.policy | 46 - .../java/net/httpclient/security/10.policy | 45 - .../java/net/httpclient/security/11.policy | 53 - .../java/net/httpclient/security/12.policy | 53 - .../java/net/httpclient/security/14.policy | 46 - .../java/net/httpclient/security/15.policy | 49 - .../java/net/httpclient/security/16.policy | 47 - .../java/net/httpclient/security/17.policy | 47 - .../jdk/java/net/httpclient/security/2.policy | 46 - .../jdk/java/net/httpclient/security/3.policy | 46 - .../jdk/java/net/httpclient/security/4.policy | 49 - .../jdk/java/net/httpclient/security/5.policy | 46 - .../jdk/java/net/httpclient/security/6.policy | 46 - .../jdk/java/net/httpclient/security/7.policy | 47 - .../jdk/java/net/httpclient/security/8.policy | 46 - .../jdk/java/net/httpclient/security/9.policy | 46 - .../java/net/httpclient/security/Driver.java | 172 -- .../net/httpclient/security/Security.java | 554 ---- .../FileProcessorPermissionTest.java | 90 +- .../filePerms/SecurityBeforeFile.java | 18 +- .../security/filePerms/allpermissions.policy | 28 - .../security/filePerms/nopermissions.policy | 26 - .../websocket/security/WSSanityTest.java | 196 ++ .../security/WSURLPermissionTest.java | 581 ----- .../websocket/security/httpclient.policy | 27 - .../ResolvePermissionTest.java | 95 - .../RuntimePermissionTest.java | 102 - .../spi/URLStreamHandlerProvider/Basic.java | 10 - .../spi/URLStreamHandlerProvider/basic.policy | 27 - .../AsynchronousChannelGroup/AsExecutor.java | 32 +- .../bootlib/Attack.java | 64 - .../bootlib/PrivilegedThreadFactory.java | 50 - .../WithSecurityManager.java | 76 - .../java.policy.allow | 3 - .../java.policy.deny | 3 - .../DatagramChannel/ConnectPortZero.java | 49 +- .../DatagramChannel/SendPortZero.java | 49 +- .../FileChannel/MapWithSecurityManager.java | 43 - .../InheritedChannelTest.java | 23 +- .../inheritedChannel/StateTestService.java | 9 +- .../inheritedChannel/java.policy.fail | 11 - .../inheritedChannel/java.policy.pass | 11 - .../nio/channels/unixdomain/Security.java | 203 -- test/jdk/java/nio/channels/unixdomain/policy1 | 26 - test/jdk/java/nio/channels/unixdomain/policy2 | 28 - test/jdk/java/nio/channels/unixdomain/policy3 | 28 - .../charset/spi/CharsetProviderBasicTest.java | 21 +- .../java/nio/charset/spi/charsetProvider.sp | 5 - test/jdk/java/nio/charset/spi/default-pol | 39 - .../java/nio/file/Files/CheckPermissions.java | 792 ------ .../nio/file/Files/CopyToNonDefaultFS.java | 4 +- .../java/nio/file/Files/FaultyFileSystem.java | 10 +- test/jdk/java/nio/file/Files/StreamTest.java | 151 -- test/jdk/java/nio/file/Files/copy.policy | 4 - .../Files/walkFileTree/WalkWithSecurity.java | 129 - .../file/Files/walkFileTree/denyAll.policy | 3 - .../file/Files/walkFileTree/grantAll.policy | 5 - .../Files/walkFileTree/grantTopOnly.policy | 4 - .../java/nio/file/Path/MacToRealPath.policy | 4 - .../nio/file/Path/MacToRealPathWithSM.java | 89 - .../WatchService/WithSecurityManager.java | 84 - .../java/nio/file/WatchService/denyAll.policy | 3 - .../WatchService/grantDirAndOneLevel.policy | 5 - .../file/WatchService/grantDirAndTree.policy | 5 - .../nio/file/WatchService/grantDirOnly.policy | 4 - .../java/nio/file/spi/SetDefaultProvider.java | 18 +- test/jdk/java/nio/file/spi/fs.policy | 3 - .../CheckPackageAccess.java | 71 - test/jdk/java/rmi/dgc/VMID/CheckVMID.java | 7 +- test/jdk/java/rmi/dgc/VMID/security.policy | 10 - .../dgcImplInsulation/DGCImplInsulation.java | 113 - .../DGCImplInsulation_Stub.java | 38 - .../rmi/dgc/dgcImplInsulation/security.policy | 7 - .../AltSecurityManager.java | 134 - .../TestSecurityManager.java | 46 - .../registry.security.policy | 8 - .../altSecurityManager/rmid.security.policy | 5 - .../classPathCodebase/ClassPathCodebase.java | 11 +- .../registry.security.policy | 18 - .../classPathCodebase/security.policy | 29 - .../rmi/registry/readTest/CodebaseTest.java | 19 +- .../readTest/registry.security.policy | 12 - .../serialFilter/RegistryFilterTest.java | 4 +- .../rmi/registry/serialFilter/security.policy | 4 - .../reliability/benchmark/bench/rmi/Main.java | 21 +- .../benchmark/bench/rmi/policy.all | 3 - .../rmi/reliability/juicer/AppleUserImpl.java | 4 +- .../rmi/reliability/juicer/security.policy | 4 - .../DelegateBeforePermissionCheck.java | 70 - .../delegateBeforePermissionCheck/Foo.java | 25 - .../DelegateToContextLoader.java | 13 +- .../delegateToContextLoader/security.policy | 21 - .../DownloadArrayClass.java | 144 -- .../DownloadArrayClass_Stub.java | 94 - .../downloadArrayClass/Foo.java | 25 - .../downloadArrayClass/Receiver.java | 31 - .../downloadArrayClass/security.policy | 22 - .../RMIClassLoader/getClassLoader/Foo.java | 25 - .../getClassLoader/GetClassLoader.java | 134 - .../getClassLoader/security.policy | 19 - .../loadProxyClasses/LoadProxyClasses.java | 25 +- .../loadProxyClasses/security.policy | 22 - .../RMIClassLoader/spi/ContextInsulation.java | 85 - .../RMIClassLoader/spi/DefaultProperty.java | 47 +- .../server/RMIClassLoader/spi/Installed.java | 4 +- .../RMIClassLoader/spi/InvalidProperty.java | 6 +- .../server/RMIClassLoader/spi/Property.java | 4 +- .../RMIClassLoader/spi/TestProvider.java | 4 +- .../server/RMIClassLoader/spi/security.policy | 22 - .../useCodebaseOnly/UseCodebaseOnly.java | 10 +- .../useCodebaseOnly/security.policy | 26 - .../RMIClassLoader/useGetURLs/UseGetURLs.java | 8 +- .../RMIClassLoader/useGetURLs/security.policy | 18 - .../useSocketFactory/registry/HelloImpl.java | 4 +- .../registry/UseCustomSocketFactory.java | 8 +- .../useSocketFactory/registry/security.policy | 34 - .../useSocketFactory/unicast/EchoImpl.java | 4 +- .../unicast/UseCustomSocketFactory.java | 10 +- .../useSocketFactory/unicast/security.policy | 34 - .../server/RemoteObject/toStub/ToStub.java | 8 +- .../RemoteObject/toStub/security.policy | 4 - .../setLogPermission/SetLogPermission.java | 81 - .../setLogPermission/security.policy | 3 - .../useDynamicProxies/UseDynamicProxies.java | 10 +- .../useDynamicProxies/security.policy | 4 - .../UnreferencedContext.java | 8 +- .../clientStackTrace/ClientStackTrace.java | 7 +- .../server/clientStackTrace/security.policy | 8 - .../rmi/server/useCustomRef/UseCustomRef.java | 5 +- .../rmi/server/useCustomRef/security.policy | 25 - .../jdk/java/rmi/testlibrary/TestLibrary.java | 31 +- .../rmi/testlibrary/TestLoaderHandler.java | 696 +++++ .../transport/dgcDeadLock/DGCDeadLock.java | 5 +- .../dgcDeadLock/registry.security.policy | 27 - .../rmi/transport/dgcDeadLock/security.policy | 27 - .../readTimeout/ReadTimeoutTest.java | 8 +- .../rmi/transport/readTimeout/security.policy | 10 - .../CheckNullPermission.java | 45 - .../security/AccessController/DoPriv.java | 68 - .../AccessController/DoPrivAccomplice.java | 32 - .../DoPrivAccompliceTest.java | 116 - .../security/AccessController/DoPrivTest.java | 30 - .../AccessController/LimitedDoPrivileged.java | 215 -- .../LimitedDoPrivilegedWithNullPerms.java | 96 - .../LimitedDoPrivilegedWithThread.java | 108 - .../jdk/java/security/AccessController/policy | 4 - .../BasicPermission/NullOrEmptyName.java | 18 +- test/jdk/java/security/KeyRep/Serial.java | 1 - test/jdk/java/security/KeyRep/Serial.policy | 4 - .../java/security/KeyRep/SerialDSAPubKey.java | 4 +- .../security/KeyRep/SerialDSAPubKey.policy | 6 - test/jdk/java/security/KeyRep/SerialOld.java | 3 +- .../jdk/java/security/KeyRep/SerialOld.policy | 16 - .../Policy/Dynamic/DynamicPolicy.java | 97 - .../Policy/Dynamic/TestDynamicPolicy.java | 124 - .../java/security/Policy/Dynamic/setpolicy.jp | 8 - .../ExtensiblePolicyTest.java | 65 - .../ExtensiblePolicyTest1.policy | 4 - .../ExtensiblePolicyTest2.policy | 3 - .../ExtensiblePolicyTest3.policy | 5 - .../ExtensiblePolicyWithJarTest.java | 115 - .../ExtensiblePolicy/TVJar/TVPermission.java | 358 --- .../Policy/GetInstance/GetInstance.java | 248 -- .../Policy/GetInstance/GetInstance.policy | 28 - .../Policy/GetInstance/GetInstance.policyURL | 3 - .../GetInstance/GetInstancePolicySpi.java | 64 - .../GetInstance/GetInstanceProvider.java | 40 - .../GetInstance/GetInstanceSecurity.java | 62 - .../GetInstance/GetInstanceSecurity.policy | 6 - .../Policy/GetPermissions/JarURL.java | 51 - .../Policy/GetPermissions/JarURL.policy | 8 - .../Policy/PolicyProvider/CustomPolicy.java | 61 - .../PolicyProvider/UseSystemClassLoader.java | 95 - .../Policy/PolicyProvider/test.policy | 9 - test/jdk/java/security/Policy/Root/Root.java | 103 - .../jdk/java/security/Policy/Root/Root.policy | 3 - .../Policy/SignedJar/SignedJarTest.java | 190 -- .../Policy/SignedJar/SignedJarTest_1.policy | 10 - .../Policy/SignedJar/SignedJarTest_2.policy | 11 - .../security/Policy/SignedJar/java.security | 3 - .../java/security/Policy/SignedJar/keypass | 1 - .../security/ProtectionDomain/AllPerm.jar | Bin 728 -> 0 bytes .../security/ProtectionDomain/AllPerm.java | 109 - .../ProtectionDomain/NullGetActions.java | 75 - .../ProtectionDomain/NullGetActions.policy | 3 - .../PreserveCombinerTest.java | 68 - .../security/ProtectionDomain/Recursion.java | 85 - .../ProtectionDomain/Recursion.policy | 6 - .../ProtectionDomain/RecursionDebug.java | 91 - .../SecureClassLoader/DefineClass.java | 303 --- .../SecureClassLoader/DefineClass.policy | 14 - .../java/security/Security/AddProvider.java | 59 - .../security/Security/AddProvider.policy.1 | 3 - .../security/Security/AddProvider.policy.2 | 5 - .../security/Security/AddProvider.policy.3 | 4 - .../SecurityPropFile/SecurityPropFile.java | 4 +- .../SecurityPropFile/SecurityPropFile.policy | 5 - .../removing/RemoveStaticProvider.java | 4 +- .../removing/RemoveStaticProvider.policy | 5 - .../security/UnresolvedPermission/Debug.java | 70 - .../UnresolvedPermission/Debug.policy | 6 - .../DebugPermission0.java | 45 - .../DebugPermission1.java | 45 - .../DebugPermission2.java | 45 - .../DebugPermissionBad.java | 45 - .../security/UnresolvedPermission/Equals.java | 53 - .../UnresolvedPermission/Equals.policy | 4 - .../cert/CertPathBuilder/GetInstance.java | 4 +- .../cert/CertPathBuilder/provider.policy | 8 - .../DriverManagerPermissionsTests.java | 161 -- .../java/time/chrono/Bug8178823.java | 37 - .../java/time/chrono/bug8178823.policy | 1 - .../util/PluggableLocale/PermissionTest.java | 80 - .../java/util/PluggableLocale/dummy.policy | 0 .../localeServiceProvider.policy | 3 - .../java/util/Properties/LoadAndStoreXML.java | 56 +- .../Properties/StoreReproducibilityTest.java | 100 +- .../java/util/ResourceBundle/Bug6359330.java | 53 - .../modules/security/TestPermission.java | 82 - .../modules/security/src/m1/module-info.java | 27 - .../modules/security/src/m1/p1/Bundle.java | 32 - .../src/m1/p1/resources/MyResources.java | 35 - .../security/src/test/jdk/test/Main.java | 64 - .../jdk/test/resources/TestResources.java | 35 - .../security/src/test/module-info.java | 26 - .../ServiceLoader/security/SecurityTest.java | 29 - .../security/test/module-info.java | 41 - .../ServiceLoader/security/test/p/Tests.java | 243 -- test/jdk/java/util/TimeZone/Bug6912560.java | 52 - .../util/TimeZone/SetDefaultSecurityTest.java | 65 - .../TimeZone/TimeZoneDatePermissionCheck.java | 40 - .../TimeZoneDatePermissionCheckRun.java | 108 - .../concurrent/BlockingQueue/LastElement.java | 4 +- .../Executors/PrivilegedCallables.java | 62 +- .../ThreadPoolExecutor/ConfigChanges.java | 12 +- .../ThreadPoolExecutor/ThrowingTasks.java | 12 +- .../concurrent/atomic/AtomicUpdaters.java | 99 +- .../tck/AbstractExecutorServiceTest.java | 9 +- .../concurrent/tck/CompletableFutureTest.java | 11 - .../util/concurrent/tck/ExecutorsTest.java | 185 +- .../concurrent/tck/ForkJoinPool9Test.java | 20 +- .../util/concurrent/tck/ForkJoinPoolTest.java | 6 +- .../util/concurrent/tck/FutureTaskTest.java | 46 - .../util/concurrent/tck/JSR166TestCase.java | 175 +- .../concurrent/tck/ThreadLocalRandomTest.java | 3 - .../java/util/concurrent/tck/ThreadTest.java | 14 +- test/jdk/java/util/concurrent/tck/tck.policy | 18 - .../AnonymousLogger/TestAnonymousLogger.java | 133 - .../util/logging/FileHandlerLongLimit.java | 254 +- .../java/util/logging/FileHandlerPath.java | 224 +- .../logging/FileHandlerPatternExceptions.java | 216 +- .../java/util/logging/HandlersConfigTest.java | 14 +- .../ParentLoggerWithHandlerGC.java | 158 +- .../Configuration/TestConfigurationLock.java | 30 +- .../BadRootLoggerHandlers.java | 6 +- .../RootLoggerHandlers.java | 4 +- .../rootLoggerHandlers/test.policy | 13 - .../HandlersOnComplexResetUpdate.java | 161 +- .../HandlersOnComplexUpdate.java | 161 +- ...SimpleUpdateConfigWithInputStreamTest.java | 199 +- .../SimpleUpdateConfigurationTest.java | 212 +- .../UpdateConfigurationTest.java | 188 +- .../setLevel/TestRootLoggerLevel.java | 53 +- .../Logger/getGlobal/TestGetGlobal.java | 10 +- .../Logger/getGlobal/TestGetGlobalByName.java | 8 +- .../getGlobal/TestGetGlobalConcurrent.java | 11 +- .../java/util/logging/Logger/getGlobal/policy | 8 - .../logging/Logger/getLogger/TestLogger.java | 8 +- .../TestSetResourceBundle.java | 138 +- .../RootLogger/RootLevelInConfigFile.java | 72 +- .../util/logging/SystemLoggerConfigTest.java | 84 +- .../logging/TestConfigurationListeners.java | 221 +- .../java/util/logging/TestGetLoggerNPE.java | 16 +- .../logging/TestLogConfigurationDeadLock.java | 28 +- .../TestLogConfigurationDeadLockWithConf.java | 34 +- .../util/logging/TestLoggerBundleSync.java | 24 +- .../TestLoggingWithMainAppContext.java | 8 +- .../java/util/logging/TestMainAppContext.java | 5 +- ...rContext.java => TestUILoggerContext.java} | 135 +- .../modules/GetResourceBundleTest.java | 18 +- .../UserDefaultControlTest.java | 39 +- test/jdk/java/util/spi/ToolProviderTest.java | 36 +- .../accessibilityProvider.sp | 4 - .../AccessibilityProvider/basic.sh | 11 +- .../CachePermissionsTest.java | 103 +- .../imageio/CachePremissionsTest/rw.policy | 5 - .../imageio/CachePremissionsTest/rwd.policy | 5 - .../imageio/CachePremissionsTest/w.policy | 5 - .../TestClassPathPlugin.sh | 16 +- .../BadPluginConfigurationTest.sh | 23 +- .../spi/AppletContextTest/IIOPluginTest.java | 22 +- .../ImplVersionTest.java | 6 +- .../management/ImplementationVersion/policy | 5 - .../Introspector/AnnotationSecurityTest.java | 164 -- .../AnnotationSecurityTest.policy | 15 - .../GetAllDescriptorsTest.java | 4 +- .../SimpleModelMBeanCommand.java | 4 +- .../modelmbean/SimpleModelMBean/policy | 14 - .../management/monitor/StartStopTest.java | 6 - .../management/monitor/ThreadPoolAccTest.java | 9 +- test/jdk/javax/management/monitor/all.policy | 3 - .../mandatory/loading/RMIDownloadTest.java | 41 +- .../mandatory/notif/NoPermToRemoveTest.java | 187 -- .../NotificationAccessControllerTest.java | 1 - .../notif/NotificationEmissionTest.java | 5 - .../remote/mandatory/notif/policy.negative | 7 - .../remote/mandatory/notif/policy.positive | 7 - .../NonJMXPrincipalsTest.java | 1 - .../PasswordAccessFileTest.java | 1 - .../passwordAuthenticator/RMIAltAuthTest.java | 2 - .../RMIPasswdAuthTest.java | 2 - .../passwordAuthenticator/SimpleStandard.java | 5 +- .../mandatory/version/ImplVersionTest.java | 7 +- .../remote/mandatory/version/policy | 3 - .../security/AuthorizationTest.java | 14 - .../security/java.policy.authorization | 88 - .../ssl/finalize/SSLSessionFinalizeTest.java | 3 +- .../javax/net/ssl/finalize/security.policy | 13 - .../script/JDK_8196959/BadFactoryTest.java | 3 +- test/jdk/javax/script/ProviderTest.sh | 9 +- .../MoreThenOnePrincipals.java | 99 - .../MoreThenOnePrincipals.policy | 10 - .../PrivateCredentialPermission/Serial.java | 3 +- .../PrivateCredentialPermission/Serial.policy | 5 - .../PrivateCredentialPermission/Subset.java | 612 ----- .../PrivateCredentialPermission/Subset.policy | 10 - .../auth/Subject/CallAsWithScopedValue.java | 3 +- .../javax/security/auth/Subject/Compat.java | 128 - .../security/auth/Subject/CurrentSubject.java | 3 +- .../security/auth/Subject/Exceptions.java | 4 +- .../javax/security/auth/Subject/FromACC.java | 62 - .../javax/security/auth/Subject/Serial.java | 22 +- .../javax/security/auth/Subject/Serial.policy | 5 - .../javax/security/auth/Subject/Synch2.java | 4 +- .../javax/security/auth/Subject/Synch2.policy | 3 - .../security/auth/Subject/UnsupportedSV.java | 73 - .../auth/Subject/doAs/NestedActions.java | 542 ---- .../security/auth/Subject/doAs/Test.java | 66 - .../javax/security/auth/Subject/doAs/policy | 10 - .../auth/Subject/doAs/policy.expect.ace | 27 - .../auth/Subject/doAs/policy.expect.pae | 19 - .../auth/Subject/doAs/policy.one.principal | 36 - .../auth/Subject/doAs/policy.two.principals | 37 - .../SubjectDomainCombiner/Regression.java | 14 +- .../SubjectDomainCombiner/Regression.policy | 12 - .../GetInstanceSecurity.grantedPolicy | 8 - .../Configuration/GetInstanceSecurity.java | 110 - .../Configuration/GetInstanceSecurity.policy | 15 - .../login/LoginContext/ConfigConstructor.java | 71 +- .../LoginContext/ConfigConstructor.policy | 7 - .../LoginContext/ConfigConstructorNoPerm.java | 134 - .../ConfigConstructorNoPerm.policy | 3 - .../smartcardio/TerminalFactorySpiTest.java | 4 +- test/jdk/javax/smartcardio/policy | 4 - .../DefaultProperties/DefaultProperties.java | 5 +- .../DefaultPropertiesNegative.java | 79 - .../MidiSystem/DefaultProperties/java.policy | 10 - .../DefaultProperties/negative.policy | 5 - ...Exception.java => EmptySoundBankTest.java} | 7 +- .../security.policy | 4 - .../DefaultProperties/DefaultProperties.java | 166 -- .../DefaultPropertiesNegative.java | 78 - .../AudioSystem/DefaultProperties/java.policy | 10 - .../DefaultProperties/negative.policy | 5 - .../testdata/conf/sound.properties | 27 - .../SyncFactoryPermissionsTests.java | 183 -- .../8080972/TestBasicComboBoxEditor.java | 9 +- .../swing/JEditorPane/5076514/bug5076514.java | 65 - .../JEditorPane/8080972/TestJEditor.java | 23 +- .../JFileChooser/6484091/bug6484091.java | 61 - .../JFileChooser/6570445/bug6570445.java | 44 - .../JFileChooser/6738668/bug6738668.java | 58 - .../JFileChooser/6738668/security.policy | 5 - .../JFileChooser/7036025/bug7036025.java | 62 - .../JFileChooser/7036025/security.policy | 5 - .../JFileChooser/8062561/bug8062561.java | 215 -- .../JFileChooser/8062561/security.policy | 5 - .../JFileChooser/8062561/security2.policy | 1 - ...ShellFolderQueriesSecurityManagerTest.java | 59 - .../shellfolderqueries.policy | 5 - .../8080972/TestDefaultFormatter.java | 7 +- .../swing/JOptionPane/8081019/bug8081019.java | 12 +- .../swing/JPopupMenu/6675802/bug6675802.java | 43 - .../swing/JPopupMenu/6691503/bug6691503.java | 116 - .../swing/JPopupMenu/6694823/bug6694823.java | 135 - .../JTable/8080972/TestJTableCellEditor.java | 8 +- .../swing/UIDefaults/6622002/bug6622002.java | 30 +- .../swing/UIDefaults/6795356/TableTest.java | 12 +- .../8080972/TestProxyLazyValue.java | 8 +- .../dnd/8080972/TestTransferHandler.java | 7 +- .../8080972/TestAbstractRegionPainter.java | 7 +- .../javax/swing/plaf/synth/Test8043627.java | 40 - .../text/View/8080972/TestObjectView.java | 7 +- test/jdk/javax/swing/text/rtf/bug4178276.java | 55 - .../crypto/dsig/ErrorHandlerPermissions.java | 119 - .../dsig/ErrorHandlerPermissions.policy | 5 - ...issions.java => ResolveReferenceURIs.java} | 30 +- .../SecurityManager/XMLDSigWithSecMgr.java | 157 -- .../xml/crypto/dsig/SecurityManager/policy | 3 - .../dsig/TransformService/NullParent.java | 3 +- .../crypto/dsig/TransformService/test.policy | 3 - .../crypto/dsig/keyinfo/KeyInfo/Marshal.java | 4 +- .../crypto/dsig/keyinfo/KeyInfo/test.policy | 3 - .../jaxp/common/8020430/JAXP15RegTest.java | 39 +- .../xml/jaxp/common/8020430/TestBase.java | 95 - .../parsers/8021148/JAXPSAXParserTest.java | 38 +- .../xml/jaxp/parsers/8021148/TestBase.java | 95 - .../xml/jaxp/parsers/8022548/TestBase.java | 73 - .../jaxp/parsers/8022548/XOMParserTest.java | 58 +- .../xml/jaxp/testng/validation/BaseTest.java | 57 +- .../xml/jaxp/transform/8004476/TestBase.java | 22 +- .../transform/8004476/XPathExFuncTest.java | 94 +- .../transform/8004476/XSLTExFuncTest.java | 81 +- test/jdk/jdk/dynalink/BeanLinkerTest.java | 54 +- .../TrustedDynamicLinkerFactoryTest.java | 4 +- .../UntrustedDynamicLinkerFactoryTest.java | 48 - test/jdk/jdk/dynalink/trusted.security.policy | 7 - .../jdk/dynalink/untrusted.security.policy | 3 - test/jdk/jdk/incubator/vector/VectorRuns.java | 3 +- .../incubator/vector/empty_security.policy | 25 - .../internal/jrtfs/WithSecurityManager.java | 93 - test/jdk/jdk/internal/jrtfs/java.policy | 3 - .../jfr/api/consumer/TestHiddenMethod.java | 50 +- .../recordingstream/TestOnErrorAsync.java | 6 +- .../recordingstream/TestOnErrorSync.java | 6 +- .../security/DriverRecordingDumper.java | 47 - .../security/TestMissingPermission.java | 68 - .../consumer/security/TestRecordingFile.java | 53 - .../security/TestRecordingStream.java | 58 - .../consumer/security/TestStreamingFile.java | 53 - .../consumer/security/TestStreamingLocal.java | 68 - .../security/TestStreamingRemote.java | 113 - .../consumer/security/local-streaming.policy | 4 - .../consumer/security/no-permission.policy | 3 - .../jmx/security/TestEnoughPermission.java | 103 - .../jmx/security/TestNoControlPermission.java | 71 - .../jmx/security/TestNoMonitorPermission.java | 63 - .../TestNotificationListenerPermission.java | 85 - test/jdk/jdk/jfr/jmx/security/enough.policy | 19 - test/jdk/jdk/jfr/jmx/security/listener.policy | 11 - .../jdk/jdk/jfr/jmx/security/nocontrol.policy | 11 - .../jdk/jdk/jfr/jmx/security/nomonitor.policy | 11 - .../jdk/jfr/startupargs/TestDumpOnExit.java | 21 +- test/jdk/jdk/nio/zipfs/Basic.java | 3 +- .../jdk/nio/zipfs/DirectoryStreamTests.java | 3 +- .../jdk/nio/zipfs/InvalidZipHeaderTests.java | 3 +- .../jdk/jdk/nio/zipfs/NewFileSystemTests.java | 3 +- test/jdk/jdk/nio/zipfs/PathOps.java | 4 +- .../nio/zipfs/PropertyPermissionTests.java | 4 +- .../jdk/nio/zipfs/PropertyPermissions.policy | 4 - test/jdk/jdk/nio/zipfs/TestPosix.java | 2 +- test/jdk/jdk/nio/zipfs/ZFSTests.java | 4 +- .../jdk/nio/zipfs/ZipFSPermissionsTest.java | 3 +- .../jdk/nio/zipfs/ZipFSPermissionsTest.policy | 5 - test/jdk/jdk/nio/zipfs/ZipFSTester.java | 4 +- test/jdk/jdk/nio/zipfs/test.policy | 7 - test/jdk/jdk/nio/zipfs/test.policy.posix | 9 - test/jdk/jdk/nio/zipfs/test.policy.readonly | 6 - .../jmxremote/bootstrap/RmiBootstrapTest.java | 2 - .../www/http/HttpClient/IsKeepingAlive.java | 69 - .../www/http/HttpClient/IsKeepingAlive.policy | 37 - .../net/www/http/HttpClient/OpenServer.java | 68 - .../net/www/http/HttpClient/OpenServer.policy | 37 - ...AuthWithSM.java => BasicNTLMAuthTest.java} | 12 +- .../HttpURLConnection/NTLMAuthWithSM.policy | 7 - .../www/protocol/jrt/WithSecurityManager.java | 72 - test/jdk/sun/net/www/protocol/jrt/java.policy | 3 - test/jdk/sun/nio/cs/StrCodingBenchmark.java | 12 +- test/jdk/sun/nio/cs/StrCodingBenchmarkDB.java | 5 +- test/jdk/sun/nio/cs/TestSJIS0213_SM.java | 38 - test/jdk/sun/nio/cs/TestStringCoding.java | 88 +- test/jdk/sun/nio/cs/TestStringCodingUTF8.java | 12 +- .../ReflectionFactoryTest.java | 3 +- .../reflect/ReflectionFactory/security.policy | 11 - .../Log/checkLogging/CheckLogging.java | 20 +- .../MarshalForeignStub.java | 6 +- .../marshalForeignStub/security.policy | 14 - .../DisableMultiplexing.java | 71 - .../DisableMultiplexing_Stub.java | 38 - test/jdk/sun/security/ec/TestEC.java | 2 - test/jdk/sun/security/ec/TestEC.policy | 3 - .../security/krb5/auto/AcceptPermissions.java | 155 -- .../jdk/sun/security/krb5/auto/BasicProc.java | 48 +- .../krb5/auto/HttpNegotiateServer.java | 27 +- .../security/krb5/auto/KeyPermissions.java | 58 - .../PrincipalSystemPropTest.java | 8 +- .../principalSystemPropTest.policy | 22 - .../sun/security/mscapi/AccessKeyStore.java | 151 -- test/jdk/sun/security/mscapi/access.policy | 4 - test/jdk/sun/security/mscapi/noaccess.policy | 4 - .../security/pkcs11/Cipher/ReinitCipher.java | 3 +- .../pkcs11/Cipher/TestPKCS5PaddingError.java | 1 - .../security/pkcs11/Cipher/TestRSACipher.java | 1 - .../pkcs11/Cipher/TestRSACipherWrap.java | 1 - .../pkcs11/Cipher/TestRawRSACipher.java | 1 - .../pkcs11/Cipher/TestSymmCiphers.java | 1 - .../pkcs11/Cipher/TestSymmCiphersNoPad.java | 1 - .../KeyAgreement/IllegalPackageAccess.java | 97 - .../pkcs11/KeyAgreement/SupportedDHKeys.java | 3 +- .../security/pkcs11/KeyAgreement/TestDH.java | 1 - .../pkcs11/KeyAgreement/TestInterop.java | 1 - .../pkcs11/KeyAgreement/TestShort.java | 3 +- .../KeyAgreement/UnsupportedDHKeys.java | 3 +- .../pkcs11/KeyGenerator/DESParity.java | 3 +- .../KeyGenerator/HmacDefKeySizeTest.java | 3 +- .../pkcs11/KeyGenerator/TestKeyGenerator.java | 3 +- .../pkcs11/KeyPairGenerator/TestDH2048.java | 3 +- .../sun/security/pkcs11/KeyStore/Basic.java | 5 +- .../sun/security/pkcs11/KeyStore/Basic.policy | 23 - test/jdk/sun/security/pkcs11/Mac/MacKAT.java | 3 +- .../sun/security/pkcs11/Mac/MacSameTest.java | 3 +- .../sun/security/pkcs11/Mac/ReinitMac.java | 3 +- .../pkcs11/MessageDigest/DigestKAT.java | 3 +- .../pkcs11/MessageDigest/ReinitDigest.java | 1 - .../pkcs11/MessageDigest/TestCloning.java | 3 +- test/jdk/sun/security/pkcs11/PKCS11Test.java | 49 +- .../sun/security/pkcs11/Provider/Login.java | 7 +- .../sun/security/pkcs11/Provider/Login.policy | 14 - .../pkcs11/Provider/MultipleLogins.java | 26 +- .../pkcs11/Provider/MultipleLogins.sh | 3 - .../security/pkcs11/Secmod/AddPrivateKey.java | 12 +- .../pkcs11/Secmod/AddTrustedCert.java | 14 +- .../sun/security/pkcs11/Secmod/Crypto.java | 9 +- .../security/pkcs11/Secmod/GetPrivateKey.java | 12 +- .../pkcs11/Secmod/JksSetPrivateKey.java | 12 +- .../security/pkcs11/Secmod/LoadKeystore.java | 12 +- .../security/pkcs11/Secmod/TrustAnchors.java | 9 +- test/jdk/sun/security/pkcs11/Secmod/policy | 6 - .../security/pkcs11/SecureRandom/Basic.java | 3 +- .../pkcs11/Signature/ByteBuffers.java | 3 +- .../security/pkcs11/Signature/TestDSA.java | 3 +- .../pkcs11/Signature/TestDSAKeyLength.java | 1 - .../pkcs11/Signature/TestRSAKeyLength.java | 3 +- .../security/pkcs11/ec/ReadCertificates.java | 3 +- .../sun/security/pkcs11/ec/ReadPKCS12.java | 3 +- .../sun/security/pkcs11/ec/TestCurves.java | 3 +- test/jdk/sun/security/pkcs11/ec/TestECDH.java | 3 +- .../jdk/sun/security/pkcs11/ec/TestECDH2.java | 3 +- .../jdk/sun/security/pkcs11/ec/TestECDSA.java | 3 +- .../sun/security/pkcs11/ec/TestECDSA2.java | 3 +- .../sun/security/pkcs11/ec/TestECGenSpec.java | 3 +- .../security/pkcs11/ec/TestKeyFactory.java | 3 +- test/jdk/sun/security/pkcs11/ec/policy | 7 - test/jdk/sun/security/pkcs11/policy | 4 - test/jdk/sun/security/pkcs11/rsa/KeyWrap.java | 3 +- .../sun/security/pkcs11/rsa/TestCACerts.java | 3 +- .../security/pkcs11/rsa/TestCACerts.policy | 7 - .../security/pkcs11/rsa/TestKeyFactory.java | 3 +- .../pkcs11/rsa/TestKeyPairGenerator.java | 4 +- .../pkcs11/rsa/TestKeyPairGenerator.policy | 4 - .../rsa/TestP11KeyFactoryGetRSAKeySpec.java | 3 +- .../security/pkcs11/rsa/TestSignatures.java | 3 +- .../sun/security/pkcs11/rsa/rsakeys.ks.policy | 5 - .../pkcs11/sslecc/ClientJSSEServerJSSE.java | 4 +- test/jdk/sun/security/pkcs11/sslecc/policy | 9 - .../security/pkcs11/tls/TestKeyMaterial.java | 3 +- .../pkcs11/tls/TestLeadingZeroesP11.java | 3 +- .../security/pkcs11/tls/TestMasterSecret.java | 3 +- .../pkcs11/tls/TestMasterSecret.policy | 8 - test/jdk/sun/security/pkcs11/tls/TestPRF.java | 3 +- .../security/pkcs11/tls/TestPremaster.java | 3 +- test/jdk/sun/security/pkcs11/tls/policy | 5 - .../security/provider/PolicyFile/Alias.java | 73 - .../security/provider/PolicyFile/Alias.policy | 12 - .../provider/PolicyFile/AliasExpansion.java | 64 - .../provider/PolicyFile/AliasExpansion.policy | 22 - .../provider/PolicyFile/BadPolicyFile.java | 51 - .../provider/PolicyFile/BadPolicyFile.policy | 4 - .../provider/PolicyFile/CombinedPerms.java | 55 - .../provider/PolicyFile/CombinedPerms.policy | 3 - .../provider/PolicyFile/DefaultPolicy.java | 78 - .../provider/PolicyFile/EmailAddress.java | 92 - .../provider/PolicyFile/EmailAddress.policy | 9 - .../security/provider/PolicyFile/Extra.policy | 3 - .../GrantAllPermToExtWhenNoPolicy.java | 52 - .../GrantAllPermToExtWhenNoPolicy.sh | 98 - .../security/provider/PolicyFile/Modules.java | 123 - .../provider/PolicyFile/SelfExpansion.java | 59 - .../provider/PolicyFile/SelfExpansion.policy | 10 - .../provider/PolicyFile/SelfWildcard.java | 78 - .../provider/PolicyFile/SelfWildcard.policy | 23 - .../PolicyFile/SomeExtensionClass.java | 38 - .../PolicyFile/TokenStore.RelPassPolicy | 6 - .../provider/PolicyFile/TokenStore.java | 262 -- .../provider/PolicyFile/TokenStore.keystore | Bin 2202 -> 0 bytes .../provider/PolicyFile/TokenStore.pwd | 1 - .../provider/PolicyFile/TrustedCert.java | 57 - .../provider/PolicyFile/TrustedCert.keystore | Bin 1063 -> 0 bytes .../provider/PolicyFile/TrustedCert.keystore1 | Bin 656 -> 0 bytes .../provider/PolicyFile/TrustedCert.policy | 11 - .../security/provider/PolicyFile/Utf8.java | 36 - .../security/provider/PolicyFile/Utf8.policy | 3 - .../PolicyFile/WildcardPrincipalName.java | 94 - .../PolicyFile/getinstance/GetInstance.java | 40 - .../PolicyFile/getinstance/GetInstance.policy | 6 - .../getinstance/NoArgPermission.java | 26 - .../getinstance/OneArgPermission.java | 26 - .../TwoArgNullActionsPermission.java | 28 - .../getinstance/TwoArgPermission.java | 26 - .../PolicyFile/getinstance/getinstance.sh | 136 - .../provider/PolicyFile/modules.policy | 61 - .../provider/PolicyFile/wildcard.policy | 7 - .../AvoidPropertyExpansionExceptions.java | 46 - .../provider/PolicyParser/BogusGrants.java | 59 - .../provider/PolicyParser/EncodeURL.java | 110 - .../ExpansionErrorMisleading.java | 54 - .../ExpansionErrorMisleading.policy | 3 - .../provider/PolicyParser/ExtDirs.java | 52 - .../provider/PolicyParser/ExtDirs.policy | 4 - .../provider/PolicyParser/ExtDirs1.policy | 4 - .../provider/PolicyParser/ExtDirs2.policy | 4 - .../provider/PolicyParser/ExtDirs3.policy | 4 - .../provider/PolicyParser/ExtDirsA.java | 37 - .../provider/PolicyParser/ExtDirsA/a.jar | Bin 1432 -> 0 bytes .../provider/PolicyParser/ExtDirsB.java | 28 - .../provider/PolicyParser/ExtDirsB/b.jar | Bin 854 -> 0 bytes .../provider/PolicyParser/ExtDirsChange.java | 110 - .../PolicyParser/ExtDirsChange.policy | 17 - .../PolicyParser/ExtDirsDefaultPolicy.java | 71 - .../PolicyParser/PrincipalExpansionError.java | 126 - .../PrincipalExpansionError.policy | 21 - .../PolicyParser/PrincipalExpansionError.sh | 25 - .../PrincipalExpansionErrorAction.java | 35 - .../provider/PolicyParser/TokenStore.java | 247 -- .../PolicyParser/UnresolvedProperty.policy | 10 - .../provider/PolicyParser/p001.policy | 3 - .../provider/PolicyParser/p002.policy | 4 - .../provider/PolicyParser/p003.policy | 4 - .../provider/PolicyParser/p004.policy | 3 - .../SecureRandom/StrongSecureRandom.java | 1 - .../sun/security/smartcardio/TestChannel.java | 3 +- .../sun/security/smartcardio/TestControl.java | 3 +- .../sun/security/smartcardio/TestDefault.java | 3 +- .../sun/security/smartcardio/TestDirect.java | 3 +- test/jdk/sun/security/smartcardio/test.policy | 3 - .../SSLSocketImpl/NotifyHandshakeTest.java | 192 -- .../SSLSocketImpl/NotifyHandshakeTest.policy | 37 - .../ssl/SSLSocketImpl/NotifyHandshakeTest.sh | 99 - .../NotifyHandshakeTestHeyYou.java | 62 - .../multiRelease/MVJarSigningTest.java | 7 +- .../jarsigner/multiRelease/SignedJar.policy | 10 - .../security/util/DerInputBuffer/Allow.policy | 11 - .../util/FilePermCompat/CompatImpact.java | 277 -- .../security/util/FilePermCompat/Flag.java | 125 - .../security/util/FilePermCompat/flag.policy | 4 - .../sun/security/util/Resources/Format.java | 12 +- .../sun/security/util/Resources/Format.policy | 3 - .../sun/security/util/Resources/Usages.java | 9 +- .../customSysClassLoader/BootMessages.java | 4 +- .../customSysClassLoader/error.policy | 5 - .../util/Resources/early/EarlyResources.java | 58 - .../util/Resources/early/malformed.policy | 3 - .../security/x509/AVA/AVAEqualsHashCode.java | 3 +- test/jdk/sun/security/x509/AVA/Allow.policy | 11 - .../sun/util/locale/provider/Bug8152817.java | 55 - .../tools/jlink/JLinkToolProviderTest.java | 16 +- test/jdk/tools/jlink/SecurityTest.java | 47 - test/jdk/tools/jlink/toolprovider.policy | 2 - .../launcher/MainClassCantBeLoadedTest.java | 64 +- test/jdk/tools/launcher/MiscTests.java | 44 +- .../modules/basic/LauncherErrors.java | 20 +- .../basic/src/test2/jdk/test2/Main.java | 10 +- .../langtools/tools/doclint/tool/RunTest.java | 57 +- .../api/ToolProvider/ToolProviderTest.java | 62 - .../javac/launcher/SourceLauncherTest.java | 19 - .../javac/processing/6348193/T6348193.java | 51 +- test/lib/jdk/test/lib/process/Proc.java | 74 +- test/lib/jdk/test/whitebox/WhiteBox.java | 5 - .../reflect/ClazzWithSecurityManager.java | 60 - .../java/security/ProtectionDomainBench.java | 8 +- 1885 files changed, 5494 insertions(+), 65616 deletions(-) delete mode 100644 src/java.base/share/classes/sun/security/provider/PolicyFile.java delete mode 100644 src/java.base/share/classes/sun/security/provider/PolicySpiFile.java delete mode 100644 src/java.base/share/conf/security/java.policy delete mode 100644 src/java.base/share/lib/security/default.policy delete mode 100644 src/java.base/share/native/libjava/AccessController.c delete mode 100644 src/java.base/share/native/libjava/SecurityManager.c delete mode 100644 src/java.base/windows/lib/security/default.policy delete mode 100644 test/hotspot/jtreg/compiler/exceptions/ExceptionInInit.java delete mode 100644 test/hotspot/jtreg/compiler/jvmci/SecurityRestrictionsTest.java delete mode 100644 test/hotspot/jtreg/runtime/Nestmates/protectionDomain/Host.java delete mode 100644 test/hotspot/jtreg/runtime/Nestmates/protectionDomain/TestDifferentProtectionDomains.java delete mode 100644 test/hotspot/jtreg/vmTestbase/nsk/jvmti/AttachOnDemand/attach004/TestDescription.java delete mode 100644 test/hotspot/jtreg/vmTestbase/nsk/jvmti/AttachOnDemand/attach004/attach004.policy delete mode 100644 test/hotspot/jtreg/vmTestbase/nsk/jvmti/AttachOnDemand/attach004/attach004Agent00.java delete mode 100644 test/hotspot/jtreg/vmTestbase/nsk/jvmti/AttachOnDemand/attach004/attach004Agent00.mf delete mode 100644 test/jaxp/javax/xml/jaxp/libs/jaxp/library/BasePolicy.java delete mode 100644 test/jaxp/javax/xml/jaxp/libs/jaxp/library/FilePolicy.java delete mode 100644 test/jaxp/javax/xml/jaxp/libs/jaxp/library/InternalAPIPolicy.java delete mode 100644 test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPPolicyManager.java delete mode 100644 test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPTestPolicy.java delete mode 100644 test/jaxp/javax/xml/jaxp/libs/jaxp/library/NetAccessPolicy.java delete mode 100644 test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogAccessTest.java delete mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/Bug7143711Test.java delete mode 100644 test/jdk/com/sun/crypto/provider/Cipher/AES/TestAESCiphers/testAES.policy delete mode 100644 test/jdk/com/sun/java/accessibility/util/8051626/Bug8051626.java delete mode 100644 test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanPermissionsTest.java rename test/jdk/com/sun/net/httpserver/simpleserver/{SecurityManagerTest.java => RootDirPermissionsTest.java} (56%) delete mode 100644 test/jdk/com/sun/net/httpserver/simpleserver/SecurityManagerTestNoRead.policy delete mode 100644 test/jdk/com/sun/net/httpserver/simpleserver/SecurityManagerTestRead.policy delete mode 100644 test/jdk/com/sun/security/auth/login/ConfigFile/InnerClassConfig.policy delete mode 100644 test/jdk/com/sun/security/auth/login/ConfigFile/PropertyExpansion.policy delete mode 100644 test/jdk/com/sun/security/auth/module/LdapLoginModule/CheckConfigs.policy delete mode 100644 test/jdk/com/sun/security/auth/uphc.policy delete mode 100644 test/jdk/com/sun/tools/attach/PermissionTest.java delete mode 100644 test/jdk/com/sun/tools/attach/java.policy.allow delete mode 100644 test/jdk/com/sun/tools/attach/java.policy.deny delete mode 100644 test/jdk/java/awt/Debug/DumpOnKey/dump.policy delete mode 100644 test/jdk/java/awt/Desktop/DefaultPermissions/DefaultPermissions.java delete mode 100644 test/jdk/java/awt/Desktop/DefaultPermissions/java.policy delete mode 100644 test/jdk/java/awt/Desktop/SecurityTest/DesktopSecurityTest.java delete mode 100644 test/jdk/java/awt/Desktop/SecurityTest/desktop.policy delete mode 100644 test/jdk/java/awt/Dialog/ModalDialogPermission/ModalDialogPermission.java delete mode 100644 test/jdk/java/awt/Dialog/ModalDialogPermission/java.policy delete mode 100644 test/jdk/java/awt/EventDispatchThread/PropertyPermissionOnEDT/PropertyPermissionOnEDT.java delete mode 100644 test/jdk/java/awt/EventDispatchThread/PropertyPermissionOnEDT/java.policy delete mode 100644 test/jdk/java/awt/Focus/CloseDialogActivateOwnerTest/CloseDialogActivateOwnerTest.java delete mode 100644 test/jdk/java/awt/Focus/CloseDialogActivateOwnerTest/java.policy delete mode 100644 test/jdk/java/awt/FontClass/CreateFont/BigFont.java delete mode 100644 test/jdk/java/awt/FontClass/CreateFont/fileaccess/FontFile.java delete mode 100644 test/jdk/java/awt/FontClass/CreateFont/fileaccess/TestFontFile.sh delete mode 100644 test/jdk/java/awt/FontClass/FontAccess.java delete mode 100644 test/jdk/java/awt/FontClass/FontPrivilege.java delete mode 100644 test/jdk/java/awt/PrintJob/Security/SecurityDialogTest.java delete mode 100644 test/jdk/java/awt/PrintJob/Security/policy delete mode 100644 test/jdk/java/awt/Toolkit/SecurityTest/SecurityTest2.java delete mode 100644 test/jdk/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy delete mode 100644 test/jdk/java/awt/TrayIcon/SecurityCheck/NoPermissionTest/NoPermissionTest.java delete mode 100644 test/jdk/java/awt/TrayIcon/SecurityCheck/NoPermissionTest/tray.policy delete mode 100644 test/jdk/java/awt/TrayIcon/SecurityCheck/PermissionTest/PermissionTest.java delete mode 100644 test/jdk/java/awt/TrayIcon/SecurityCheck/PermissionTest/tray.policy delete mode 100644 test/jdk/java/awt/color/LoadProfileWithSM.java delete mode 100644 test/jdk/java/awt/color/StandardProfileTest.java delete mode 100644 test/jdk/java/awt/color/StandardProfileTest.policy delete mode 100644 test/jdk/java/awt/dnd/DragInterceptorAppletTest/DragInterceptorAppletTest.java delete mode 100644 test/jdk/java/awt/dnd/DragInterceptorAppletTest/DragInterceptorFrame.java delete mode 100644 test/jdk/java/awt/dnd/DragInterceptorAppletTest/InterprocessMessages.java delete mode 100644 test/jdk/java/awt/dnd/DragInterceptorAppletTest/SourceFrame.java delete mode 100644 test/jdk/java/awt/event/KeyEvent/RegisterKeyStroke/policy delete mode 100644 test/jdk/java/awt/image/mlib/mlib.security.policy delete mode 100644 test/jdk/java/awt/print/PrintServicesSecurityManager.java delete mode 100644 test/jdk/java/awt/print/PrinterJob/CheckAccess.java delete mode 100644 test/jdk/java/awt/print/PrinterJob/GetUserNameTest.java delete mode 100644 test/jdk/java/awt/print/PrinterJob/GetUserNameTest.policy delete mode 100644 test/jdk/java/awt/security/Permissions.java delete mode 100644 test/jdk/java/awt/security/WarningWindowDisposeTest/WarningWindowDisposeCrashTest.java delete mode 100644 test/jdk/java/awt/security/WarningWindowDisposeTest/WarningWindowDisposeTest.java delete mode 100644 test/jdk/java/awt/security/WarningWindowDisposeTest/policy delete mode 100644 test/jdk/java/beans/Beans/Test4080522.java delete mode 100644 test/jdk/java/beans/EventHandler/Test6277246.java delete mode 100644 test/jdk/java/beans/EventHandler/Test6277266.java delete mode 100644 test/jdk/java/beans/Introspector/Test6277246.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestBooleanClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestBooleanType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestByteClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestByteType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestColorClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestDoubleClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestDoubleType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestEnumClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestEnumSubclass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestFloatClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestFloatType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestFontClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestIntegerClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestIntegerType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestLongClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestLongType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestShortClass.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestShortType.java delete mode 100644 test/jdk/java/beans/PropertyEditor/TestStringClass.java delete mode 100644 test/jdk/java/beans/SimpleBeanInfo/LoadingStandardIcons/java.policy delete mode 100644 test/jdk/java/beans/Statement/ClassForName/java.policy delete mode 100644 test/jdk/java/beans/Statement/Test6224433.java delete mode 100644 test/jdk/java/beans/XMLEncoder/4741757/TestSecurityManager.java delete mode 100644 test/jdk/java/foreign/security.policy delete mode 100644 test/jdk/java/io/Console/SecurityManagerTest.java delete mode 100644 test/jdk/java/io/Console/test.policy delete mode 100644 test/jdk/java/io/File/CheckPermission.java delete mode 100644 test/jdk/java/io/File/createTempFile/SecurityTest.java delete mode 100644 test/jdk/java/io/File/createTempFile/java.policy delete mode 100644 test/jdk/java/io/FilePermission/MergeName.java delete mode 100644 test/jdk/java/io/FilePermission/ReadFileOnPath.java delete mode 100644 test/jdk/java/io/FilePermission/m/module-info.java delete mode 100644 test/jdk/java/io/FilePermission/m/p/App.java delete mode 100644 test/jdk/java/io/Serializable/records/ConstructorPermissionTest.java delete mode 100644 test/jdk/java/io/Serializable/records/empty_security.policy delete mode 100644 test/jdk/java/io/Serializable/serialFilter/FilterWithSecurityManagerTest.java delete mode 100644 test/jdk/java/io/Serializable/serialFilter/security.policy delete mode 100644 test/jdk/java/io/Serializable/serialFilter/security.policy.without.globalFilter delete mode 100644 test/jdk/java/io/Serializable/subclass/Allow.policy delete mode 100644 test/jdk/java/io/Serializable/subclassGC/security.policy delete mode 100644 test/jdk/java/lang/Class/forName/modules/policy delete mode 100644 test/jdk/java/lang/Class/forName/modules/policy.denied delete mode 100644 test/jdk/java/lang/Class/forName/modules/src/m3/module-info.java delete mode 100644 test/jdk/java/lang/Class/forName/modules/src/m3/p3/NoAccess.java delete mode 100644 test/jdk/java/lang/Class/forName/modules/src/m3/p3/NoGetClassLoaderAccess.java delete mode 100644 test/jdk/java/lang/Class/forName/modules/src/m3/p3/internal/Foo.java delete mode 100644 test/jdk/java/lang/Class/getEnclosingConstructor/EnclosingConstructorWithSecurityManager.java delete mode 100644 test/jdk/java/lang/Class/getEnclosingMethod/EnclosingMethodWithSecurityManager.java delete mode 100644 test/jdk/java/lang/ClassLoader/UninitializedParent.java delete mode 100644 test/jdk/java/lang/ClassLoader/forNameLeak/test.policy delete mode 100644 test/jdk/java/lang/ClassLoader/securityManager/ClassLoaderTest.java delete mode 100644 test/jdk/java/lang/ClassLoader/securityManager/TestClassLoader.java delete mode 100644 test/jdk/java/lang/ClassLoader/securityManager/TestClient.java delete mode 100644 test/jdk/java/lang/ClassLoader/securityManager/malformed.policy delete mode 100644 test/jdk/java/lang/ClassLoader/securityManager/valid.policy delete mode 100644 test/jdk/java/lang/ModuleTests/WithSecurityManager.java delete mode 100644 test/jdk/java/lang/ModuleTests/allow.policy delete mode 100644 test/jdk/java/lang/ProcessBuilder/SecurityManagerClinit.java delete mode 100644 test/jdk/java/lang/ProcessHandle/PermissionTest.java create mode 100644 test/jdk/java/lang/SecurityManager/Basic.java delete mode 100644 test/jdk/java/lang/SecurityManager/CheckAccessClassInPackagePermissions.java delete mode 100644 test/jdk/java/lang/SecurityManager/CheckPackageAccess.java delete mode 100644 test/jdk/java/lang/SecurityManager/CheckPackageMatching.java delete mode 100644 test/jdk/java/lang/SecurityManager/PackageAccessTest.java delete mode 100644 test/jdk/java/lang/SecurityManager/empty.policy delete mode 100644 test/jdk/java/lang/SecurityManager/modules/CustomSecurityManagerTest.java delete mode 100644 test/jdk/java/lang/SecurityManager/modules/m/module-info.java delete mode 100644 test/jdk/java/lang/SecurityManager/modules/m/p/CustomSecurityManager.java delete mode 100644 test/jdk/java/lang/SecurityManager/modules/test.policy delete mode 100644 test/jdk/java/lang/SecurityManager/test.policy delete mode 100644 test/jdk/java/lang/StackWalker/SecurityExceptions.java delete mode 100644 test/jdk/java/lang/StackWalker/noperms.policy delete mode 100644 test/jdk/java/lang/StackWalker/stackwalk.policy delete mode 100644 test/jdk/java/lang/StackWalker/stackwalktest.policy delete mode 100644 test/jdk/java/lang/String/concat/WithSecurityManager.java delete mode 100644 test/jdk/java/lang/System/IgnoreNullSecurityManager.java delete mode 100644 test/jdk/java/lang/System/SecurityRace.java delete mode 100644 test/jdk/java/lang/System/System.policy delete mode 100644 test/jdk/java/lang/annotation/repeatingAnnotations/CustomRepeatingWithSecurityManager.java delete mode 100644 test/jdk/java/lang/annotation/repeatingAnnotations/RepeatingWithSecurityManager.java delete mode 100644 test/jdk/java/lang/constant/methodTypeDesc/ResolveConstantDesc.java delete mode 100644 test/jdk/java/lang/constant/methodTypeDesc/jdk.unsupported/sun/misc/Test.java delete mode 100644 test/jdk/java/lang/constant/methodTypeDesc/test.policy delete mode 100644 test/jdk/java/lang/invoke/7196190/GetUnsafeTest.java delete mode 100644 test/jdk/java/lang/invoke/7196190/jtreg.security.policy delete mode 100644 test/jdk/java/lang/invoke/8076596/Test8076596.java delete mode 100644 test/jdk/java/lang/invoke/8076596/Test8076596.security.policy delete mode 100644 test/jdk/java/lang/invoke/FindClassSecurityManager.java delete mode 100644 test/jdk/java/lang/invoke/MethodHandleProxies/WithSecurityManagerTest.java delete mode 100644 test/jdk/java/lang/invoke/MethodHandleProxies/jtreg.security.policy delete mode 100644 test/jdk/java/lang/invoke/MethodTypeSecurityManager.java delete mode 100644 test/jdk/java/lang/invoke/TestPrivateMember.java delete mode 100644 test/jdk/java/lang/invoke/getclassloader.policy delete mode 100644 test/jdk/java/lang/invoke/jtreg.security.policy delete mode 100644 test/jdk/java/lang/invoke/lambda/LambdaAccessControlTest.java delete mode 100644 test/jdk/java/lang/module/ModuleFinderWithSecurityManager.java delete mode 100644 test/jdk/java/lang/module/java.policy delete mode 100644 test/jdk/java/lang/reflect/Nestmates/TestSecurityManagerChecks.java delete mode 100644 test/jdk/java/lang/reflect/Nestmates/empty.policy delete mode 100644 test/jdk/java/lang/reflect/Nestmates/testPkg/Host.java delete mode 100644 test/jdk/java/lang/reflect/Nestmates/testPkg/Singleton.java delete mode 100644 test/jdk/java/lang/reflect/records/RecordPermissionsTest.java delete mode 100644 test/jdk/java/lang/reflect/records/allPermissions.policy delete mode 100644 test/jdk/java/lang/reflect/sealed_classes/TestSecurityManagerChecks.java delete mode 100644 test/jdk/java/lang/runtime/empty.policy delete mode 100644 test/jdk/java/net/DatagramSocket/TimeoutWithSM.java delete mode 100644 test/jdk/java/net/IPSupport/MinimumPermissions.java delete mode 100644 test/jdk/java/net/IPSupport/MinimumPermissions.policy delete mode 100644 test/jdk/java/net/InetAddress/GetLocalHostWithSM.java delete mode 100644 test/jdk/java/net/ServerSocket/TestLocalAddress.java delete mode 100644 test/jdk/java/net/SetFactoryPermission/SetFactoryPermission.java delete mode 100644 test/jdk/java/net/SocketOption/options.policy delete mode 100644 test/jdk/java/net/SocketPermission/SocketPermissionTest.java delete mode 100644 test/jdk/java/net/URLClassLoader/getresourceasstream/policy delete mode 100644 test/jdk/java/net/httpclient/AsFileDownloadTest.policy delete mode 100644 test/jdk/java/net/httpclient/FilePublisher/FilePublisherPermsTest1.policy delete mode 100644 test/jdk/java/net/httpclient/FilePublisher/FilePublisherPermsTest2.policy delete mode 100644 test/jdk/java/net/httpclient/FilePublisher/FilePublisherPermsTest3.policy delete mode 100644 test/jdk/java/net/httpclient/FilePublisher/FilePublisherTest.policy delete mode 100644 test/jdk/java/net/httpclient/PathSubscriber/ofFile.policy delete mode 100644 test/jdk/java/net/httpclient/PathSubscriber/ofFileDownload.policy delete mode 100644 test/jdk/java/net/httpclient/RequestBodyTest.policy delete mode 100644 test/jdk/java/net/httpclient/dependent.policy delete mode 100644 test/jdk/java/net/httpclient/httpclient-localaddr-security.policy delete mode 100644 test/jdk/java/net/httpclient/noPermissions.policy delete mode 100644 test/jdk/java/net/httpclient/security/0.policy delete mode 100644 test/jdk/java/net/httpclient/security/1.policy delete mode 100644 test/jdk/java/net/httpclient/security/10.policy delete mode 100644 test/jdk/java/net/httpclient/security/11.policy delete mode 100644 test/jdk/java/net/httpclient/security/12.policy delete mode 100644 test/jdk/java/net/httpclient/security/14.policy delete mode 100644 test/jdk/java/net/httpclient/security/15.policy delete mode 100644 test/jdk/java/net/httpclient/security/16.policy delete mode 100644 test/jdk/java/net/httpclient/security/17.policy delete mode 100644 test/jdk/java/net/httpclient/security/2.policy delete mode 100644 test/jdk/java/net/httpclient/security/3.policy delete mode 100644 test/jdk/java/net/httpclient/security/4.policy delete mode 100644 test/jdk/java/net/httpclient/security/5.policy delete mode 100644 test/jdk/java/net/httpclient/security/6.policy delete mode 100644 test/jdk/java/net/httpclient/security/7.policy delete mode 100644 test/jdk/java/net/httpclient/security/8.policy delete mode 100644 test/jdk/java/net/httpclient/security/9.policy delete mode 100644 test/jdk/java/net/httpclient/security/Driver.java delete mode 100644 test/jdk/java/net/httpclient/security/Security.java delete mode 100644 test/jdk/java/net/httpclient/security/filePerms/allpermissions.policy delete mode 100644 test/jdk/java/net/httpclient/security/filePerms/nopermissions.policy create mode 100644 test/jdk/java/net/httpclient/websocket/security/WSSanityTest.java delete mode 100644 test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java delete mode 100644 test/jdk/java/net/httpclient/websocket/security/httpclient.policy delete mode 100644 test/jdk/java/net/spi/InetAddressResolverProvider/ResolvePermissionTest.java delete mode 100644 test/jdk/java/net/spi/InetAddressResolverProvider/RuntimePermissionTest.java delete mode 100644 test/jdk/java/net/spi/URLStreamHandlerProvider/basic.policy delete mode 100644 test/jdk/java/nio/channels/AsynchronousChannelGroup/bootlib/Attack.java delete mode 100644 test/jdk/java/nio/channels/AsynchronousChannelGroup/bootlib/PrivilegedThreadFactory.java delete mode 100644 test/jdk/java/nio/channels/AsynchronousServerSocketChannel/WithSecurityManager.java delete mode 100644 test/jdk/java/nio/channels/AsynchronousServerSocketChannel/java.policy.allow delete mode 100644 test/jdk/java/nio/channels/AsynchronousServerSocketChannel/java.policy.deny delete mode 100644 test/jdk/java/nio/channels/FileChannel/MapWithSecurityManager.java delete mode 100644 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/java.policy.fail delete mode 100644 test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/java.policy.pass delete mode 100644 test/jdk/java/nio/channels/unixdomain/Security.java delete mode 100644 test/jdk/java/nio/channels/unixdomain/policy1 delete mode 100644 test/jdk/java/nio/channels/unixdomain/policy2 delete mode 100644 test/jdk/java/nio/channels/unixdomain/policy3 delete mode 100644 test/jdk/java/nio/charset/spi/charsetProvider.sp delete mode 100644 test/jdk/java/nio/charset/spi/default-pol delete mode 100644 test/jdk/java/nio/file/Files/CheckPermissions.java delete mode 100644 test/jdk/java/nio/file/Files/copy.policy delete mode 100644 test/jdk/java/nio/file/Files/walkFileTree/WalkWithSecurity.java delete mode 100644 test/jdk/java/nio/file/Files/walkFileTree/denyAll.policy delete mode 100644 test/jdk/java/nio/file/Files/walkFileTree/grantAll.policy delete mode 100644 test/jdk/java/nio/file/Files/walkFileTree/grantTopOnly.policy delete mode 100644 test/jdk/java/nio/file/Path/MacToRealPath.policy delete mode 100644 test/jdk/java/nio/file/Path/MacToRealPathWithSM.java delete mode 100644 test/jdk/java/nio/file/WatchService/WithSecurityManager.java delete mode 100644 test/jdk/java/nio/file/WatchService/denyAll.policy delete mode 100644 test/jdk/java/nio/file/WatchService/grantDirAndOneLevel.policy delete mode 100644 test/jdk/java/nio/file/WatchService/grantDirAndTree.policy delete mode 100644 test/jdk/java/nio/file/WatchService/grantDirOnly.policy delete mode 100644 test/jdk/java/nio/file/spi/fs.policy delete mode 100644 test/jdk/java/rmi/RMISecurityManager/checkPackageAccess/CheckPackageAccess.java delete mode 100644 test/jdk/java/rmi/dgc/VMID/security.policy delete mode 100644 test/jdk/java/rmi/dgc/dgcImplInsulation/DGCImplInsulation.java delete mode 100644 test/jdk/java/rmi/dgc/dgcImplInsulation/DGCImplInsulation_Stub.java delete mode 100644 test/jdk/java/rmi/dgc/dgcImplInsulation/security.policy delete mode 100644 test/jdk/java/rmi/registry/altSecurityManager/AltSecurityManager.java delete mode 100644 test/jdk/java/rmi/registry/altSecurityManager/TestSecurityManager.java delete mode 100644 test/jdk/java/rmi/registry/altSecurityManager/registry.security.policy delete mode 100644 test/jdk/java/rmi/registry/altSecurityManager/rmid.security.policy delete mode 100644 test/jdk/java/rmi/registry/classPathCodebase/registry.security.policy delete mode 100644 test/jdk/java/rmi/registry/classPathCodebase/security.policy delete mode 100644 test/jdk/java/rmi/registry/readTest/registry.security.policy delete mode 100644 test/jdk/java/rmi/registry/serialFilter/security.policy delete mode 100644 test/jdk/java/rmi/reliability/benchmark/bench/rmi/policy.all delete mode 100644 test/jdk/java/rmi/reliability/juicer/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/delegateBeforePermissionCheck/DelegateBeforePermissionCheck.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/delegateBeforePermissionCheck/Foo.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/delegateToContextLoader/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/downloadArrayClass/DownloadArrayClass.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/downloadArrayClass/DownloadArrayClass_Stub.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/downloadArrayClass/Foo.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/downloadArrayClass/Receiver.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/downloadArrayClass/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/getClassLoader/Foo.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/getClassLoader/GetClassLoader.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/getClassLoader/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/loadProxyClasses/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/spi/ContextInsulation.java delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/spi/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/useCodebaseOnly/security.policy delete mode 100644 test/jdk/java/rmi/server/RMIClassLoader/useGetURLs/security.policy delete mode 100644 test/jdk/java/rmi/server/RMISocketFactory/useSocketFactory/registry/security.policy delete mode 100644 test/jdk/java/rmi/server/RMISocketFactory/useSocketFactory/unicast/security.policy delete mode 100644 test/jdk/java/rmi/server/RemoteObject/toStub/security.policy delete mode 100644 test/jdk/java/rmi/server/RemoteServer/setLogPermission/SetLogPermission.java delete mode 100644 test/jdk/java/rmi/server/RemoteServer/setLogPermission/security.policy delete mode 100644 test/jdk/java/rmi/server/UnicastRemoteObject/useDynamicProxies/security.policy delete mode 100644 test/jdk/java/rmi/server/clientStackTrace/security.policy delete mode 100644 test/jdk/java/rmi/server/useCustomRef/security.policy create mode 100644 test/jdk/java/rmi/testlibrary/TestLoaderHandler.java delete mode 100644 test/jdk/java/rmi/transport/dgcDeadLock/registry.security.policy delete mode 100644 test/jdk/java/rmi/transport/dgcDeadLock/security.policy delete mode 100644 test/jdk/java/rmi/transport/readTimeout/security.policy delete mode 100644 test/jdk/java/security/AccessControlContext/CheckNullPermission.java delete mode 100644 test/jdk/java/security/AccessController/DoPriv.java delete mode 100644 test/jdk/java/security/AccessController/DoPrivAccomplice.java delete mode 100644 test/jdk/java/security/AccessController/DoPrivAccompliceTest.java delete mode 100644 test/jdk/java/security/AccessController/DoPrivTest.java delete mode 100644 test/jdk/java/security/AccessController/LimitedDoPrivileged.java delete mode 100644 test/jdk/java/security/AccessController/LimitedDoPrivilegedWithNullPerms.java delete mode 100644 test/jdk/java/security/AccessController/LimitedDoPrivilegedWithThread.java delete mode 100644 test/jdk/java/security/AccessController/policy delete mode 100644 test/jdk/java/security/KeyRep/Serial.policy delete mode 100644 test/jdk/java/security/KeyRep/SerialDSAPubKey.policy delete mode 100644 test/jdk/java/security/KeyRep/SerialOld.policy delete mode 100644 test/jdk/java/security/Policy/Dynamic/DynamicPolicy.java delete mode 100644 test/jdk/java/security/Policy/Dynamic/TestDynamicPolicy.java delete mode 100644 test/jdk/java/security/Policy/Dynamic/setpolicy.jp delete mode 100644 test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyTest.java delete mode 100644 test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyTest1.policy delete mode 100644 test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyTest2.policy delete mode 100644 test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyTest3.policy delete mode 100644 test/jdk/java/security/Policy/ExtensiblePolicy/ExtensiblePolicyWithJarTest.java delete mode 100644 test/jdk/java/security/Policy/ExtensiblePolicy/TVJar/TVPermission.java delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstance.java delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstance.policy delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstance.policyURL delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstancePolicySpi.java delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstanceProvider.java delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstanceSecurity.java delete mode 100644 test/jdk/java/security/Policy/GetInstance/GetInstanceSecurity.policy delete mode 100644 test/jdk/java/security/Policy/GetPermissions/JarURL.java delete mode 100644 test/jdk/java/security/Policy/GetPermissions/JarURL.policy delete mode 100644 test/jdk/java/security/Policy/PolicyProvider/CustomPolicy.java delete mode 100644 test/jdk/java/security/Policy/PolicyProvider/UseSystemClassLoader.java delete mode 100644 test/jdk/java/security/Policy/PolicyProvider/test.policy delete mode 100644 test/jdk/java/security/Policy/Root/Root.java delete mode 100644 test/jdk/java/security/Policy/Root/Root.policy delete mode 100644 test/jdk/java/security/Policy/SignedJar/SignedJarTest.java delete mode 100644 test/jdk/java/security/Policy/SignedJar/SignedJarTest_1.policy delete mode 100644 test/jdk/java/security/Policy/SignedJar/SignedJarTest_2.policy delete mode 100644 test/jdk/java/security/Policy/SignedJar/java.security delete mode 100644 test/jdk/java/security/Policy/SignedJar/keypass delete mode 100644 test/jdk/java/security/ProtectionDomain/AllPerm.jar delete mode 100644 test/jdk/java/security/ProtectionDomain/AllPerm.java delete mode 100644 test/jdk/java/security/ProtectionDomain/NullGetActions.java delete mode 100644 test/jdk/java/security/ProtectionDomain/NullGetActions.policy delete mode 100644 test/jdk/java/security/ProtectionDomain/PreserveCombinerTest.java delete mode 100644 test/jdk/java/security/ProtectionDomain/Recursion.java delete mode 100644 test/jdk/java/security/ProtectionDomain/Recursion.policy delete mode 100644 test/jdk/java/security/ProtectionDomain/RecursionDebug.java delete mode 100644 test/jdk/java/security/SecureClassLoader/DefineClass.java delete mode 100644 test/jdk/java/security/SecureClassLoader/DefineClass.policy delete mode 100644 test/jdk/java/security/Security/AddProvider.java delete mode 100644 test/jdk/java/security/Security/AddProvider.policy.1 delete mode 100644 test/jdk/java/security/Security/AddProvider.policy.2 delete mode 100644 test/jdk/java/security/Security/AddProvider.policy.3 delete mode 100644 test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.policy delete mode 100644 test/jdk/java/security/Security/removing/RemoveStaticProvider.policy delete mode 100644 test/jdk/java/security/UnresolvedPermission/Debug.java delete mode 100644 test/jdk/java/security/UnresolvedPermission/Debug.policy delete mode 100644 test/jdk/java/security/UnresolvedPermission/DebugPermission0.java delete mode 100644 test/jdk/java/security/UnresolvedPermission/DebugPermission1.java delete mode 100644 test/jdk/java/security/UnresolvedPermission/DebugPermission2.java delete mode 100644 test/jdk/java/security/UnresolvedPermission/DebugPermissionBad.java delete mode 100644 test/jdk/java/security/UnresolvedPermission/Equals.java delete mode 100644 test/jdk/java/security/UnresolvedPermission/Equals.policy delete mode 100644 test/jdk/java/security/cert/CertPathBuilder/provider.policy delete mode 100644 test/jdk/java/sql/permissionTests/DriverManagerPermissionsTests.java delete mode 100644 test/jdk/java/time/nontestng/java/time/chrono/Bug8178823.java delete mode 100644 test/jdk/java/time/nontestng/java/time/chrono/bug8178823.policy delete mode 100644 test/jdk/java/util/PluggableLocale/PermissionTest.java delete mode 100644 test/jdk/java/util/PluggableLocale/dummy.policy delete mode 100644 test/jdk/java/util/PluggableLocale/localeServiceProvider.policy delete mode 100644 test/jdk/java/util/ResourceBundle/Bug6359330.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/TestPermission.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/src/m1/module-info.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/src/m1/p1/Bundle.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/src/m1/p1/resources/MyResources.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/src/test/jdk/test/Main.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/src/test/jdk/test/resources/TestResources.java delete mode 100644 test/jdk/java/util/ResourceBundle/modules/security/src/test/module-info.java delete mode 100644 test/jdk/java/util/ServiceLoader/security/SecurityTest.java delete mode 100644 test/jdk/java/util/ServiceLoader/security/test/module-info.java delete mode 100644 test/jdk/java/util/ServiceLoader/security/test/p/Tests.java delete mode 100644 test/jdk/java/util/TimeZone/Bug6912560.java delete mode 100644 test/jdk/java/util/TimeZone/SetDefaultSecurityTest.java delete mode 100644 test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheck.java delete mode 100644 test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java delete mode 100644 test/jdk/java/util/concurrent/tck/tck.policy delete mode 100644 test/jdk/java/util/logging/AnonymousLogger/TestAnonymousLogger.java delete mode 100644 test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/test.policy delete mode 100644 test/jdk/java/util/logging/Logger/getGlobal/policy rename test/jdk/java/util/logging/{TestAppletLoggerContext.java => TestUILoggerContext.java} (76%) delete mode 100644 test/jdk/javax/accessibility/AccessibilityProvider/accessibilityProvider.sp delete mode 100644 test/jdk/javax/imageio/CachePremissionsTest/rw.policy delete mode 100644 test/jdk/javax/imageio/CachePremissionsTest/rwd.policy delete mode 100644 test/jdk/javax/imageio/CachePremissionsTest/w.policy delete mode 100644 test/jdk/javax/management/ImplementationVersion/policy delete mode 100644 test/jdk/javax/management/Introspector/AnnotationSecurityTest.java delete mode 100644 test/jdk/javax/management/Introspector/AnnotationSecurityTest.policy delete mode 100644 test/jdk/javax/management/modelmbean/SimpleModelMBean/policy delete mode 100644 test/jdk/javax/management/monitor/all.policy delete mode 100644 test/jdk/javax/management/remote/mandatory/notif/NoPermToRemoveTest.java delete mode 100644 test/jdk/javax/management/remote/mandatory/notif/policy.negative delete mode 100644 test/jdk/javax/management/remote/mandatory/notif/policy.positive delete mode 100644 test/jdk/javax/management/remote/mandatory/version/policy delete mode 100644 test/jdk/javax/management/security/java.policy.authorization delete mode 100644 test/jdk/javax/net/ssl/finalize/security.policy delete mode 100644 test/jdk/javax/security/auth/PrivateCredentialPermission/MoreThenOnePrincipals.java delete mode 100644 test/jdk/javax/security/auth/PrivateCredentialPermission/MoreThenOnePrincipals.policy delete mode 100644 test/jdk/javax/security/auth/PrivateCredentialPermission/Serial.policy delete mode 100644 test/jdk/javax/security/auth/PrivateCredentialPermission/Subset.java delete mode 100644 test/jdk/javax/security/auth/PrivateCredentialPermission/Subset.policy delete mode 100644 test/jdk/javax/security/auth/Subject/Compat.java delete mode 100644 test/jdk/javax/security/auth/Subject/FromACC.java delete mode 100644 test/jdk/javax/security/auth/Subject/Serial.policy delete mode 100644 test/jdk/javax/security/auth/Subject/Synch2.policy delete mode 100644 test/jdk/javax/security/auth/Subject/UnsupportedSV.java delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/NestedActions.java delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/Test.java delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/policy delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/policy.expect.ace delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/policy.expect.pae delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/policy.one.principal delete mode 100644 test/jdk/javax/security/auth/Subject/doAs/policy.two.principals delete mode 100644 test/jdk/javax/security/auth/SubjectDomainCombiner/Regression.policy delete mode 100644 test/jdk/javax/security/auth/login/Configuration/GetInstanceSecurity.grantedPolicy delete mode 100644 test/jdk/javax/security/auth/login/Configuration/GetInstanceSecurity.java delete mode 100644 test/jdk/javax/security/auth/login/Configuration/GetInstanceSecurity.policy delete mode 100644 test/jdk/javax/security/auth/login/LoginContext/ConfigConstructor.policy delete mode 100644 test/jdk/javax/security/auth/login/LoginContext/ConfigConstructorNoPerm.java delete mode 100644 test/jdk/javax/security/auth/login/LoginContext/ConfigConstructorNoPerm.policy delete mode 100644 test/jdk/javax/smartcardio/policy delete mode 100644 test/jdk/javax/sound/midi/MidiSystem/DefaultProperties/DefaultPropertiesNegative.java delete mode 100644 test/jdk/javax/sound/midi/MidiSystem/DefaultProperties/java.policy delete mode 100644 test/jdk/javax/sound/midi/MidiSystem/DefaultProperties/negative.policy rename test/jdk/javax/sound/midi/Soundbanks/{GetSoundBankSecurityException/GetSoundBankSecurityException.java => EmptySoundBankTest.java} (89%) delete mode 100644 test/jdk/javax/sound/midi/Soundbanks/GetSoundBankSecurityException/security.policy delete mode 100644 test/jdk/javax/sound/sampled/AudioSystem/DefaultProperties/DefaultProperties.java delete mode 100644 test/jdk/javax/sound/sampled/AudioSystem/DefaultProperties/DefaultPropertiesNegative.java delete mode 100644 test/jdk/javax/sound/sampled/AudioSystem/DefaultProperties/java.policy delete mode 100644 test/jdk/javax/sound/sampled/AudioSystem/DefaultProperties/negative.policy delete mode 100644 test/jdk/javax/sound/sampled/AudioSystem/DefaultProperties/testdata/conf/sound.properties delete mode 100644 test/jdk/javax/sql/permissionTests/SyncFactoryPermissionsTests.java delete mode 100644 test/jdk/javax/swing/JEditorPane/5076514/bug5076514.java delete mode 100644 test/jdk/javax/swing/JFileChooser/6484091/bug6484091.java delete mode 100644 test/jdk/javax/swing/JFileChooser/6570445/bug6570445.java delete mode 100644 test/jdk/javax/swing/JFileChooser/6738668/bug6738668.java delete mode 100644 test/jdk/javax/swing/JFileChooser/6738668/security.policy delete mode 100644 test/jdk/javax/swing/JFileChooser/7036025/bug7036025.java delete mode 100644 test/jdk/javax/swing/JFileChooser/7036025/security.policy delete mode 100644 test/jdk/javax/swing/JFileChooser/8062561/bug8062561.java delete mode 100644 test/jdk/javax/swing/JFileChooser/8062561/security.policy delete mode 100644 test/jdk/javax/swing/JFileChooser/8062561/security2.policy delete mode 100644 test/jdk/javax/swing/JFileChooser/ShellFolderQueries/ShellFolderQueriesSecurityManagerTest.java delete mode 100644 test/jdk/javax/swing/JFileChooser/ShellFolderQueries/shellfolderqueries.policy delete mode 100644 test/jdk/javax/swing/JPopupMenu/6675802/bug6675802.java delete mode 100644 test/jdk/javax/swing/JPopupMenu/6691503/bug6691503.java delete mode 100644 test/jdk/javax/swing/JPopupMenu/6694823/bug6694823.java delete mode 100644 test/jdk/javax/swing/plaf/synth/Test8043627.java delete mode 100644 test/jdk/javax/swing/text/rtf/bug4178276.java delete mode 100644 test/jdk/javax/xml/crypto/dsig/ErrorHandlerPermissions.java delete mode 100644 test/jdk/javax/xml/crypto/dsig/ErrorHandlerPermissions.policy rename test/jdk/javax/xml/crypto/dsig/{FileSocketPermissions.java => ResolveReferenceURIs.java} (75%) delete mode 100644 test/jdk/javax/xml/crypto/dsig/SecurityManager/XMLDSigWithSecMgr.java delete mode 100644 test/jdk/javax/xml/crypto/dsig/SecurityManager/policy delete mode 100644 test/jdk/javax/xml/crypto/dsig/TransformService/test.policy delete mode 100644 test/jdk/javax/xml/crypto/dsig/keyinfo/KeyInfo/test.policy delete mode 100644 test/jdk/javax/xml/jaxp/common/8020430/TestBase.java delete mode 100644 test/jdk/javax/xml/jaxp/parsers/8021148/TestBase.java delete mode 100644 test/jdk/javax/xml/jaxp/parsers/8022548/TestBase.java delete mode 100644 test/jdk/jdk/dynalink/UntrustedDynamicLinkerFactoryTest.java delete mode 100644 test/jdk/jdk/dynalink/trusted.security.policy delete mode 100644 test/jdk/jdk/dynalink/untrusted.security.policy delete mode 100644 test/jdk/jdk/incubator/vector/empty_security.policy delete mode 100644 test/jdk/jdk/internal/jrtfs/WithSecurityManager.java delete mode 100644 test/jdk/jdk/internal/jrtfs/java.policy delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/DriverRecordingDumper.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/TestMissingPermission.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/TestRecordingFile.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/TestRecordingStream.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/TestStreamingFile.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/TestStreamingLocal.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/local-streaming.policy delete mode 100644 test/jdk/jdk/jfr/api/consumer/security/no-permission.policy delete mode 100644 test/jdk/jdk/jfr/jmx/security/TestEnoughPermission.java delete mode 100644 test/jdk/jdk/jfr/jmx/security/TestNoControlPermission.java delete mode 100644 test/jdk/jdk/jfr/jmx/security/TestNoMonitorPermission.java delete mode 100644 test/jdk/jdk/jfr/jmx/security/TestNotificationListenerPermission.java delete mode 100644 test/jdk/jdk/jfr/jmx/security/enough.policy delete mode 100644 test/jdk/jdk/jfr/jmx/security/listener.policy delete mode 100644 test/jdk/jdk/jfr/jmx/security/nocontrol.policy delete mode 100644 test/jdk/jdk/jfr/jmx/security/nomonitor.policy delete mode 100644 test/jdk/jdk/nio/zipfs/PropertyPermissions.policy delete mode 100644 test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.policy delete mode 100644 test/jdk/jdk/nio/zipfs/test.policy delete mode 100644 test/jdk/jdk/nio/zipfs/test.policy.posix delete mode 100644 test/jdk/jdk/nio/zipfs/test.policy.readonly delete mode 100644 test/jdk/sun/net/www/http/HttpClient/IsKeepingAlive.java delete mode 100644 test/jdk/sun/net/www/http/HttpClient/IsKeepingAlive.policy delete mode 100644 test/jdk/sun/net/www/http/HttpClient/OpenServer.java delete mode 100644 test/jdk/sun/net/www/http/HttpClient/OpenServer.policy rename test/jdk/sun/net/www/http/HttpURLConnection/{NTLMAuthWithSM.java => BasicNTLMAuthTest.java} (94%) delete mode 100644 test/jdk/sun/net/www/http/HttpURLConnection/NTLMAuthWithSM.policy delete mode 100644 test/jdk/sun/net/www/protocol/jrt/WithSecurityManager.java delete mode 100644 test/jdk/sun/net/www/protocol/jrt/java.policy delete mode 100644 test/jdk/sun/nio/cs/TestSJIS0213_SM.java delete mode 100644 test/jdk/sun/reflect/ReflectionFactory/security.policy delete mode 100644 test/jdk/sun/rmi/server/MarshalOutputStream/marshalForeignStub/security.policy delete mode 100644 test/jdk/sun/rmi/transport/tcp/disableMultiplexing/DisableMultiplexing.java delete mode 100644 test/jdk/sun/rmi/transport/tcp/disableMultiplexing/DisableMultiplexing_Stub.java delete mode 100644 test/jdk/sun/security/ec/TestEC.policy delete mode 100644 test/jdk/sun/security/krb5/auto/AcceptPermissions.java delete mode 100644 test/jdk/sun/security/krb5/auto/KeyPermissions.java delete mode 100644 test/jdk/sun/security/krb5/auto/principalProperty/principalSystemPropTest.policy delete mode 100644 test/jdk/sun/security/mscapi/AccessKeyStore.java delete mode 100644 test/jdk/sun/security/mscapi/access.policy delete mode 100644 test/jdk/sun/security/mscapi/noaccess.policy delete mode 100644 test/jdk/sun/security/pkcs11/KeyAgreement/IllegalPackageAccess.java delete mode 100644 test/jdk/sun/security/pkcs11/KeyStore/Basic.policy delete mode 100644 test/jdk/sun/security/pkcs11/Provider/Login.policy delete mode 100644 test/jdk/sun/security/pkcs11/Secmod/policy delete mode 100644 test/jdk/sun/security/pkcs11/ec/policy delete mode 100644 test/jdk/sun/security/pkcs11/policy delete mode 100644 test/jdk/sun/security/pkcs11/rsa/TestCACerts.policy delete mode 100644 test/jdk/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy delete mode 100644 test/jdk/sun/security/pkcs11/rsa/rsakeys.ks.policy delete mode 100644 test/jdk/sun/security/pkcs11/sslecc/policy delete mode 100644 test/jdk/sun/security/pkcs11/tls/TestMasterSecret.policy delete mode 100644 test/jdk/sun/security/pkcs11/tls/policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/Alias.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/Alias.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/AliasExpansion.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/AliasExpansion.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/BadPolicyFile.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/BadPolicyFile.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/CombinedPerms.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/CombinedPerms.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/DefaultPolicy.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/EmailAddress.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/EmailAddress.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/Extra.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.sh delete mode 100644 test/jdk/sun/security/provider/PolicyFile/Modules.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/SelfExpansion.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/SelfExpansion.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/SelfWildcard.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/SelfWildcard.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/SomeExtensionClass.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TokenStore.RelPassPolicy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TokenStore.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TokenStore.keystore delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TokenStore.pwd delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TrustedCert.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TrustedCert.keystore delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TrustedCert.keystore1 delete mode 100644 test/jdk/sun/security/provider/PolicyFile/TrustedCert.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/Utf8.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/Utf8.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/WildcardPrincipalName.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/GetInstance.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/GetInstance.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/NoArgPermission.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/OneArgPermission.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/TwoArgNullActionsPermission.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/TwoArgPermission.java delete mode 100644 test/jdk/sun/security/provider/PolicyFile/getinstance/getinstance.sh delete mode 100644 test/jdk/sun/security/provider/PolicyFile/modules.policy delete mode 100644 test/jdk/sun/security/provider/PolicyFile/wildcard.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/AvoidPropertyExpansionExceptions.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/BogusGrants.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/EncodeURL.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExpansionErrorMisleading.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExpansionErrorMisleading.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirs.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirs.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirs1.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirs2.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirs3.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsA.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsA/a.jar delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsB.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsB/b.jar delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsChange.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsChange.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/ExtDirsDefaultPolicy.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/PrincipalExpansionError.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/PrincipalExpansionError.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/PrincipalExpansionError.sh delete mode 100644 test/jdk/sun/security/provider/PolicyParser/PrincipalExpansionErrorAction.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/TokenStore.java delete mode 100644 test/jdk/sun/security/provider/PolicyParser/UnresolvedProperty.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/p001.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/p002.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/p003.policy delete mode 100644 test/jdk/sun/security/provider/PolicyParser/p004.policy delete mode 100644 test/jdk/sun/security/smartcardio/test.policy delete mode 100644 test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.java delete mode 100644 test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy delete mode 100644 test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.sh delete mode 100644 test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTestHeyYou.java delete mode 100644 test/jdk/sun/security/tools/jarsigner/multiRelease/SignedJar.policy delete mode 100644 test/jdk/sun/security/util/DerInputBuffer/Allow.policy delete mode 100644 test/jdk/sun/security/util/FilePermCompat/CompatImpact.java delete mode 100644 test/jdk/sun/security/util/FilePermCompat/Flag.java delete mode 100644 test/jdk/sun/security/util/FilePermCompat/flag.policy delete mode 100644 test/jdk/sun/security/util/Resources/Format.policy delete mode 100644 test/jdk/sun/security/util/Resources/customSysClassLoader/error.policy delete mode 100644 test/jdk/sun/security/util/Resources/early/EarlyResources.java delete mode 100644 test/jdk/sun/security/util/Resources/early/malformed.policy delete mode 100644 test/jdk/sun/security/x509/AVA/Allow.policy delete mode 100644 test/jdk/sun/util/locale/provider/Bug8152817.java delete mode 100644 test/jdk/tools/jlink/SecurityTest.java delete mode 100644 test/jdk/tools/jlink/toolprovider.policy delete mode 100644 test/langtools/tools/javac/api/ToolProvider/ToolProviderTest.java delete mode 100644 test/micro/org/openjdk/bench/java/lang/reflect/ClazzWithSecurityManager.java diff --git a/make/modules/java.base/Copy.gmk b/make/modules/java.base/Copy.gmk index fea544a47a132..7e3a0e9c3ab5e 100644 --- a/make/modules/java.base/Copy.gmk +++ b/make/modules/java.base/Copy.gmk @@ -123,46 +123,6 @@ TARGETS += $(JVMCFG) ################################################################################ -POLICY_SRC := $(TOPDIR)/src/java.base/share/conf/security/java.policy -POLICY_DST := $(CONF_DST_DIR)/security/java.policy - -POLICY_SRC_LIST := $(POLICY_SRC) - -$(POLICY_DST): $(POLICY_SRC_LIST) - $(call MakeTargetDir) - $(RM) $@ $@.tmp - $(foreach f, $(POLICY_SRC_LIST), $(CAT) $(f) >> $@.tmp;) - $(MV) $@.tmp $@ - -TARGETS += $(POLICY_DST) - -################################################################################ - -DEF_POLICY_SRC := $(TOPDIR)/src/java.base/share/lib/security/default.policy -DEF_POLICY_DST := $(LIB_DST_DIR)/security/default.policy - -DEF_POLICY_SRC_LIST := $(DEF_POLICY_SRC) -DEF_POLICY_SRC_LIST += $(CUSTOM_POLICY_SRC_LIST) - -ifeq ($(call isTargetOs, windows), true) - DEF_POLICY_SRC_LIST += $(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/lib/security/default.policy -endif - -# Allow imported modules to modify the java.policy -ifneq ($(IMPORT_MODULES_CONF), ) - DEF_POLICY_SRC_LIST += $(wildcard $(IMPORT_MODULES_CONF)/java.base/security/java.policy.extra) -endif - -$(DEF_POLICY_DST): $(DEF_POLICY_SRC_LIST) - $(call MakeTargetDir) - $(RM) $@ $@.tmp - $(foreach f, $(DEF_POLICY_SRC_LIST), $(CAT) $(f) >> $@.tmp;) - $(MV) $@.tmp $@ - -TARGETS += $(DEF_POLICY_DST) - -################################################################################ - # CACERTS_FILE is optionally set in configure to override the default cacerts # which is otherwise generated in Gendata-java.base.gmk CACERTS_DST := $(LIB_DST_DIR)/security/cacerts diff --git a/make/modules/java.rmi/Launcher.gmk b/make/modules/java.rmi/Launcher.gmk index 14b381a540ec3..8c335711da026 100644 --- a/make/modules/java.rmi/Launcher.gmk +++ b/make/modules/java.rmi/Launcher.gmk @@ -31,5 +31,4 @@ include LauncherCommon.gmk $(eval $(call SetupBuildLauncher, rmiregistry, \ MAIN_CLASS := sun.rmi.registry.RegistryImpl, \ - JAVA_ARGS := -Djava.security.manager=allow, \ )) diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index f25c453582fb8..eecfc9e88a0fd 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -347,68 +347,6 @@ void Dictionary::check_package_access(InstanceKlass* klass, assert(class_loader() != nullptr, "Should not call this"); assert(protection_domain() != nullptr, "Should not call this"); - - if (!java_lang_System::allow_security_manager()) { - // No need for any further checking. Package access always allowed. - return; - } - - if (is_in_package_access_cache(THREAD, klass->name(), protection_domain)) { - // No need to check again. - return; - } - - // We only have to call checkPackageAccess if there's a security manager installed. - if (java_lang_System::has_security_manager()) { - - // This handle and the class_loader handle passed in keeps this class from - // being unloaded through several GC points. - // The class_loader handle passed in is the initiating loader. - Handle mirror(THREAD, klass->java_mirror()); - - // Now we have to call back to java to check if the initating class has access - InstanceKlass* system_loader = vmClasses::ClassLoader_klass(); - JavaValue result(T_VOID); - JavaCalls::call_special(&result, - class_loader, - system_loader, - vmSymbols::checkPackageAccess_name(), - vmSymbols::class_protectiondomain_signature(), - mirror, - protection_domain, - THREAD); - - LogTarget(Debug, protectiondomain) lt; - if (lt.is_enabled()) { - ResourceMark rm(THREAD); - // Print out trace information - LogStream ls(lt); - ls.print_cr("Checking package access"); - ls.print("class loader: "); - class_loader()->print_value_on(&ls); - ls.print(" protection domain: "); - protection_domain()->print_value_on(&ls); - ls.print(" loading: "); klass->print_value_on(&ls); - if (HAS_PENDING_EXCEPTION) { - ls.print_cr(" DENIED !!!!!!!!!!!!!!!!!!!!!"); - } else { - ls.print_cr(" granted"); - } - } - - if (HAS_PENDING_EXCEPTION) return; - } - - // If no exception has been thrown, we have checked that the protection_domain can access - // this klass. Always add it to the cache (even if no SecurityManager is installed yet). - // - // This ensures that subsequent calls to Dictionary::find(THREAD, klass->name(), protection_domain) - // will always succeed. I.e., a new SecurityManager installed in the future cannot retroactively - // revoke the granted access. - { - MutexLocker mu(THREAD, SystemDictionary_lock); - add_to_package_access_cache(THREAD, klass, protection_domain); - } } // During class loading we may have cached a protection domain that has diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 0b6704c1a5b22..c18f23badd14f 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1599,7 +1599,6 @@ oop java_lang_Thread_Constants::get_VTHREAD_GROUP() { int java_lang_Thread::_holder_offset; int java_lang_Thread::_name_offset; int java_lang_Thread::_contextClassLoader_offset; -int java_lang_Thread::_inheritedAccessControlContext_offset; int java_lang_Thread::_eetop_offset; int java_lang_Thread::_jvmti_thread_state_offset; int java_lang_Thread::_jvmti_VTMS_transition_disable_count_offset; @@ -1616,7 +1615,6 @@ JFR_ONLY(int java_lang_Thread::_jfr_epoch_offset;) macro(_holder_offset, k, "holder", thread_fieldholder_signature, false); \ macro(_name_offset, k, vmSymbols::name_name(), string_signature, false); \ macro(_contextClassLoader_offset, k, vmSymbols::contextClassLoader_name(), classloader_signature, false); \ - macro(_inheritedAccessControlContext_offset, k, vmSymbols::inheritedAccessControlContext_name(), accesscontrolcontext_signature, false); \ macro(_eetop_offset, k, "eetop", long_signature, false); \ macro(_interrupted_offset, k, "interrupted", bool_signature, false); \ macro(_interruptLock_offset, k, "interruptLock", object_signature, false); \ @@ -1794,10 +1792,6 @@ oop java_lang_Thread::context_class_loader(oop java_thread) { return java_thread->obj_field(_contextClassLoader_offset); } -oop java_lang_Thread::inherited_access_control_context(oop java_thread) { - return java_thread->obj_field(_inheritedAccessControlContext_offset); -} - jlong java_lang_Thread::stackSize(oop java_thread) { GET_FIELDHOLDER_FIELD(java_thread, stackSize, 0); @@ -4861,17 +4855,11 @@ oop java_lang_ClassLoader::unnamedModule(oop loader) { int java_lang_System::_static_in_offset; int java_lang_System::_static_out_offset; int java_lang_System::_static_err_offset; -int java_lang_System::_static_security_offset; -int java_lang_System::_static_allow_security_offset; -int java_lang_System::_static_never_offset; #define SYSTEM_FIELDS_DO(macro) \ macro(_static_in_offset, k, "in", input_stream_signature, true); \ macro(_static_out_offset, k, "out", print_stream_signature, true); \ - macro(_static_err_offset, k, "err", print_stream_signature, true); \ - macro(_static_security_offset, k, "security", security_manager_signature, true); \ - macro(_static_allow_security_offset, k, "allowSecurityManager", int_signature, true); \ - macro(_static_never_offset, k, "NEVER", int_signature, true) + macro(_static_err_offset, k, "err", print_stream_signature, true); void java_lang_System::compute_offsets() { InstanceKlass* k = vmClasses::System_klass(); @@ -4881,21 +4869,12 @@ void java_lang_System::compute_offsets() { // This field tells us that a security manager can never be installed so we // can completely skip populating the ProtectionDomainCacheTable. bool java_lang_System::allow_security_manager() { - static int initialized = false; - static bool allowed = true; // default - if (!initialized) { - oop base = vmClasses::System_klass()->static_field_base_raw(); - int never = base->int_field(_static_never_offset); - allowed = (base->int_field(_static_allow_security_offset) != never); - initialized = true; - } - return allowed; + return false; } // This field tells us that a security manager is installed. bool java_lang_System::has_security_manager() { - oop base = vmClasses::System_klass()->static_field_base_raw(); - return base->obj_field(_static_security_offset) != nullptr; + return false; } #if INCLUDE_CDS diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index c968859c591e5..e01235958103a 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -358,7 +358,6 @@ class java_lang_Thread : AllStatic { static int _holder_offset; static int _name_offset; static int _contextClassLoader_offset; - static int _inheritedAccessControlContext_offset; static int _eetop_offset; static int _jvmti_thread_state_offset; static int _jvmti_VTMS_transition_disable_count_offset; @@ -405,8 +404,6 @@ class java_lang_Thread : AllStatic { static void set_daemon(oop java_thread); // Context ClassLoader static oop context_class_loader(oop java_thread); - // Control context - static oop inherited_access_control_context(oop java_thread); // Stack size hint static jlong stackSize(oop java_thread); // Thread ID diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index d42e718a155d2..34861b52d54fc 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -439,10 +439,8 @@ class SerializeClosure; template(getProperty_name, "getProperty") \ template(context_name, "context") \ template(contextClassLoader_name, "contextClassLoader") \ - template(inheritedAccessControlContext_name, "inheritedAccessControlContext") \ template(getClassContext_name, "getClassContext") \ template(wait_name, "wait0") \ - template(checkPackageAccess_name, "checkPackageAccess") \ template(forName_name, "forName") \ template(forName0_name, "forName0") \ template(isJavaIdentifierStart_name, "isJavaIdentifierStart") \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index d4fddd222b2bb..28e8c2b08d36e 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -723,13 +723,6 @@ JNIEXPORT jbyte JNICALL JVM_ConstantPoolGetTagAt JNIEXPORT jobjectArray JNICALL JVM_GetMethodParameters(JNIEnv *env, jobject method); -/* - * java.security.* - */ - -JNIEXPORT jobject JNICALL -JVM_GetInheritedAccessControlContext(JNIEnv *env, jclass cls); - /* * Ensure that code doing a stackwalk and using javaVFrame::locals() to * get the value will see a materialized value and not a scalar-replaced @@ -741,9 +734,6 @@ JVM_GetInheritedAccessControlContext(JNIEnv *env, jclass cls); JNIEXPORT void JNICALL JVM_EnsureMaterializedForStackWalk_func(JNIEnv* env, jobject vthread, jobject value); -JNIEXPORT jobject JNICALL -JVM_GetStackAccessControlContext(JNIEnv *env, jclass cls); - /* * Signal support, used to implement the shutdown sequence. Every VM must * support JVM_SIGINT and JVM_SIGTERM, raising the former for user interrupts diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 8bc03ce2efb42..9a132b089f2e1 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -1269,87 +1269,6 @@ JVM_ENTRY(jobject, JVM_GetProtectionDomain(JNIEnv *env, jclass cls)) JVM_END -// Returns the inherited_access_control_context field of the running thread. -JVM_ENTRY(jobject, JVM_GetInheritedAccessControlContext(JNIEnv *env, jclass cls)) - oop result = java_lang_Thread::inherited_access_control_context(thread->threadObj()); - return JNIHandles::make_local(THREAD, result); -JVM_END - -JVM_ENTRY(jobject, JVM_GetStackAccessControlContext(JNIEnv *env, jclass cls)) - if (!UsePrivilegedStack) return nullptr; - - ResourceMark rm(THREAD); - GrowableArray* local_array = new GrowableArray(12); - JvmtiVMObjectAllocEventCollector oam; - - // count the protection domains on the execution stack. We collapse - // duplicate consecutive protection domains into a single one, as - // well as stopping when we hit a privileged frame. - - oop previous_protection_domain = nullptr; - Handle privileged_context(thread, nullptr); - bool is_privileged = false; - oop protection_domain = nullptr; - - // Iterate through Java frames - vframeStream vfst(thread); - for(; !vfst.at_end(); vfst.next()) { - // get method of frame - Method* method = vfst.method(); - - // stop at the first privileged frame - if (method->method_holder() == vmClasses::AccessController_klass() && - method->name() == vmSymbols::executePrivileged_name()) - { - // this frame is privileged - is_privileged = true; - - javaVFrame *priv = vfst.asJavaVFrame(); // executePrivileged - - StackValueCollection* locals = priv->locals(); - StackValue* ctx_sv = locals->at(1); // AccessControlContext context - StackValue* clr_sv = locals->at(2); // Class caller - assert(!ctx_sv->obj_is_scalar_replaced(), "found scalar-replaced object"); - assert(!clr_sv->obj_is_scalar_replaced(), "found scalar-replaced object"); - privileged_context = ctx_sv->get_obj(); - Handle caller = clr_sv->get_obj(); - - Klass *caller_klass = java_lang_Class::as_Klass(caller()); - protection_domain = caller_klass->protection_domain(); - } else { - protection_domain = method->method_holder()->protection_domain(); - } - - if ((previous_protection_domain != protection_domain) && (protection_domain != nullptr)) { - local_array->push(Handle(thread, protection_domain)); - previous_protection_domain = protection_domain; - } - - if (is_privileged) break; - } - - - // either all the domains on the stack were system domains, or - // we had a privileged system domain - if (local_array->is_empty()) { - if (is_privileged && privileged_context.is_null()) return nullptr; - - oop result = java_security_AccessControlContext::create(objArrayHandle(), is_privileged, privileged_context, CHECK_NULL); - return JNIHandles::make_local(THREAD, result); - } - - objArrayOop context = oopFactory::new_objArray(vmClasses::ProtectionDomain_klass(), - local_array->length(), CHECK_NULL); - objArrayHandle h_context(thread, context); - for (int index = 0; index < local_array->length(); index++) { - h_context->obj_at_put(index, local_array->at(index)()); - } - - oop result = java_security_AccessControlContext::create(h_context, is_privileged, privileged_context, CHECK_NULL); - - return JNIHandles::make_local(THREAD, result); -JVM_END - class ScopedValueBindingsResolver { public: InstanceKlass* Carrier_klass; diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index cca58b0c22e26..70b537bf2d731 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -681,9 +681,6 @@ const int ObjectAlignmentInBytes = 8; develop(bool, PrintCodeCacheExtension, false, \ "Print extension of code cache") \ \ - develop(bool, UsePrivilegedStack, true, \ - "Enable the security JVM functions") \ - \ product(bool, ClassUnloading, true, \ "Do unloading of classes") \ \ diff --git a/src/java.base/share/classes/java/io/File.java b/src/java.base/share/classes/java/io/File.java index 61c15bdd15c0b..b8eda9dcf8315 100644 --- a/src/java.base/share/classes/java/io/File.java +++ b/src/java.base/share/classes/java/io/File.java @@ -556,9 +556,6 @@ public boolean isAbsolute() { * @return The absolute pathname string denoting the same file or * directory as this abstract pathname * - * @throws SecurityException - * If a required system property value cannot be accessed. - * * @see java.io.File#isAbsolute() */ public String getAbsolutePath() { @@ -572,9 +569,6 @@ public String getAbsolutePath() { * @return The absolute abstract pathname denoting the same file or * directory as this abstract pathname * - * @throws SecurityException - * If a required system property value cannot be accessed. - * * @since 1.2 */ public File getAbsoluteFile() { @@ -614,12 +608,6 @@ public File getAbsoluteFile() { * construction of the canonical pathname may require * filesystem queries * - * @throws SecurityException - * If a required system property value cannot be accessed, or - * if a security manager exists and its {@link - * java.lang.SecurityManager#checkRead} method denies - * read access to the file - * * @since 1.1 * @see Path#toRealPath */ @@ -642,12 +630,6 @@ public String getCanonicalPath() throws IOException { * construction of the canonical pathname may require * filesystem queries * - * @throws SecurityException - * If a required system property value cannot be accessed, or - * if a security manager exists and its {@link - * java.lang.SecurityManager#checkRead} method denies - * read access to the file - * * @since 1.2 * @see Path#toRealPath */ @@ -736,8 +718,6 @@ public URL toURL() throws MalformedURLException { * @return An absolute, hierarchical URI with a scheme equal to * {@code "file"}, a path representing this abstract pathname, * and undefined authority, query, and fragment components - * @throws SecurityException If a required system property value cannot - * be accessed. * * @see #File(java.net.URI) * @see java.net.URI @@ -769,11 +749,6 @@ public URI toURI() { * @return {@code true} if and only if the file specified by this * abstract pathname exists and can be read by the * application; {@code false} otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file */ public boolean canRead() { @SuppressWarnings("removal") @@ -798,11 +773,6 @@ public boolean canRead() { * contains a file denoted by this abstract pathname and * the application is allowed to write to the file; * {@code false} otherwise. - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file */ public boolean canWrite() { @SuppressWarnings("removal") @@ -822,11 +792,6 @@ public boolean canWrite() { * * @return {@code true} if and only if the file or directory denoted * by this abstract pathname exists; {@code false} otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file or directory */ public boolean exists() { @SuppressWarnings("removal") @@ -853,11 +818,6 @@ public boolean exists() { * @return {@code true} if and only if the file denoted by this * abstract pathname exists and is a directory; * {@code false} otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file */ public boolean isDirectory() { @SuppressWarnings("removal") @@ -886,11 +846,6 @@ public boolean isDirectory() { * @return {@code true} if and only if the file denoted by this * abstract pathname exists and is a normal file; * {@code false} otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file */ public boolean isFile() { @SuppressWarnings("removal") @@ -923,11 +878,6 @@ public boolean isFile() { * abstract pathname is hidden according to the conventions of the * underlying platform * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file - * * @since 1.2 */ public boolean isHidden() { @@ -968,11 +918,6 @@ public boolean isHidden() { * file does not exist or if an I/O error occurs. The value may * be negative indicating the number of milliseconds before the * epoch - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file */ public long lastModified() { @SuppressWarnings("removal") @@ -1000,11 +945,6 @@ public long lastModified() { * pathname, or {@code 0L} if the file does not exist. Some * operating systems may return {@code 0L} for pathnames * denoting system-dependent entities such as devices or pipes. - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method denies read access to the file */ public long length() { @SuppressWarnings("removal") @@ -1040,11 +980,6 @@ public long length() { * @throws IOException * If an I/O error occurred * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file - * * @since 1.2 */ public boolean createNewFile() throws IOException { @@ -1070,11 +1005,6 @@ public boolean createNewFile() throws IOException { * * @return {@code true} if and only if the file or directory is * successfully deleted; {@code false} otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkDelete} method denies - * delete access to the file */ public boolean delete() { @SuppressWarnings("removal") @@ -1108,11 +1038,6 @@ public boolean delete() { * {@link java.nio.channels.FileLock FileLock} * facility should be used instead. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkDelete} method denies - * delete access to the file - * * @see #delete * * @since 1.2 @@ -1155,11 +1080,6 @@ public void deleteOnExit() { * empty if the directory is empty. Returns {@code null} if * this abstract pathname does not denote a directory, or if an * I/O error occurs. - * - * @throws SecurityException - * If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to - * the directory */ public String[] list() { return normalizedList(); @@ -1175,11 +1095,6 @@ public String[] list() { * empty if the directory is empty. Returns {@code null} if * this abstract pathname does not denote a directory, or if an * I/O error occurs. - * - * @throws SecurityException - * If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to - * the directory */ private final String[] normalizedList() { @SuppressWarnings("removal") @@ -1223,11 +1138,6 @@ private final String[] normalizedList() { * Returns {@code null} if this abstract pathname does not denote * a directory, or if an I/O error occurs. * - * @throws SecurityException - * If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to - * the directory - * * @see java.nio.file.Files#newDirectoryStream(Path,String) */ public String[] list(FilenameFilter filter) { @@ -1275,11 +1185,6 @@ public String[] list(FilenameFilter filter) { * {@code null} if this abstract pathname does not denote a * directory, or if an I/O error occurs. * - * @throws SecurityException - * If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to - * the directory - * * @since 1.2 */ public File[] listFiles() { @@ -1315,11 +1220,6 @@ public File[] listFiles() { * {@code null} if this abstract pathname does not denote a * directory, or if an I/O error occurs. * - * @throws SecurityException - * If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to - * the directory - * * @since 1.2 * @see java.nio.file.Files#newDirectoryStream(Path,String) */ @@ -1353,11 +1253,6 @@ public File[] listFiles(FilenameFilter filter) { * {@code null} if this abstract pathname does not denote a * directory, or if an I/O error occurs. * - * @throws SecurityException - * If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to - * the directory - * * @since 1.2 * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) */ @@ -1378,11 +1273,6 @@ public File[] listFiles(FileFilter filter) { * * @return {@code true} if and only if the directory was * created; {@code false} otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method does not permit the named directory to be created */ public boolean mkdir() { @SuppressWarnings("removal") @@ -1405,16 +1295,6 @@ public boolean mkdir() { * @return {@code true} if and only if the directory was created, * along with all necessary parent directories; {@code false} * otherwise - * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkRead(java.lang.String)} - * method does not permit verification of the existence of the - * named directory and all necessary parent directories; or if - * the {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method does not permit the named directory and all necessary - * parent directories to be created */ public boolean mkdirs() { if (exists()) { @@ -1458,11 +1338,6 @@ public boolean mkdirs() { * @return {@code true} if and only if the renaming succeeded; * {@code false} otherwise * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to either the old or new pathnames - * * @throws NullPointerException * If parameter {@code dest} is {@code null} */ @@ -1501,11 +1376,6 @@ public boolean renameTo(File dest) { * * @throws IllegalArgumentException If the argument is negative * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the named file - * * @since 1.2 */ public boolean setLastModified(long time) { @@ -1533,11 +1403,6 @@ public boolean setLastModified(long time) { * @return {@code true} if and only if the operation succeeded; * {@code false} otherwise * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the named file - * * @since 1.2 */ public boolean setReadOnly() { @@ -1577,11 +1442,6 @@ public boolean setReadOnly() { * operation will fail if the user does not have permission to change * the access permissions of this abstract pathname. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the named file - * * @since 1.6 */ public boolean setWritable(boolean writable, boolean ownerOnly) { @@ -1617,11 +1477,6 @@ public boolean setWritable(boolean writable, boolean ownerOnly) { * operation will fail if the user does not have permission to * change the access permissions of this abstract pathname. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file - * * @since 1.6 */ public boolean setWritable(boolean writable) { @@ -1659,11 +1514,6 @@ public boolean setWritable(boolean writable) { * fails, or the value of the {@code readable} parameter if * setting the read permission is not supported. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file - * * @since 1.6 */ public boolean setReadable(boolean readable, boolean ownerOnly) { @@ -1705,11 +1555,6 @@ public boolean setReadable(boolean readable, boolean ownerOnly) { * fails, or the value of the {@code readable} parameter if * setting the read permission is not supported. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file - * * @since 1.6 */ public boolean setReadable(boolean readable) { @@ -1747,11 +1592,6 @@ public boolean setReadable(boolean readable) { * fails, or the value of the {@code executable} parameter if * setting the execute permission is not supported. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file - * * @since 1.6 */ public boolean setExecutable(boolean executable, boolean ownerOnly) { @@ -1793,11 +1633,6 @@ public boolean setExecutable(boolean executable, boolean ownerOnly) { * fails, or the value of the {@code executable} parameter if * setting the execute permission is not supported. * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method denies write access to the file - * * @since 1.6 */ public boolean setExecutable(boolean executable) { @@ -1814,11 +1649,6 @@ public boolean setExecutable(boolean executable) { * @return {@code true} if and only if the abstract pathname exists * and the application is allowed to execute the file * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkExec(java.lang.String)} - * method denies execute access to the file - * * @since 1.6 */ public boolean canExecute() { @@ -1850,12 +1680,6 @@ public boolean canExecute() { * machine will begin with one of the roots returned by this method. * There is no guarantee that a root directory can be accessed. * - *

Unlike most methods in this class, this method does not throw - * security exceptions. If a security manager exists and its {@link - * SecurityManager#checkRead(String)} method denies read access to a - * particular root directory, then that directory will not appear in the - * result. - * * @implNote * Windows platforms, for example, have a root directory * for each active drive; UNIX platforms have a single root directory, @@ -1898,12 +1722,6 @@ public static File[] listRoots() { * abstract pathname does not name a partition or if the size * cannot be obtained * - * @throws SecurityException - * If a security manager has been installed and it denies - * {@link RuntimePermission}{@code ("getFileSystemAttributes")} - * or its {@link SecurityManager#checkRead(String)} method denies - * read access to the file named by this abstract pathname - * * @since 1.6 * @see FileStore#getTotalSpace */ @@ -1942,12 +1760,6 @@ public long getTotalSpace() { * equal to the total file system size returned by * {@link #getTotalSpace}. * - * @throws SecurityException - * If a security manager has been installed and it denies - * {@link RuntimePermission}{@code ("getFileSystemAttributes")} - * or its {@link SecurityManager#checkRead(String)} method denies - * read access to the file named by this abstract pathname - * * @since 1.6 * @see FileStore#getUnallocatedSpace */ @@ -1989,12 +1801,6 @@ public long getFreeSpace() { * is not available, this method will be equivalent to a call to * {@link #getFreeSpace}. * - * @throws SecurityException - * If a security manager has been installed and it denies - * {@link RuntimePermission}{@code ("getFileSystemAttributes")} - * or its {@link SecurityManager#checkRead(String)} method denies - * read access to the file named by this abstract pathname - * * @since 1.6 * @see FileStore#getUsableSpace */ @@ -2176,11 +1982,6 @@ static File generateFile(String prefix, String suffix, File dir) * @throws IOException * If a file could not be created * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method does not allow a file to be created - * * @since 1.2 */ public static File createTempFile(String prefix, String suffix, @@ -2250,11 +2051,6 @@ public static File createTempFile(String prefix, String suffix, * * @throws IOException If a file could not be created * - * @throws SecurityException - * If a security manager exists and its {@link - * java.lang.SecurityManager#checkWrite(java.lang.String)} - * method does not allow a file to be created - * * @since 1.2 * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) */ diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java index 4210a0f56b6b6..e429faec09bd0 100644 --- a/src/java.base/share/classes/java/io/FileInputStream.java +++ b/src/java.base/share/classes/java/io/FileInputStream.java @@ -95,11 +95,6 @@ public class FileInputStream extends InputStream * object is created to represent this file * connection. *

- * First, if there is a security - * manager, its {@code checkRead} method - * is called with the {@code name} argument - * as its argument. - *

* If the named file does not exist, is a directory rather than a regular * file, or for some other reason cannot be opened for reading then a * {@code FileNotFoundException} is thrown. @@ -109,10 +104,6 @@ public class FileInputStream extends InputStream * is a directory rather than a regular file, * or for some other reason cannot be opened for * reading. - * @throws SecurityException if a security manager exists and its - * {@code checkRead} method denies read access - * to the file. - * @see java.lang.SecurityManager#checkRead(java.lang.String) */ public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); @@ -126,11 +117,6 @@ public FileInputStream(String name) throws FileNotFoundException { * A new {@code FileDescriptor} object * is created to represent this file connection. *

- * First, if there is a security manager, - * its {@code checkRead} method is called - * with the path represented by the {@code file} - * argument as its argument. - *

* If the named file does not exist, is a directory rather than a regular * file, or for some other reason cannot be opened for reading then a * {@code FileNotFoundException} is thrown. @@ -140,10 +126,7 @@ public FileInputStream(String name) throws FileNotFoundException { * is a directory rather than a regular file, * or for some other reason cannot be opened for * reading. - * @throws SecurityException if a security manager exists and its - * {@code checkRead} method denies read access to the file. * @see java.io.File#getPath() - * @see java.lang.SecurityManager#checkRead(java.lang.String) */ @SuppressWarnings("this-escape") public FileInputStream(File file) throws FileNotFoundException { @@ -171,11 +154,6 @@ public FileInputStream(File file) throws FileNotFoundException { * {@code fdObj}, which represents an existing connection to an * actual file in the file system. *

- * If there is a security manager, its {@code checkRead} method is - * called with the file descriptor {@code fdObj} as its argument to - * see if it's ok to read the file descriptor. If read access is denied - * to the file descriptor a {@code SecurityException} is thrown. - *

* If {@code fdObj} is null then a {@code NullPointerException} * is thrown. *

@@ -185,10 +163,6 @@ public FileInputStream(File file) throws FileNotFoundException { * I/O on the stream, an {@code IOException} is thrown. * * @param fdObj the file descriptor to be opened for reading. - * @throws SecurityException if a security manager exists and its - * {@code checkRead} method denies read access to the - * file descriptor. - * @see SecurityManager#checkRead(java.io.FileDescriptor) */ @SuppressWarnings("this-escape") public FileInputStream(FileDescriptor fdObj) { diff --git a/src/java.base/share/classes/java/io/FileOutputStream.java b/src/java.base/share/classes/java/io/FileOutputStream.java index ff15e831b9530..557bea0c3fc51 100644 --- a/src/java.base/share/classes/java/io/FileOutputStream.java +++ b/src/java.base/share/classes/java/io/FileOutputStream.java @@ -103,9 +103,6 @@ public class FileOutputStream extends OutputStream * A new {@code FileDescriptor} object is * created to represent this file connection. *

- * First, if there is a security manager, its {@code checkWrite} - * method is called with {@code name} as its argument. - *

* If the file exists but is a directory rather than a regular file, does * not exist but cannot be created, or cannot be opened for any other * reason then a {@code FileNotFoundException} is thrown. @@ -118,10 +115,6 @@ public class FileOutputStream extends OutputStream * @throws FileNotFoundException if the file exists but is a directory * rather than a regular file, does not exist but cannot * be created, or cannot be opened for any other reason - * @throws SecurityException if a security manager exists and its - * {@code checkWrite} method denies write access - * to the file. - * @see java.lang.SecurityManager#checkWrite(java.lang.String) */ public FileOutputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null, false); @@ -137,9 +130,6 @@ public FileOutputStream(String name) throws FileNotFoundException { * A new {@code FileDescriptor} object is created to represent this * file connection. *

- * First, if there is a security manager, its {@code checkWrite} - * method is called with {@code name} as its argument. - *

* If the file exists but is a directory rather than a regular file, does * not exist but cannot be created, or cannot be opened for any other * reason then a {@code FileNotFoundException} is thrown. @@ -150,10 +140,6 @@ public FileOutputStream(String name) throws FileNotFoundException { * @throws FileNotFoundException if the file exists but is a directory * rather than a regular file, does not exist but cannot * be created, or cannot be opened for any other reason. - * @throws SecurityException if a security manager exists and its - * {@code checkWrite} method denies write access - * to the file. - * @see java.lang.SecurityManager#checkWrite(java.lang.String) * @since 1.1 */ public FileOutputStream(String name, boolean append) @@ -171,10 +157,6 @@ public FileOutputStream(String name, boolean append) * A new {@code FileDescriptor} object is * created to represent this file connection. *

- * First, if there is a security manager, its {@code checkWrite} - * method is called with the path represented by the {@code file} - * argument as its argument. - *

* If the file exists but is a directory rather than a regular file, does * not exist but cannot be created, or cannot be opened for any other * reason then a {@code FileNotFoundException} is thrown. @@ -183,12 +165,7 @@ public FileOutputStream(String name, boolean append) * @throws FileNotFoundException if the file exists but is a directory * rather than a regular file, does not exist but cannot * be created, or cannot be opened for any other reason - * @throws SecurityException if a security manager exists and its - * {@code checkWrite} method denies write access - * to the file. * @see java.io.File#getPath() - * @see java.lang.SecurityException - * @see java.lang.SecurityManager#checkWrite(java.lang.String) */ public FileOutputStream(File file) throws FileNotFoundException { this(file, false); @@ -205,10 +182,6 @@ public FileOutputStream(File file) throws FileNotFoundException { * A new {@code FileDescriptor} object is created to represent this * file connection. *

- * First, if there is a security manager, its {@code checkWrite} - * method is called with the path represented by the {@code file} - * argument as its argument. - *

* If the file exists but is a directory rather than a regular file, does * not exist but cannot be created, or cannot be opened for any other * reason then a {@code FileNotFoundException} is thrown. @@ -219,12 +192,7 @@ public FileOutputStream(File file) throws FileNotFoundException { * @throws FileNotFoundException if the file exists but is a directory * rather than a regular file, does not exist but cannot * be created, or cannot be opened for any other reason - * @throws SecurityException if a security manager exists and its - * {@code checkWrite} method denies write access - * to the file. * @see java.io.File#getPath() - * @see java.lang.SecurityException - * @see java.lang.SecurityManager#checkWrite(java.lang.String) * @since 1.4 */ @SuppressWarnings("this-escape") @@ -256,10 +224,6 @@ public FileOutputStream(File file, boolean append) * descriptor, which represents an existing connection to an actual * file in the file system. *

- * First, if there is a security manager, its {@code checkWrite} - * method is called with the file descriptor {@code fdObj} - * argument as its argument. - *

* If {@code fdObj} is null then a {@code NullPointerException} * is thrown. *

@@ -269,10 +233,6 @@ public FileOutputStream(File file, boolean append) * I/O on the stream, an {@code IOException} is thrown. * * @param fdObj the file descriptor to be opened for writing - * @throws SecurityException if a security manager exists and its - * {@code checkWrite} method denies - * write access to the file descriptor - * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor) */ @SuppressWarnings("this-escape") public FileOutputStream(FileDescriptor fdObj) { diff --git a/src/java.base/share/classes/java/io/FilePermission.java b/src/java.base/share/classes/java/io/FilePermission.java index 808c42931434d..b11e0dd25bebe 100644 --- a/src/java.base/share/classes/java/io/FilePermission.java +++ b/src/java.base/share/classes/java/io/FilePermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,39 +61,15 @@ * (recursively) all files and subdirectories contained in the current * directory. *

- * The actions to be granted are passed to the constructor in a string containing + * The actions are passed to the constructor in a string containing * a list of one or more comma-separated keywords. The possible keywords are - * "read", "write", "execute", "delete", and "readlink". Their meaning is - * defined as follows: - * - *

- *
read
read permission - *
write
write permission - *
execute - *
execute permission. Allows {@code Runtime.exec} to - * be called. Corresponds to {@code SecurityManager.checkExec}. - *
delete - *
delete permission. Allows {@code File.delete} to - * be called. Corresponds to {@code SecurityManager.checkDelete}. - *
readlink - *
read link permission. Allows the target of a - * symbolic link - * to be read by invoking the {@link java.nio.file.Files#readSymbolicLink - * readSymbolicLink } method. - *
+ * "read", "write", "execute", "delete", and "readlink". *

* The actions string is converted to lowercase before processing. - *

- * Be careful when granting FilePermissions. Think about the implications - * of granting read and especially write access to various files and - * directories. The {@literal "<>"} permission with write action is - * especially dangerous. This grants permission to write to the entire - * file system. One thing this effectively allows is replacement of the - * system binary, including the JVM runtime environment. - *

- * Please note: Code can always read a file from the same - * directory it's in (or a subdirectory of that directory); it does not - * need explicit permission to do so. + * + * @apiNote + * This permission cannot be used for controlling access to resources + * as the Security Manager is no longer supported. * * @see java.security.Permission * @see java.security.Permissions diff --git a/src/java.base/share/classes/java/io/ObjectInputFilter.java b/src/java.base/share/classes/java/io/ObjectInputFilter.java index 879820761c129..9a04e1f58e330 100644 --- a/src/java.base/share/classes/java/io/ObjectInputFilter.java +++ b/src/java.base/share/classes/java/io/ObjectInputFilter.java @@ -728,8 +728,6 @@ public static ObjectInputFilter getSerialFilter() { * Set the static JVM-wide filter if it has not already been configured or set. * * @param filter the deserialization filter to set as the JVM-wide filter; not null - * @throws SecurityException if there is security manager and the - * {@code SerializablePermission("serialFilter")} is not granted * @throws IllegalStateException if the filter has already been set or the initialization * of the filter from the system property {@code jdk.serialFilter} or * the security property {@code jdk.serialFilter} fails. @@ -829,8 +827,6 @@ static BinaryOperator getSerialFilterFactorySingleton() { * @throws IllegalStateException if the builtin deserialization filter factory * has already been replaced or any instance of {@link ObjectInputStream} * has been created. - * @throws SecurityException if there is security manager and the - * {@code SerializablePermission("serialFilter")} is not granted * @since 17 */ public static void setSerialFilterFactory(BinaryOperator filterFactory) { diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index 983b80d2bc673..60d8cadf6a341 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -374,17 +374,9 @@ private static class Logging { * When the filter factory {@code apply} method is invoked it may throw a runtime exception * preventing the {@code ObjectInputStream} from being constructed. * - *

If a security manager is installed, this constructor will check for - * the "enableSubclassImplementation" SerializablePermission when invoked - * directly or indirectly by the constructor of a subclass which overrides - * the ObjectInputStream.readFields or ObjectInputStream.readUnshared - * methods. - * * @param in input stream to read from * @throws StreamCorruptedException if the stream header is incorrect * @throws IOException if an I/O error occurs while reading stream header - * @throws SecurityException if untrusted subclass illegally overrides - * security-sensitive methods * @throws IllegalStateException if the initialization of {@link ObjectInputFilter.Config} * fails due to invalid serial filter or serial filter factory properties. * @throws NullPointerException if {@code in} is {@code null} @@ -419,21 +411,11 @@ public ObjectInputStream(InputStream in) throws IOException { * When the filter factory {@code apply} method is invoked it may throw a runtime exception * preventing the {@code ObjectInputStream} from being constructed. * - *

If there is a security manager installed, this method first calls the - * security manager's {@code checkPermission} method with the - * {@code SerializablePermission("enableSubclassImplementation")} - * permission to ensure it's ok to enable subclassing. - * - * @throws SecurityException if a security manager exists and its - * {@code checkPermission} method denies enabling - * subclassing. * @throws IOException if an I/O error occurs while creating this stream * @throws IllegalStateException if the initialization of {@link ObjectInputFilter.Config} * fails due to invalid serial filter or serial filter factory properties. - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission */ - protected ObjectInputStream() throws IOException, SecurityException { + protected ObjectInputStream() throws IOException { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -599,12 +581,6 @@ protected Object readObjectOverride() * each object (regular or class) read to reconstruct the root object. * See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details. * - *

ObjectInputStream subclasses which override this method can only be - * constructed in security contexts possessing the - * "enableSubclassImplementation" SerializablePermission; any attempt to - * instantiate such a subclass without this permission will cause a - * SecurityException to be thrown. - * * @return reference to deserialized object * @throws ClassNotFoundException if class of an object to deserialize * cannot be found @@ -923,26 +899,11 @@ protected Object resolveObject(Object obj) throws IOException { * enabled, the {@link #resolveObject} method is called for every object being * deserialized. * - *

If object replacement is currently not enabled, and - * {@code enable} is true, and there is a security manager installed, - * this method first calls the security manager's - * {@code checkPermission} method with the - * {@code SerializablePermission("enableSubstitution")} permission to - * ensure that the caller is permitted to enable the stream to do replacement - * of objects read from the stream. - * * @param enable true for enabling use of {@code resolveObject} for * every object being deserialized * @return the previous setting before this method was invoked - * @throws SecurityException if a security manager exists and its - * {@code checkPermission} method denies enabling the stream - * to do replacement of objects read from the stream. - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission */ - protected boolean enableResolveObject(boolean enable) - throws SecurityException - { + protected boolean enableResolveObject(boolean enable) { if (enable == enableResolve) { return enable; } @@ -1341,8 +1302,6 @@ public final ObjectInputFilter getObjectInputFilter() { * is increased before reading an object. * * @param filter the filter, may be null - * @throws SecurityException if there is security manager and the - * {@code SerializablePermission("serialFilter")} is not granted * @throws IllegalStateException if an object has been read, * if the filter factory returns {@code null} when the * {@linkplain #getObjectInputFilter() current filter} is non-null, or diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java index bde069a1774d1..429c91ab673b1 100644 --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -237,16 +237,8 @@ protected Boolean computeValue(Class type) { * ensure that constructors for receiving ObjectInputStreams will not block * when reading the header. * - *

If a security manager is installed, this constructor will check for - * the "enableSubclassImplementation" SerializablePermission when invoked - * directly or indirectly by the constructor of a subclass which overrides - * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared - * methods. - * * @param out output stream to write to * @throws IOException if an I/O error occurs while writing stream header - * @throws SecurityException if untrusted subclass illegally overrides - * security-sensitive methods * @throws NullPointerException if {@code out} is {@code null} * @since 1.4 * @see ObjectOutputStream#ObjectOutputStream() @@ -274,19 +266,9 @@ public ObjectOutputStream(OutputStream out) throws IOException { * ObjectOutputStream to not have to allocate private data just used by * this implementation of ObjectOutputStream. * - *

If there is a security manager installed, this method first calls the - * security manager's {@code checkPermission} method with a - * {@code SerializablePermission("enableSubclassImplementation")} - * permission to ensure it's ok to enable subclassing. - * - * @throws SecurityException if a security manager exists and its - * {@code checkPermission} method denies enabling - * subclassing. * @throws IOException if an I/O error occurs while creating this stream - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission */ - protected ObjectOutputStream() throws IOException, SecurityException { + protected ObjectOutputStream() throws IOException { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -414,12 +396,6 @@ protected void writeObjectOverride(Object obj) throws IOException { * writeUnshared, and not to any transitively referenced sub-objects in the * object graph to be serialized. * - *

ObjectOutputStream subclasses which override this method can only be - * constructed in security contexts possessing the - * "enableSubclassImplementation" SerializablePermission; any attempt to - * instantiate such a subclass without this permission will cause a - * SecurityException to be thrown. - * * @param obj object to write to stream * @throws NotSerializableException if an object in the graph to be * serialized does not implement the Serializable interface @@ -611,26 +587,11 @@ protected Object replaceObject(Object obj) throws IOException { * enabled, the {@link #replaceObject} method is called for every object being * serialized. * - *

If object replacement is currently not enabled, and - * {@code enable} is true, and there is a security manager installed, - * this method first calls the security manager's - * {@code checkPermission} method with the - * {@code SerializablePermission("enableSubstitution")} permission to - * ensure that the caller is permitted to enable the stream to do replacement - * of objects written to the stream. - * * @param enable true for enabling use of {@code replaceObject} for * every object being serialized * @return the previous setting before this method was invoked - * @throws SecurityException if a security manager exists and its - * {@code checkPermission} method denies enabling the stream - * to do replacement of objects written to the stream. - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission */ - protected boolean enableReplaceObject(boolean enable) - throws SecurityException - { + protected boolean enableReplaceObject(boolean enable) { if (enable == enableReplace) { return enable; } diff --git a/src/java.base/share/classes/java/io/PrintStream.java b/src/java.base/share/classes/java/io/PrintStream.java index 35e2716dbd324..3096f2356f52f 100644 --- a/src/java.base/share/classes/java/io/PrintStream.java +++ b/src/java.base/share/classes/java/io/PrintStream.java @@ -249,10 +249,6 @@ public PrintStream(OutputStream out, boolean autoFlush, Charset charset) { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(fileName)} denies write - * access to the file * @see Charset#defaultCharset() * * @since 1.5 @@ -284,11 +280,6 @@ public PrintStream(String fileName) throws FileNotFoundException { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(fileName)} denies write - * access to the file - * * @throws UnsupportedEncodingException * If the named charset is not supported * @@ -320,11 +311,6 @@ public PrintStream(String fileName, String csn) * @throws IOException * if an I/O error occurs while opening or creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(fileName)} denies write - * access to the file - * * @since 10 */ public PrintStream(String fileName, Charset charset) throws IOException { @@ -351,10 +337,6 @@ public PrintStream(String fileName, Charset charset) throws IOException { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(file.getPath())} - * denies write access to the file * @see Charset#defaultCharset() * * @since 1.5 @@ -386,11 +368,6 @@ public PrintStream(File file) throws FileNotFoundException { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(file.getPath())} - * denies write access to the file - * * @throws UnsupportedEncodingException * If the named charset is not supported * @@ -423,11 +400,6 @@ public PrintStream(File file, String csn) * @throws IOException * if an I/O error occurs while opening or creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(file.getPath())} - * denies write access to the file - * * @since 10 */ public PrintStream(File file, Charset charset) throws IOException { diff --git a/src/java.base/share/classes/java/io/PrintWriter.java b/src/java.base/share/classes/java/io/PrintWriter.java index 2ed9315ba6a30..55baa2e0a5701 100644 --- a/src/java.base/share/classes/java/io/PrintWriter.java +++ b/src/java.base/share/classes/java/io/PrintWriter.java @@ -195,10 +195,6 @@ public PrintWriter(OutputStream out, boolean autoFlush, Charset charset) { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(fileName)} denies write - * access to the file * @see Charset#defaultCharset() * * @since 1.5 @@ -247,11 +243,6 @@ private PrintWriter(Charset charset, File file) * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(fileName)} denies write - * access to the file - * * @throws UnsupportedEncodingException * If the named charset is not supported * @@ -282,11 +273,6 @@ public PrintWriter(String fileName, String csn) * @throws IOException * if an I/O error occurs while opening or creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(fileName)} denies write - * access to the file - * * @since 10 */ public PrintWriter(String fileName, Charset charset) throws IOException { @@ -313,10 +299,6 @@ public PrintWriter(String fileName, Charset charset) throws IOException { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(file.getPath())} - * denies write access to the file * @see Charset#defaultCharset() * * @since 1.5 @@ -348,11 +330,6 @@ public PrintWriter(File file) throws FileNotFoundException { * created, or if some other error occurs while opening or * creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(file.getPath())} - * denies write access to the file - * * @throws UnsupportedEncodingException * If the named charset is not supported * @@ -383,11 +360,6 @@ public PrintWriter(File file, String csn) * @throws IOException * if an I/O error occurs while opening or creating the file * - * @throws SecurityException - * If a security manager is present and {@link - * SecurityManager#checkWrite checkWrite(file.getPath())} - * denies write access to the file - * * @since 10 */ public PrintWriter(File file, Charset charset) throws IOException { diff --git a/src/java.base/share/classes/java/io/RandomAccessFile.java b/src/java.base/share/classes/java/io/RandomAccessFile.java index cf8ae43dc2147..1487764ac4475 100644 --- a/src/java.base/share/classes/java/io/RandomAccessFile.java +++ b/src/java.base/share/classes/java/io/RandomAccessFile.java @@ -114,15 +114,6 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable { * specified for the {@code RandomAccessFile(File,String)} constructor. * - *

- * If there is a security manager, its {@code checkRead} method - * is called with the {@code pathname} argument - * as its argument to see if read access to the file is allowed. - * If the mode allows writing, the security manager's - * {@code checkWrite} method - * is also called with the {@code pathname} argument - * as its argument to see if write access to the file is allowed. - * * @param pathname the system-dependent pathname string * @param mode the access mode * @throws IllegalArgumentException if the mode argument is not equal @@ -135,13 +126,6 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable { * existing, writable regular file and a new regular file of * that pathname cannot be created, or if some other error * occurs while opening or creating the file - * @throws SecurityException if a security manager exists and its - * {@code checkRead} method denies read access to the file - * or the mode is {@code "rw"} and the security manager's - * {@code checkWrite} method denies write access to the file - * @see java.lang.SecurityException - * @see java.lang.SecurityManager#checkRead(java.lang.String) - * @see java.lang.SecurityManager#checkWrite(java.lang.String) */ public RandomAccessFile(String pathname, String mode) throws FileNotFoundException @@ -205,13 +189,6 @@ public RandomAccessFile(String pathname, String mode) * updates to both the file's content and its metadata to be written, which * generally requires at least one more low-level I/O operation. * - *

If there is a security manager, its {@code checkRead} method is - * called with the pathname of the {@code file} argument as its - * argument to see if read access to the file is allowed. If the mode - * allows writing, the security manager's {@code checkWrite} method is - * also called with the pathname of the {@code file} argument to see if - * write access to the file is allowed. - * * @param file the file object * @param mode the access mode, as described * above @@ -225,12 +202,6 @@ public RandomAccessFile(String pathname, String mode) * an existing, writable regular file and a new regular file of * that pathname cannot be created, or if some other error * occurs while opening or creating the file - * @throws SecurityException if a security manager exists and its - * {@code checkRead} method denies read access to the file - * or the mode is {@code "rw"} and the security manager's - * {@code checkWrite} method denies write access to the file - * @see java.lang.SecurityManager#checkRead(java.lang.String) - * @see java.lang.SecurityManager#checkWrite(java.lang.String) * @see java.nio.channels.FileChannel#force(boolean) */ @SuppressWarnings("this-escape") diff --git a/src/java.base/share/classes/java/io/SerializablePermission.java b/src/java.base/share/classes/java/io/SerializablePermission.java index 914b5b13dc3bd..1e617e173a755 100644 --- a/src/java.base/share/classes/java/io/SerializablePermission.java +++ b/src/java.base/share/classes/java/io/SerializablePermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,55 +36,9 @@ * no actions list; you either have the named permission * or you don't. * - *

- * The target name is the name of the Serializable permission (see below). - * - *

- * The following table lists the standard {@code SerializablePermission} target names, - * and for each provides a description of what the permission allows - * and a discussion of the risks of granting code the permission. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Permission target name, what the permission allows, and associated risks
Permission Target NameWhat the Permission AllowsRisks of Allowing this Permission
enableSubclassImplementationSubclass implementation of ObjectOutputStream or ObjectInputStream - * to override the default serialization or deserialization, respectively, - * of objectsCode can use this to serialize or - * deserialize classes in a purposefully malfeasant manner. For example, - * during serialization, malicious code can use this to - * purposefully store confidential private field data in a way easily accessible - * to attackers. Or, during deserialization it could, for example, deserialize - * a class with all its private fields zeroed out.
enableSubstitutionSubstitution of one object for another during - * serialization or deserializationThis is dangerous because malicious code - * can replace the actual object with one which has incorrect or - * malignant data.
serialFilterSetting a filter for ObjectInputStreams.Code could remove a configured filter and remove protections - * already established.
+ * @apiNote + * This permission cannot be used for controlling access to resources + * as the Security Manager is no longer supported. * * @see java.security.BasicPermission * @see java.security.Permission @@ -92,7 +46,6 @@ * @see java.security.PermissionCollection * @see java.lang.SecurityManager * - * * @author Joe Fialli * @since 1.2 */ diff --git a/src/java.base/share/classes/java/lang/Boolean.java b/src/java.base/share/classes/java/lang/Boolean.java index 31507f60e3782..f5c643881a883 100644 --- a/src/java.base/share/classes/java/lang/Boolean.java +++ b/src/java.base/share/classes/java/lang/Boolean.java @@ -271,8 +271,6 @@ public boolean equals(Object obj) { * * @param name the system property name. * @return the {@code boolean} value of the system property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see java.lang.System#getProperty(java.lang.String) * @see java.lang.System#getProperty(java.lang.String, java.lang.String) */ diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 2edf0a9169e86..bb091235646bd 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -530,11 +530,6 @@ private static Class forName(String className, Class caller) * by this method fails * @throws ClassNotFoundException if the class cannot be located by * the specified class loader - * @throws SecurityException - * if a security manager is present, and the {@code loader} is - * {@code null}, and the caller's class loader is not - * {@code null}, and the caller does not have the - * {@link RuntimePermission}{@code ("getClassLoader")} * * @see java.lang.Class#forName(String) * @see java.lang.ClassLoader @@ -610,8 +605,6 @@ private static native Class forName0(String name, boolean initialize, * a binary name. This method returns {@code null} on failure rather than * throwing a {@link ClassNotFoundException}, as is done by * the {@link #forName(String, boolean, ClassLoader)} method. - * The security check is a stack-based permission check if the caller - * loads a class in another module. * * @param module A module * @param name The {@linkplain ClassLoader##binary-name binary name} @@ -623,16 +616,6 @@ private static native Class forName0(String name, boolean initialize, * * @throws LinkageError if the linkage fails * - * @throws SecurityException - *

    - *
  • if the caller is not the specified module and - * {@code RuntimePermission("getClassLoader")} permission is denied; or
  • - *
  • access to the module content is denied. For example, - * permission check will be performed when a class loader calls - * {@link ModuleReader#open(String)} to read the bytes of a class file - * in a module.
  • - *
- * * @jls 12.2 Loading of Classes and Interfaces * @jls 12.3 Linking of Classes and Interfaces * @since 9 @@ -756,13 +739,6 @@ public static Class forPrimitiveName(String primitiveName) { * or if the instantiation fails for some other reason. * @throws ExceptionInInitializerError if the initialization * provoked by this method fails. - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. */ @SuppressWarnings("removal") @CallerSensitive @@ -1057,15 +1033,7 @@ public String getName() { * * @return the class loader that loaded the class or interface * represented by this {@code Class} object. - * @throws SecurityException - * if a security manager is present, and the caller's class loader - * is not {@code null} and is not the same as or an ancestor of the - * class loader for the class whose class loader is requested, - * and the caller does not have the - * {@link RuntimePermission}{@code ("getClassLoader")} * @see java.lang.ClassLoader - * @see SecurityManager#checkPermission - * @see java.lang.RuntimePermission */ @CallerSensitive @ForceInline // to ensure Reflection.getCallerClass optimization @@ -1541,30 +1509,10 @@ void setSigners(Object[] signers) { * @return the immediately enclosing method of the underlying class, if * that class is a local or anonymous class; otherwise {@code null}. * - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of the enclosing class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the methods within the enclosing class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the enclosing class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of the enclosing class - * - *
* @since 1.5 */ @CallerSensitive - public Method getEnclosingMethod() throws SecurityException { + public Method getEnclosingMethod() { EnclosingMethodInfo enclosingInfo = getEnclosingMethodInfo(); if (enclosingInfo == null) @@ -1697,30 +1645,11 @@ private static Class toClass(Type o) { * * @return the immediately enclosing constructor of the underlying class, if * that class is a local or anonymous class; otherwise {@code null}. - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of the enclosing class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the constructors within the enclosing class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the enclosing class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of the enclosing class - * - *
+ * * @since 1.5 */ @CallerSensitive - public Constructor getEnclosingConstructor() throws SecurityException { + public Constructor getEnclosingConstructor() { EnclosingMethodInfo enclosingInfo = getEnclosingMethodInfo(); if (enclosingInfo == null) @@ -1777,16 +1706,10 @@ public Constructor getEnclosingConstructor() throws SecurityException { * type, or void, then this method returns null. * * @return the declaring class for this class - * @throws SecurityException - * If a security manager, s, is present and the caller's - * class loader is not the same as or an ancestor of the class - * loader for the declaring class and invocation of {@link - * SecurityManager#checkPackageAccess s.checkPackageAccess()} - * denies access to the package of the declaring class * @since 1.1 */ @CallerSensitive - public Class getDeclaringClass() throws SecurityException { + public Class getDeclaringClass() { final Class candidate = getDeclaringClass0(); if (candidate != null) { @@ -1808,16 +1731,10 @@ public Class getDeclaringClass() throws SecurityException { * class. If the underlying class is a top level class this * method returns {@code null}. * @return the immediately enclosing class of the underlying class - * @throws SecurityException - * If a security manager, s, is present and the caller's - * class loader is not the same as or an ancestor of the class - * loader for the enclosing class and invocation of {@link - * SecurityManager#checkPackageAccess s.checkPackageAccess()} - * denies access to the package of the enclosing class * @since 1.5 */ @CallerSensitive - public Class getEnclosingClass() throws SecurityException { + public Class getEnclosingClass() { // There are five kinds of classes (or interfaces): // a) Top level classes // b) Nested classes (static member classes) @@ -2072,14 +1989,6 @@ private boolean hasEnclosingMethodInfo() { * * @return the array of {@code Class} objects representing the public * members of this class - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. - * * @since 1.1 */ @SuppressWarnings("removal") @@ -2140,20 +2049,13 @@ public Class[] run() { * * @return the array of {@code Field} objects representing the * public fields - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. * * @since 1.1 * @jls 8.2 Class Members * @jls 8.3 Field Declarations */ @CallerSensitive - public Field[] getFields() throws SecurityException { + public Field[] getFields() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2231,20 +2133,13 @@ public Field[] getFields() throws SecurityException { * * @return the array of {@code Method} objects representing the * public methods of this class - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. * * @jls 8.2 Class Members * @jls 8.4 Method Declarations * @since 1.1 */ @CallerSensitive - public Method[] getMethods() throws SecurityException { + public Method[] getMethods() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2274,19 +2169,12 @@ public Method[] getMethods() throws SecurityException { * * @return the array of {@code Constructor} objects representing the * public constructors of this class - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. * * @see #getDeclaredConstructors() * @since 1.1 */ @CallerSensitive - public Constructor[] getConstructors() throws SecurityException { + public Constructor[] getConstructors() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2326,21 +2214,13 @@ public Constructor[] getConstructors() throws SecurityException { * @throws NoSuchFieldException if a field with the specified name is * not found. * @throws NullPointerException if {@code name} is {@code null} - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. * * @since 1.1 * @jls 8.2 Class Members * @jls 8.3 Field Declarations */ @CallerSensitive - public Field getField(String name) - throws NoSuchFieldException, SecurityException { + public Field getField(String name) throws NoSuchFieldException { Objects.requireNonNull(name); @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); @@ -2437,13 +2317,6 @@ public Field getField(String name) * or if the name is {@value ConstantDescs#INIT_NAME} or * {@value ConstantDescs#CLASS_INIT_NAME}. * @throws NullPointerException if {@code name} is {@code null} - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. * * @jls 8.2 Class Members * @jls 8.4 Method Declarations @@ -2451,7 +2324,7 @@ public Field getField(String name) */ @CallerSensitive public Method getMethod(String name, Class... parameterTypes) - throws NoSuchMethodException, SecurityException { + throws NoSuchMethodException { Objects.requireNonNull(name); @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); @@ -2486,21 +2359,13 @@ public Method getMethod(String name, Class... parameterTypes) * @throws NoSuchMethodException if a matching constructor is not found, * including when this {@code Class} object represents * an interface, a primitive type, an array class, or void. - * @throws SecurityException - * If a security manager, s, is present and - * the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class. - * - * @see #getDeclaredConstructor(Class[]) + * + * @see #getDeclaredConstructor(Class[]) * @since 1.1 */ @CallerSensitive public Constructor getConstructor(Class... parameterTypes) - throws NoSuchMethodException, SecurityException - { + throws NoSuchMethodException { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2523,32 +2388,12 @@ public Constructor getConstructor(Class... parameterTypes) * * @return the array of {@code Class} objects representing all the * declared members of this class - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared classes within this class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @since 1.1 * @jls 8.5 Member Class and Interface Declarations */ @CallerSensitive - public Class[] getDeclaredClasses() throws SecurityException { + public Class[] getDeclaredClasses() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2575,33 +2420,13 @@ public Class[] getDeclaredClasses() throws SecurityException { * * @return the array of {@code Field} objects representing all the * declared fields of this class - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared fields within this class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @since 1.1 * @jls 8.2 Class Members * @jls 8.3 Field Declarations */ @CallerSensitive - public Field[] getDeclaredFields() throws SecurityException { + public Field[] getDeclaredFields() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2638,26 +2463,6 @@ public Field[] getDeclaredFields() throws SecurityException { * @return An array of {@code RecordComponent} objects representing all the * record components of this record class, or {@code null} if this * class is not a record class - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared methods within this class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @jls 8.10 Record Classes * @since 16 @@ -2706,26 +2511,6 @@ public RecordComponent[] getRecordComponents() { * * @return the array of {@code Method} objects representing all the * declared methods of this class - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared methods within this class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @jls 8.2 Class Members * @jls 8.4 Method Declarations @@ -2735,7 +2520,7 @@ public RecordComponent[] getRecordComponents() { * @since 1.1 */ @CallerSensitive - public Method[] getDeclaredMethods() throws SecurityException { + public Method[] getDeclaredMethods() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2760,33 +2545,13 @@ public Method[] getDeclaredMethods() throws SecurityException { * * @return the array of {@code Constructor} objects representing all the * declared constructors of this class - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared constructors within this class - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @since 1.1 * @see #getConstructors() * @jls 8.8 Constructor Declarations */ @CallerSensitive - public Constructor[] getDeclaredConstructors() throws SecurityException { + public Constructor[] getDeclaredConstructors() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -2811,34 +2576,13 @@ public Constructor[] getDeclaredConstructors() throws SecurityException { * @throws NoSuchFieldException if a field with the specified name is * not found. * @throws NullPointerException if {@code name} is {@code null} - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared field - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @since 1.1 * @jls 8.2 Class Members * @jls 8.3 Field Declarations */ @CallerSensitive - public Field getDeclaredField(String name) - throws NoSuchFieldException, SecurityException { + public Field getDeclaredField(String name) throws NoSuchFieldException { Objects.requireNonNull(name); @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); @@ -2877,26 +2621,6 @@ public Field getDeclaredField(String name) * matching the specified name and parameters * @throws NoSuchMethodException if a matching method is not found. * @throws NullPointerException if {@code name} is {@code null} - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared method - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class - * - *
* * @jls 8.2 Class Members * @jls 8.4 Method Declarations @@ -2904,7 +2628,7 @@ public Field getDeclaredField(String name) */ @CallerSensitive public Method getDeclaredMethod(String name, Class... parameterTypes) - throws NoSuchMethodException, SecurityException { + throws NoSuchMethodException { Objects.requireNonNull(name); @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); @@ -2975,34 +2699,13 @@ Method findMethod(boolean publicOnly, String name, Class... parameterTypes) { * @throws NoSuchMethodException if a matching constructor is not found, * including when this {@code Class} object represents * an interface, a primitive type, an array class, or void. - * @throws SecurityException - * If a security manager, s, is present and any of the - * following conditions is met: - * - *
    - * - *
  • the caller's class loader is not the same as the - * class loader of this class and invocation of - * {@link SecurityManager#checkPermission - * s.checkPermission} method with - * {@code RuntimePermission("accessDeclaredMembers")} - * denies access to the declared constructor - * - *
  • the caller's class loader is not the same as or an - * ancestor of the class loader for the current class and - * invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package - * of this class * - *
- * - * @see #getConstructor(Class[]) + * @see #getConstructor(Class[]) * @since 1.1 */ @CallerSensitive public Constructor getDeclaredConstructor(Class... parameterTypes) - throws NoSuchMethodException, SecurityException - { + throws NoSuchMethodException { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -3058,10 +2761,9 @@ public Constructor getDeclaredConstructor(Class... parameterTypes) * * @param name name of the desired resource * @return A {@link java.io.InputStream} object; {@code null} if no - * resource with this name is found, the resource is in a package + * resource with this name is found, or the resource is in a package * that is not {@linkplain Module#isOpen(String, Module) open} to at - * least the caller module, or access to the resource is denied - * by the security manager. + * least the caller module. * @throws NullPointerException If {@code name} is {@code null} * * @see Module#getResourceAsStream(String) @@ -3154,11 +2856,10 @@ public InputStream getResourceAsStream(String name) { * * @param name name of the desired resource * @return A {@link java.net.URL} object; {@code null} if no resource with - * this name is found, the resource cannot be located by a URL, the + * this name is found, the resource cannot be located by a URL, or the * resource is in a package that is not * {@linkplain Module#isOpen(String, Module) open} to at least the caller - * module, or access to the resource is denied by the security - * manager. + * module. * @throws NullPointerException If {@code name} is {@code null} * @since 1.1 */ @@ -3224,23 +2925,11 @@ private boolean isOpenToCaller(String name, Class caller) { } /** - * Returns the {@code ProtectionDomain} of this class. If there is a - * security manager installed, this method first calls the security - * manager's {@code checkPermission} method with a - * {@code RuntimePermission("getProtectionDomain")} permission to - * ensure it's ok to get the - * {@code ProtectionDomain}. + * Returns the {@code ProtectionDomain} of this class. * * @return the ProtectionDomain of this class * - * @throws SecurityException - * if a security manager exists and its - * {@code checkPermission} method doesn't allow - * getting the ProtectionDomain. - * * @see java.security.ProtectionDomain - * @see SecurityManager#checkPermission - * @see java.lang.RuntimePermission * @since 1.2 */ public ProtectionDomain getProtectionDomain() { @@ -4466,13 +4155,6 @@ public AnnotatedType[] getAnnotatedInterfaces() { * * @return the nest host of this class or interface * - * @throws SecurityException - * If the returned class is not the current class, and - * if a security manager, s, is present and the caller's - * class loader is not the same as or an ancestor of the class - * loader for the returned class and invocation of {@link - * SecurityManager#checkPackageAccess s.checkPackageAccess()} - * denies access to the package of the returned class * @since 11 * @jvms 4.7.28 The {@code NestHost} Attribute * @jvms 4.7.29 The {@code NestMembers} Attribute @@ -4557,14 +4239,6 @@ public boolean isNestmateOf(Class c) { * @return an array of all classes and interfaces in the same nest as * this class or interface * - * @throws SecurityException - * If any returned class is not the current class, and - * if a security manager, s, is present and the caller's - * class loader is not the same as or an ancestor of the class - * loader for that returned class and invocation of {@link - * SecurityManager#checkPackageAccess s.checkPackageAccess()} - * denies access to the package of that returned class - * * @since 11 * @see #getNestHost() * @jvms 4.7.28 The {@code NestHost} Attribute @@ -4751,15 +4425,8 @@ public Optional describeConstable() { * cannot be obtained, it is silently ignored, and not included in the result * array. * - * @return an array of {@code Class} objects of the permitted subclasses of this class or interface, - * or {@code null} if this class or interface is not sealed. - * - * @throws SecurityException - * If a security manager, s, is present and the caller's - * class loader is not the same as or an ancestor of the class - * loader for that returned class and invocation of {@link - * SecurityManager#checkPackageAccess s.checkPackageAccess()} - * denies access to the package of any class in the returned array. + * @return an array of {@code Class} objects of the permitted subclasses of this class + * or interface, or {@code null} if this class or interface is not sealed. * * @jls 8.1 Class Declarations * @jls 9.1 Interface Declarations diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index fafa8895ee667..85fc315c76715 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -33,10 +33,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URL; -import java.security.AccessController; -import java.security.AccessControlContext; import java.security.CodeSource; -import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; import java.util.ArrayList; @@ -67,7 +64,6 @@ import jdk.internal.reflect.CallerSensitiveAdapter; import jdk.internal.reflect.Reflection; import jdk.internal.util.StaticProperty; -import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; /** @@ -93,9 +89,6 @@ * extend the manner in which the Java virtual machine dynamically loads * classes. * - *

Class loaders may typically be used by security managers to indicate - * security domains. - * *

In addition to loading classes, a class loader is also responsible for * locating resources. A resource is some data (a "{@code .class}" file, * configuration data, or an image for example) that is identified with an @@ -424,11 +417,6 @@ String nameAndId() { * * @throws IllegalArgumentException if the given name is empty. * - * @throws SecurityException - * If a security manager exists and its - * {@link SecurityManager#checkCreateClassLoader()} - * method doesn't allow creation of a new class loader. - * * @since 9 */ @SuppressWarnings("this-escape") @@ -440,10 +428,6 @@ protected ClassLoader(String name, ClassLoader parent) { * Creates a new class loader using the specified parent class loader for * delegation. * - *

If there is a security manager, its {@link - * SecurityManager#checkCreateClassLoader() checkCreateClassLoader} method - * is invoked. This may result in a security exception.

- * * @apiNote If the parent is specified as {@code null} (for the * bootstrap class loader) then there is no guarantee that all platform * classes are visible. @@ -451,11 +435,6 @@ protected ClassLoader(String name, ClassLoader parent) { * @param parent * The parent class loader * - * @throws SecurityException - * If a security manager exists and its - * {@code checkCreateClassLoader} method doesn't allow creation - * of a new class loader. - * * @since 1.2 */ @SuppressWarnings("this-escape") @@ -467,16 +446,6 @@ protected ClassLoader(ClassLoader parent) { * Creates a new class loader using the {@code ClassLoader} returned by * the method {@link #getSystemClassLoader() * getSystemClassLoader()} as the parent class loader. - * - *

If there is a security manager, its {@link - * SecurityManager#checkCreateClassLoader() - * checkCreateClassLoader} method is invoked. This may result in - * a security exception.

- * - * @throws SecurityException - * If a security manager exists and its - * {@code checkCreateClassLoader} method doesn't allow creation - * of a new class loader. */ @SuppressWarnings("this-escape") protected ClassLoader() { @@ -688,30 +657,6 @@ protected Object getClassLoadingLock(String className) { return lock; } - // Invoked by the VM after loading class with this loader. - @SuppressWarnings("removal") - private void checkPackageAccess(Class cls, ProtectionDomain pd) { - final SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - if (ReflectUtil.isNonPublicProxyClass(cls)) { - for (Class intf: cls.getInterfaces()) { - checkPackageAccess(intf, pd); - } - return; - } - - final String packageName = cls.getPackageName(); - if (!packageName.isEmpty()) { - AccessController.doPrivileged(new PrivilegedAction<>() { - public Void run() { - sm.checkPackageAccess(packageName); - return null; - } - }, new AccessControlContext(new ProtectionDomain[] {pd})); - } - } - } - /** * Finds the class with the specified binary name. * This method should be overridden by class loader implementations that @@ -823,12 +768,10 @@ protected final Class defineClass(byte[] b, int off, int len) * Before the {@code Class} can be used it must be resolved. * *

This method assigns a default {@link java.security.ProtectionDomain - * ProtectionDomain} to the newly defined class. The - * {@code ProtectionDomain} is effectively granted the same set of - * permissions returned when {@link - * java.security.Policy#getPermissions(java.security.CodeSource) - * Policy.getPolicy().getPermissions(new CodeSource(null, null))} - * is invoked. The default protection domain is created on the first invocation + * ProtectionDomain} to the newly defined class. The + * {@code getPermissions} method of the {@code ProtectionDomain} always + * returns {@code null}. + * The default protection domain is created on the first invocation * of {@link #defineClass(String, byte[], int, int) defineClass}, * and re-used on subsequent invocations. * @@ -1342,8 +1285,7 @@ protected final void setSigners(Class c, Object[] signers) { * The resource name * * @return A URL to the resource; {@code null} if the resource could not be - * found, a URL could not be constructed to locate the resource, - * access to the resource is denied by the security manager, or + * found, a URL could not be constructed to locate the resource, or * there isn't a module of the given name defined to the class * loader. * @@ -1395,9 +1337,8 @@ protected URL findResource(String moduleName, String name) throws IOException { * * @return {@code URL} object for reading the resource; {@code null} if * the resource could not be found, a {@code URL} could not be - * constructed to locate the resource, the resource is in a package - * that is not opened unconditionally, or access to the resource is - * denied by the security manager. + * constructed to locate the resource, or the resource is in a package + * that is not opened unconditionally. * * @throws NullPointerException If {@code name} is {@code null} * @@ -1457,9 +1398,8 @@ public URL getResource(String name) { * @return An enumeration of {@link java.net.URL URL} objects for the * resource. If no resources could be found, the enumeration will * be empty. Resources for which a {@code URL} cannot be - * constructed, are in a package that is not opened - * unconditionally, or access to the resource is denied by the - * security manager, are not returned in the enumeration. + * constructed, or are in a package that is not opened + * unconditionally, are not returned in the enumeration. * * @throws IOException * If I/O errors occur @@ -1518,9 +1458,8 @@ public Enumeration getResources(String name) throws IOException { * * @return A stream of resource {@link java.net.URL URL} objects. If no * resources could be found, the stream will be empty. Resources - * for which a {@code URL} cannot be constructed, are in a package - * that is not opened unconditionally, or access to the resource - * is denied by the security manager, will not be in the stream. + * for which a {@code URL} cannot be constructed, or are in a package + * that is not opened unconditionally, will not be in the stream. * * @throws NullPointerException If {@code name} is {@code null} * @@ -1558,9 +1497,8 @@ public Stream resources(String name) { * * @return {@code URL} object for reading the resource; {@code null} if * the resource could not be found, a {@code URL} could not be - * constructed to locate the resource, the resource is in a package - * that is not opened unconditionally, or access to the resource is - * denied by the security manager. + * constructed to locate the resource, or the resource is in a package + * that is not opened unconditionally. * * @since 1.2 */ @@ -1589,8 +1527,7 @@ protected URL findResource(String name) { * @return An enumeration of {@link java.net.URL URL} objects for * the resource. If no resources could be found, the enumeration * will be empty. Resources for which a {@code URL} cannot be - * constructed, are in a package that is not opened unconditionally, - * or access to the resource is denied by the security manager, + * constructed, or are in a package that is not opened unconditionally, * are not returned in the enumeration. * * @throws IOException @@ -1676,9 +1613,8 @@ public final boolean isRegisteredAsParallelCapable() { * * @return A {@link java.net.URL URL} to the resource; {@code * null} if the resource could not be found, a URL could not be - * constructed to locate the resource, the resource is in a package - * that is not opened unconditionally or access to the resource is - * denied by the security manager. + * constructed to locate the resource, or the resource is in a package + * that is not opened unconditionally. * * @since 1.1 */ @@ -1708,8 +1644,7 @@ public static URL getSystemResource(String name) { * @return An enumeration of {@link java.net.URL URL} objects for * the resource. If no resources could be found, the enumeration * will be empty. Resources for which a {@code URL} cannot be - * constructed, are in a package that is not opened unconditionally, - * or access to the resource is denied by the security manager, + * constructed, or are in a package that is not opened unconditionally, * are not returned in the enumeration. * * @throws IOException @@ -1740,9 +1675,8 @@ public static Enumeration getSystemResources(String name) * The resource name * * @return An input stream for reading the resource; {@code null} if the - * resource could not be found, the resource is in a package that - * is not opened unconditionally, or access to the resource is - * denied by the security manager. + * resource could not be found, or the resource is in a package that + * is not opened unconditionally. * * @throws NullPointerException If {@code name} is {@code null} * @@ -1774,9 +1708,8 @@ public InputStream getResourceAsStream(String name) { * The resource name * * @return An input stream for reading the resource; {@code null} if the - * resource could not be found, the resource is in a package that - * is not opened unconditionally, or access to the resource is - * denied by the security manager. + * resource could not be found, or the resource is in a package that + * is not opened unconditionally. * * @since 1.1 */ @@ -1800,12 +1733,6 @@ public static InputStream getSystemResourceAsStream(String name) { * * @return The parent {@code ClassLoader} * - * @throws SecurityException - * If a security manager is present, and the caller's class loader - * is not {@code null} and is not an ancestor of this class loader, - * and the caller does not have the - * {@link RuntimePermission}{@code ("getClassLoader")} - * * @since 1.2 */ @CallerSensitive @@ -1845,13 +1772,6 @@ public final Module getUnnamedModule() { * * @return The platform {@code ClassLoader}. * - * @throws SecurityException - * If a security manager is present, and the caller's class loader is - * not {@code null}, and the caller's class loader is not the same - * as or an ancestor of the platform class loader, - * and the caller does not have the - * {@link RuntimePermission}{@code ("getClassLoader")} - * * @since 9 */ @CallerSensitive @@ -1920,12 +1840,6 @@ public static ClassLoader getPlatformClassLoader() { * * @return The system {@code ClassLoader} * - * @throws SecurityException - * If a security manager is present, and the caller's class loader - * is not {@code null} and is not the same as or an ancestor of the - * system class loader, and the caller does not have the - * {@link RuntimePermission}{@code ("getClassLoader")} - * * @throws IllegalStateException * If invoked recursively during the construction of the class * loader specified by the "{@code java.system.class.loader}" diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index e666e977c61a5..8bf573733f2ce 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -1187,8 +1187,6 @@ public boolean equals(Object obj) { * * @param nm property name. * @return the {@code Integer} value of the property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see java.lang.System#getProperty(java.lang.String) * @see java.lang.System#getProperty(java.lang.String, java.lang.String) */ @@ -1233,8 +1231,6 @@ public static Integer getInteger(String nm) { * @param nm property name. * @param val default value. * @return the {@code Integer} value of the property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see java.lang.System#getProperty(java.lang.String) * @see java.lang.System#getProperty(java.lang.String, java.lang.String) */ @@ -1275,8 +1271,6 @@ public static Integer getInteger(String nm, int val) { * @param nm property name. * @param val default value. * @return the {@code Integer} value of the property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see System#getProperty(java.lang.String) * @see System#getProperty(java.lang.String, java.lang.String) */ diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 8c083b3ec8431..e67b751470e48 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -1276,8 +1276,6 @@ public boolean equals(Object obj) { * * @param nm property name. * @return the {@code Long} value of the property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see java.lang.System#getProperty(java.lang.String) * @see java.lang.System#getProperty(java.lang.String, java.lang.String) */ @@ -1321,8 +1319,6 @@ public static Long getLong(String nm) { * @param nm property name. * @param val default value. * @return the {@code Long} value of the property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see java.lang.System#getProperty(java.lang.String) * @see java.lang.System#getProperty(java.lang.String, java.lang.String) */ @@ -1370,8 +1366,6 @@ public static Long getLong(String nm, long val) { * @param nm property name. * @param val default value. * @return the {@code Long} value of the property. - * @throws SecurityException for the same reasons as - * {@link System#getProperty(String) System.getProperty} * @see System#getProperty(java.lang.String) * @see System#getProperty(java.lang.String, java.lang.String) */ diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index 4d4593b31974f..a90fbc992602b 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -195,15 +195,7 @@ public String getName() { /** * Returns the {@code ClassLoader} for this module. * - *

If there is a security manager then its {@code checkPermission} - * method if first called with a {@code RuntimePermission("getClassLoader")} - * permission to check that the caller is allowed to get access to the - * class loader.

- * * @return The class loader for this module - * - * @throws SecurityException - * If denied by the security manager */ public ClassLoader getClassLoader() { @SuppressWarnings("removal") @@ -1689,9 +1681,8 @@ protected Class loadClass(String cn, boolean resolve) * with the name "{@code META-INF/MANIFEST.MF}" is never encapsulated * because "{@code META-INF}" is not a legal package name.

* - *

This method returns {@code null} if the resource is not in this - * module, the resource is encapsulated and cannot be located by the caller, - * or access to the resource is denied by the security manager.

+ *

This method returns {@code null} if the resource is not in this module + * or the resource is encapsulated and cannot be located by the caller.

* * @param name * The resource name diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java index 80d392470cb33..4ee2b02414dee 100644 --- a/src/java.base/share/classes/java/lang/ModuleLayer.java +++ b/src/java.base/share/classes/java/lang/ModuleLayer.java @@ -359,10 +359,6 @@ public Controller enableNativeAccess(Module target) { * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModulesWithOneLoader} method - * @throws SecurityException - * If {@code RuntimePermission("createClassLoader")} or - * {@code RuntimePermission("getClassLoader")} is denied by - * the security manager * * @see #findLoader */ @@ -401,10 +397,6 @@ public ModuleLayer defineModulesWithOneLoader(Configuration cf, * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModulesWithManyLoaders} method - * @throws SecurityException - * If {@code RuntimePermission("createClassLoader")} or - * {@code RuntimePermission("getClassLoader")} is denied by - * the security manager * * @see #findLoader */ @@ -440,9 +432,6 @@ public ModuleLayer defineModulesWithManyLoaders(Configuration cf, * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModules} method - * @throws SecurityException - * If {@code RuntimePermission("getClassLoader")} is denied by - * the security manager */ public ModuleLayer defineModules(Configuration cf, Function clf) { @@ -490,10 +479,6 @@ public ModuleLayer defineModules(Configuration cf, * a module named "{@code java.base}", or a module contains a package named * "{@code java}" or a package with a name starting with "{@code java.}".

* - *

If there is a security manager then the class loader created by - * this method will load classes and resources with privileges that are - * restricted by the calling context of this method.

- * * @param cf * The configuration for the layer * @param parentLayers @@ -510,10 +495,6 @@ public ModuleLayer defineModules(Configuration cf, * @throws LayerInstantiationException * If all modules cannot be defined to the same class loader for any * of the reasons listed above - * @throws SecurityException - * If {@code RuntimePermission("createClassLoader")} or - * {@code RuntimePermission("getClassLoader")} is denied by - * the security manager * * @see #findLoader */ @@ -563,10 +544,6 @@ public static Controller defineModulesWithOneLoader(Configuration cf, * methods) in the module defined to the class loader before searching * the parent class loader.

* - *

If there is a security manager then the class loaders created by - * this method will load classes and resources with privileges that are - * restricted by the calling context of this method.

- * * @param cf * The configuration for the layer * @param parentLayers @@ -586,11 +563,6 @@ public static Controller defineModulesWithOneLoader(Configuration cf, * named "{@code java}" or a package with a name starting with * "{@code java.}" * - * @throws SecurityException - * If {@code RuntimePermission("createClassLoader")} or - * {@code RuntimePermission("getClassLoader")} is denied by - * the security manager - * * @see #findLoader */ public static Controller defineModulesWithManyLoaders(Configuration cf, @@ -673,9 +645,6 @@ public static Controller defineModulesWithManyLoaders(Configuration cf, * configuration of the parent layers, including order * @throws LayerInstantiationException * If creating the layer fails for any of the reasons listed above - * @throws SecurityException - * If {@code RuntimePermission("getClassLoader")} is denied by - * the security manager */ public static Controller defineModules(Configuration cf, List parentLayers, @@ -906,11 +875,6 @@ boolean addEnableNativeAccess(String name) { * parent} layers are searched in the manner specified by {@link * #findModule(String) findModule}. * - *

If there is a security manager then its {@code checkPermission} - * method is called with a {@code RuntimePermission("getClassLoader")} - * permission to check that the caller is allowed to get access to the - * class loader.

- * * @apiNote This method does not return an {@code Optional} * because `null` must be used to represent the bootstrap class loader. * @@ -921,8 +885,6 @@ boolean addEnableNativeAccess(String name) { * * @throws IllegalArgumentException if a module of the given name is not * defined in this layer or any parent of this layer - * - * @throws SecurityException if denied by the security manager */ public ClassLoader findLoader(String name) { Optional om = findModule(name); diff --git a/src/java.base/share/classes/java/lang/Process.java b/src/java.base/share/classes/java/lang/Process.java index af6753a236bb2..3e4837d2e0253 100644 --- a/src/java.base/share/classes/java/lang/Process.java +++ b/src/java.base/share/classes/java/lang/Process.java @@ -753,8 +753,7 @@ public boolean isReleasable() { * * {@code Process} objects returned by {@link ProcessBuilder#start()} and * {@link Runtime#exec} implement {@code toHandle} as the equivalent of - * {@link ProcessHandle#of(long) ProcessHandle.of(pid)} including the - * check for a SecurityManager and {@code RuntimePermission("manageProcess")}. + * {@link ProcessHandle#of(long) ProcessHandle.of(pid)}. * * @implSpec * This implementation throws an instance of @@ -766,8 +765,6 @@ public boolean isReleasable() { * @return Returns a ProcessHandle for the Process * @throws UnsupportedOperationException if the Process implementation * does not support this operation - * @throws SecurityException if a security manager has been installed and - * it denies RuntimePermission("manageProcess") * @since 9 */ public ProcessHandle toHandle() { @@ -811,8 +808,6 @@ public ProcessHandle.Info info() { * direct children of the process * @throws UnsupportedOperationException if the Process implementation * does not support this operation - * @throws SecurityException if a security manager has been installed and - * it denies RuntimePermission("manageProcess") * @since 9 */ public Stream children() { @@ -837,8 +832,6 @@ public Stream children() { * are descendants of the process * @throws UnsupportedOperationException if the Process implementation * does not support this operation - * @throws SecurityException if a security manager has been installed and - * it denies RuntimePermission("manageProcess") * @since 9 */ public Stream descendants() { diff --git a/src/java.base/share/classes/java/lang/ProcessBuilder.java b/src/java.base/share/classes/java/lang/ProcessBuilder.java index e7d5d9debef69..c3cb9bfd14515 100644 --- a/src/java.base/share/classes/java/lang/ProcessBuilder.java +++ b/src/java.base/share/classes/java/lang/ProcessBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -329,23 +329,12 @@ public List command() { * *

The returned map is typically case-sensitive on all platforms. * - *

If a security manager exists, its - * {@link SecurityManager#checkPermission checkPermission} method - * is called with a - * {@link RuntimePermission}{@code ("getenv.*")} permission. - * This may result in a {@link SecurityException} being thrown. - * *

When passing information to a Java subprocess, * system properties * are generally preferred over environment variables. * * @return this process builder's environment * - * @throws SecurityException - * if a security manager exists and its - * {@link SecurityManager#checkPermission checkPermission} - * method doesn't allow access to the process environment - * * @see Runtime#exec(String[],String[],java.io.File) * @see System#getenv() */ @@ -1009,12 +998,6 @@ public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) { * The minimal set of system dependent environment variables * may override the values provided in the environment. * - *

If there is a security manager, its - * {@link SecurityManager#checkExec checkExec} - * method is called with the first component of this object's - * {@code command} array as its argument. This may result in - * a {@link SecurityException} being thrown. - * *

Starting an operating system process is highly system-dependent. * Among the many things that can go wrong are: *