From a84767e95c2bedfe80fbc5aee57dd2198f030042 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 9 Jun 2023 07:30:03 +0000 Subject: [PATCH 1/8] 8309545: Thread.interrupted from virtual thread needlessly resets interrupt status Reviewed-by: rpressler, dholmes, jpai --- .../share/classes/java/lang/VirtualThread.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 153e684aede..b82a0cde46f 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -870,13 +870,14 @@ public boolean isInterrupted() { @Override boolean getAndClearInterrupt() { assert Thread.currentThread() == this; - synchronized (interruptLock) { - boolean oldValue = interrupted; - if (oldValue) + boolean oldValue = interrupted; + if (oldValue) { + synchronized (interruptLock) { interrupted = false; - carrierThread.clearInterrupt(); - return oldValue; + carrierThread.clearInterrupt(); + } } + return oldValue; } @Override From 6b58c970fee4ba3176dd87de7663903511ae6757 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 24 Aug 2023 16:51:20 +0000 Subject: [PATCH 2/8] 8314759: VirtualThread.parkNanos timeout adjustment when pinned should be replaced Reviewed-by: aturbanov, shade, dfuchs --- src/java.base/share/classes/java/lang/VirtualThread.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index b82a0cde46f..1ac004c5b93 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -636,10 +636,8 @@ void parkNanos(long nanos) { // park on carrier thread for remaining time when pinned if (!yielded) { - long deadline = startTime + nanos; - if (deadline < 0L) - deadline = Long.MAX_VALUE; - parkOnCarrierThread(true, deadline - System.nanoTime()); + long remainingNanos = nanos - (System.nanoTime() - startTime); + parkOnCarrierThread(true, remainingNanos); } } } From 8ff57135770abdae305e4d08a3818a4902824b10 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Sun, 10 Sep 2023 14:57:40 +0000 Subject: [PATCH 3/8] 8315373: Change VirtualThread to unmount after freezing, re-mount before thawing Reviewed-by: pchilanomate, mgronlun, sspitsyn --- .../classes/java/lang/VirtualThread.java | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 1ac004c5b93..26e4a8937a5 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -200,8 +200,11 @@ public void run() { } /** - * Runs or continues execution of the continuation on the current thread. + * Runs or continues execution on the current thread. The virtual thread is mounted + * on the current thread before the task runs or continues. It unmounts when the + * task completes or yields. */ + @ChangesCurrentThread private void runContinuation() { // the carrier must be a platform thread if (Thread.currentThread().isVirtual()) { @@ -223,11 +226,13 @@ private void runContinuation() { // notify JVMTI before mount notifyJvmtiMount(/*hide*/true); + mount(); try { cont.run(); } finally { + unmount(); if (cont.isDone()) { - afterTerminate(); + afterDone(); } else { afterYield(); } @@ -291,16 +296,13 @@ private void submitFailed(RejectedExecutionException ree) { } /** - * Runs a task in the context of this virtual thread. The virtual thread is - * mounted on the current (carrier) thread before the task runs. It unmounts - * from its carrier thread when the task completes. + * Runs a task in the context of this virtual thread. */ @ChangesCurrentThread private void run(Runnable task) { - assert state == RUNNING; + assert Thread.currentThread() == this && state == RUNNING; - // first mount - mount(); + // notify JVMTI, may post VirtualThreadStart event notifyJvmtiStart(); // emit JFR event if enabled @@ -328,12 +330,8 @@ private void run(Runnable task) { } } finally { - // last unmount + // notify JVMTI, may post VirtualThreadEnd event notifyJvmtiEnd(); - unmount(); - - // final state - setState(TERMINATED); } } } @@ -423,21 +421,15 @@ V executeOnCarrierThread(Callable task) throws Exception { } /** - * Unmounts this virtual thread, invokes Continuation.yield, and re-mounts the - * thread when continued. When enabled, JVMTI must be notified from this method. - * @return true if the yield was successful + * Invokes Continuation.yield, notifying JVMTI (if enabled) to hide frames until + * the continuation continues. */ @Hidden - @ChangesCurrentThread private boolean yieldContinuation() { - // unmount notifyJvmtiUnmount(/*hide*/true); - unmount(); try { return Continuation.yield(VTHREAD_SCOPE); } finally { - // re-mount - mount(); notifyJvmtiMount(/*hide*/false); } } @@ -483,22 +475,22 @@ private void afterYield() { } /** - * Invoked after the thread terminates execution. It notifies anyone - * waiting for the thread to terminate. + * Invoked after the continuation completes. */ - private void afterTerminate() { - afterTerminate(true, true); + private void afterDone() { + afterDone(true, true); } /** - * Invoked after the thread terminates (or start failed). This method - * notifies anyone waiting for the thread to terminate. + * Invoked after the continuation completes (or start failed). Sets the thread + * state to TERMINATED and notifies anyone waiting for the thread to terminate. * * @param notifyContainer true if its container should be notified * @param executed true if the thread executed, false if it failed to start */ - private void afterTerminate(boolean notifyContainer, boolean executed) { - assert (state() == TERMINATED) && (carrierThread == null); + private void afterDone(boolean notifyContainer, boolean executed) { + assert carrierThread == null; + setState(TERMINATED); if (executed) { notifyJvmtiUnmount(/*hide*/false); @@ -552,8 +544,7 @@ void start(ThreadContainer container) { started = true; } finally { if (!started) { - setState(TERMINATED); - afterTerminate(addedToContainer, /*executed*/false); + afterDone(addedToContainer, /*executed*/false); } } } From dcec579feed44b6c62ff265b5035b258991f99d0 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn Date: Tue, 12 Sep 2023 02:46:47 +0000 Subject: [PATCH 4/8] 8312174: missing JVMTI events from vthreads parked during JVMTI attach Reviewed-by: lmesnik, amenkov --- .../VThreadEventTest/VThreadEventTest.java | 210 ++++++++++++++++++ .../VThreadEventTest/libVThreadEventTest.cpp | 104 +++++++++ test/lib/jdk/test/lib/jvmti/jvmti_common.h | 7 + 3 files changed, 321 insertions(+) create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java new file mode 100644 index 00000000000..554e83c231c --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java @@ -0,0 +1,210 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8312174 + * @summary missing JVMTI events from vthreads parked during JVMTI attach + * @requires vm.continuations + * @requires vm.jvmti + * @requires vm.compMode != "Xcomp" + * @run main/othervm/native + * -Djdk.virtualThreadScheduler.parallelism=9 + * -Djdk.attach.allowAttachSelf=true -XX:+EnableDynamicAgentLoading VThreadEventTest attach + */ + +import com.sun.tools.attach.VirtualMachine; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.LockSupport; +import java.util.List; +import java.util.ArrayList; + +/* + * The test uses custom implementation of the CountDownLatch class. + * The reason is we want the state of tested thread to be predictable. + * With java.util.concurrent.CountDownLatch it is not clear what thread state is expected. + */ +class CountDownLatch { + private int count = 0; + + CountDownLatch(int count) { + this.count = count; + } + + public synchronized void countDown() { + count--; + notify(); + } + + public synchronized void await() throws InterruptedException { + while (count > 0) { + wait(1); + } + } +} + +public class VThreadEventTest { + static final int TCNT1 = 10; + static final int TCNT2 = 4; + static final int TCNT3 = 4; + static final int THREAD_CNT = TCNT1 + TCNT2 + TCNT3; + + private static void log(String msg) { System.out.println(msg); } + + private static native int threadEndCount(); + private static native int threadMountCount(); + private static native int threadUnmountCount(); + + private static volatile boolean attached; + private static boolean failed; + private static List test1Threads = new ArrayList(TCNT1); + + private static CountDownLatch ready0 = new CountDownLatch(THREAD_CNT); + private static CountDownLatch ready1 = new CountDownLatch(TCNT1); + private static CountDownLatch ready2 = new CountDownLatch(THREAD_CNT); + private static CountDownLatch mready = new CountDownLatch(1); + + private static void await(CountDownLatch dumpedLatch) { + try { + dumpedLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + // The test1 vthreads are kept unmounted until interrupted after agent attach. + static final Runnable test1 = () -> { + synchronized (test1Threads) { + test1Threads.add(Thread.currentThread()); + } + log("test1 vthread started"); + ready0.countDown(); + await(mready); + ready1.countDown(); // to guaranty state is not State.WAITING after await(mready) + try { + Thread.sleep(20000); // big timeout to keep unmounted until interrupted + } catch (InterruptedException ex) { + // it is expected, ignore + } + ready2.countDown(); + }; + + // The test2 vthreads are kept mounted until agent attach. + static final Runnable test2 = () -> { + log("test2 vthread started"); + ready0.countDown(); + await(mready); + while (!attached) { + // keep mounted + } + ready2.countDown(); + }; + + // The test3 vthreads are kept mounted until agent attach. + static final Runnable test3 = () -> { + log("test3 vthread started"); + ready0.countDown(); + await(mready); + while (!attached) { + // keep mounted + } + LockSupport.parkNanos(10_000_000L); // will cause extra mount and unmount + ready2.countDown(); + }; + + public static void main(String[] args) throws Exception { + if (Runtime.getRuntime().availableProcessors() < 8) { + log("WARNING: test expects at least 8 processors."); + } + try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) { + for (int i = 0; i < TCNT1; i++) { + executorService.execute(test1); + } + for (int i = 0; i < TCNT2; i++) { + executorService.execute(test2); + } + for (int i = 0; i < TCNT3; i++) { + executorService.execute(test3); + } + await(ready0); + mready.countDown(); + await(ready1); // to guaranty state is not State.WAITING after await(mready) in test1() + // wait for test1 threads to reach WAITING state in sleep() + for (Thread t : test1Threads) { + Thread.State state = t.getState(); + log("DBG: state: " + state); + while (state != Thread.State.WAITING) { + Thread.sleep(10); + state = t.getState(); + log("DBG: state: " + state); + } + } + + VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); + vm.loadAgentLibrary("VThreadEventTest"); + Thread.sleep(200); // to allow the agent to get ready + + attached = true; + for (Thread t : test1Threads) { + t.interrupt(); + } + ready2.await(); + } + // wait until all VirtualThreadEnd events have been sent + for (int sleepNo = 1; threadEndCount() < THREAD_CNT; sleepNo++) { + Thread.sleep(100); + if (sleepNo % 100 == 0) { // 10 sec period of waiting + log("main: waited seconds: " + sleepNo/10); + } + } + int threadEndCnt = threadEndCount(); + int threadMountCnt = threadMountCount(); + int threadUnmountCnt = threadUnmountCount(); + int threadEndExp = THREAD_CNT; + int threadMountExp = THREAD_CNT - TCNT2; + int threadUnmountExp = THREAD_CNT + TCNT3; + + log("ThreadEnd cnt: " + threadEndCnt + " (expected: " + threadEndExp + ")"); + log("ThreadMount cnt: " + threadMountCnt + " (expected: " + threadMountExp + ")"); + log("ThreadUnmount cnt: " + threadUnmountCnt + " (expected: " + threadUnmountExp + ")"); + + if (threadEndCnt != threadEndExp) { + log("FAILED: unexpected count of ThreadEnd events"); + failed = true; + } + if (threadMountCnt != threadMountExp) { + log("FAILED: unexpected count of ThreadMount events"); + failed = true; + } + if (threadUnmountCnt != threadUnmountExp) { + log("FAILED: unexpected count of ThreadUnmount events"); + failed = true; + } + if (failed) { + throw new RuntimeException("FAILED: event count is wrong"); + } + } + +} + diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp new file mode 100644 index 00000000000..24b678eb18e --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#include +#include +#include +#include "jvmti_common.h" + +extern "C" { + +static jvmtiEnv *jvmti = nullptr; +static std::atomic thread_end_cnt(0); +static std::atomic thread_unmount_cnt(0); +static std::atomic thread_mount_cnt(0); + +void JNICALL VirtualThreadEnd(jvmtiEnv *jvmti, JNIEnv* jni, jthread vthread) { + thread_end_cnt++; +} + +void JNICALL VirtualThreadMount(jvmtiEnv* jvmti, ...) { + thread_mount_cnt++; +} + +void JNICALL VirtualThreadUnmount(jvmtiEnv* jvmti, ...) { + thread_unmount_cnt++; +} + +JNIEXPORT jint JNICALL +Java_VThreadEventTest_threadEndCount(JNIEnv* jni, jclass clazz) { + return thread_end_cnt; +} + +JNIEXPORT jint JNICALL +Java_VThreadEventTest_threadMountCount(JNIEnv* jni, jclass clazz) { + return thread_mount_cnt; +} + +JNIEXPORT jint JNICALL +Java_VThreadEventTest_threadUnmountCount(JNIEnv* jni, jclass clazz) { + return thread_unmount_cnt; +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { + jvmtiEventCallbacks callbacks; + jvmtiCapabilities caps; + jvmtiError err; + + LOG("Agent_OnAttach started\n"); + if (vm->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) { + LOG("Could not initialize JVMTI env\n"); + return JNI_ERR; + } + memset(&caps, 0, sizeof(caps)); + caps.can_support_virtual_threads = 1; + check_jvmti_error(jvmti->AddCapabilities(&caps), "AddCapabilities"); + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.VirtualThreadEnd = &VirtualThreadEnd; + + err = jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks)); + check_jvmti_error(err, "SetEventCallbacks"); + + err = set_ext_event_callback(jvmti, "VirtualThreadMount", VirtualThreadMount); + check_jvmti_error(err, "SetExtEventCallback for VirtualThreadMount"); + + err = set_ext_event_callback(jvmti, "VirtualThreadUnmount", VirtualThreadUnmount); + check_jvmti_error(err, "SetExtEventCallback for VirtualThreadUnmount"); + + err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VIRTUAL_THREAD_END, nullptr); + check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadEnd"); + + err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, EXT_EVENT_VIRTUAL_THREAD_MOUNT, nullptr); + check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadMount"); + + err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, EXT_EVENT_VIRTUAL_THREAD_UNMOUNT, nullptr); + check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadUnmount"); + + LOG("vthread events enabled\n"); + return JVMTI_ERROR_NONE; +} + +} // extern "C" + diff --git a/test/lib/jdk/test/lib/jvmti/jvmti_common.h b/test/lib/jdk/test/lib/jvmti/jvmti_common.h index 802c3443a8c..85acbe73160 100644 --- a/test/lib/jdk/test/lib/jvmti/jvmti_common.h +++ b/test/lib/jdk/test/lib/jvmti/jvmti_common.h @@ -133,6 +133,13 @@ fatal(JNIEnv* jni, const char* msg) { jni->FatalError(msg); } +static void +check_jvmti_error(jvmtiError err, const char* msg) { + if (err != JVMTI_ERROR_NONE) { + LOG("check_jvmti_error: JVMTI function returned error: %s: %s(%d)\n", msg, TranslateError(err), err); + abort(); + } +} static void check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) { From 4e37c1a7ad7249a6a4a0b31a338a5d1c44cb8253 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Tue, 19 Sep 2023 10:58:12 +0000 Subject: [PATCH 5/8] 8312498: Thread::getState and JVM TI GetThreadState should return TIMED_WAITING virtual thread is timed parked Reviewed-by: sspitsyn, rpressler --- .../classes/java/lang/VirtualThread.java | 78 ++-- .../GetThreadState/GetThreadStateTest.java | 416 ++++++++++++++++++ .../GetThreadState/libGetThreadStateTest.c | 51 +++ .../VThreadEventTest/VThreadEventTest.java | 6 +- .../lang/Thread/virtual/CustomScheduler.java | 15 - .../java/lang/Thread/virtual/ThreadAPI.java | 293 +++++++----- .../lang/Thread/virtual/ThreadBuilders.java | 1 - .../lang/Thread/virtual/stress/ParkALot.java | 94 ++++ 8 files changed, 795 insertions(+), 159 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/libGetThreadStateTest.c create mode 100644 test/jdk/java/lang/Thread/virtual/stress/ParkALot.java diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 26e4a8937a5..1ca78e55a0a 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -91,41 +91,52 @@ final class VirtualThread extends BaseVirtualThread { private volatile int state; /* - * Virtual thread state and transitions: + * Virtual thread state transitions: * - * NEW -> STARTED // Thread.start + * NEW -> STARTED // Thread.start, schedule to run * STARTED -> TERMINATED // failed to start * STARTED -> RUNNING // first run + * RUNNING -> TERMINATED // done * - * RUNNING -> PARKING // Thread attempts to park - * PARKING -> PARKED // cont.yield successful, thread is parked - * PARKING -> PINNED // cont.yield failed, thread is pinned + * RUNNING -> PARKING // Thread parking with LockSupport.park + * PARKING -> PARKED // cont.yield successful, parked indefinitely + * PARKING -> PINNED // cont.yield failed, parked indefinitely on carrier + * PARKED -> RUNNABLE // unparked, schedule to continue + * PINNED -> RUNNING // unparked, continue execution on same carrier * - * PARKED -> RUNNABLE // unpark or interrupted - * PINNED -> RUNNABLE // unpark or interrupted + * RUNNING -> TIMED_PARKING // Thread parking with LockSupport.parkNanos + * TIMED_PARKING -> TIMED_PARKED // cont.yield successful, timed-parked + * TIMED_PARKING -> TIMED_PINNED // cont.yield failed, timed-parked on carrier + * TIMED_PARKED -> RUNNABLE // unparked, schedule to continue + * TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier * * RUNNABLE -> RUNNING // continue execution - * + * RUNNING -> YIELDING // Thread.yield * YIELDING -> RUNNABLE // yield successful * YIELDING -> RUNNING // yield failed - * - * RUNNING -> TERMINATED // done */ private static final int NEW = 0; private static final int STARTED = 1; private static final int RUNNABLE = 2; // runnable-unmounted private static final int RUNNING = 3; // runnable-mounted + + // untimed parking private static final int PARKING = 4; private static final int PARKED = 5; // unmounted private static final int PINNED = 6; // mounted - private static final int YIELDING = 7; // Thread.yield + + // timed parking + private static final int TIMED_PARKING = 7; + private static final int TIMED_PARKED = 8; + private static final int TIMED_PINNED = 9; + + private static final int YIELDING = 10; // Thread.yield + private static final int TERMINATED = 99; // final state // can be suspended from scheduling when unmounted private static final int SUSPENDED = 1 << 8; - private static final int RUNNABLE_SUSPENDED = (RUNNABLE | SUSPENDED); - private static final int PARKED_SUSPENDED = (PARKED | SUSPENDED); // parking permit private volatile boolean parkPermit; @@ -440,17 +451,18 @@ private boolean yieldContinuation() { * If yielding due to Thread.yield then it just submits the task to continue. */ private void afterYield() { - int s = state(); - assert (s == PARKING || s == YIELDING) && (carrierThread == null); + assert carrierThread == null; - if (s == PARKING) { - setState(PARKED); + int s = state(); + if (s == PARKING || s == TIMED_PARKING) { + int newState = (s == PARKING) ? PARKED : TIMED_PARKED; + setState(newState); // notify JVMTI that unmount has completed, thread is parked notifyJvmtiUnmount(/*hide*/false); // may have been unparked while parking - if (parkPermit && compareAndSetState(PARKED, RUNNABLE)) { + if (parkPermit && compareAndSetState(newState, RUNNABLE)) { // lazy submit to continue on the current thread as carrier if possible if (currentThread() instanceof CarrierThread ct) { lazySubmitRunContinuation(ct.getPool()); @@ -471,6 +483,8 @@ private void afterYield() { } else { submitRunContinuation(); } + } else { + assert false; } } @@ -613,13 +627,13 @@ void parkNanos(long nanos) { boolean yielded = false; Future unparker = scheduleUnpark(this::unpark, nanos); - setState(PARKING); + setState(TIMED_PARKING); try { yielded = yieldContinuation(); // may throw } finally { assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING)); if (!yielded) { - assert state() == PARKING; + assert state() == TIMED_PARKING; setState(RUNNING); } cancel(unparker); @@ -651,7 +665,7 @@ private void parkOnCarrierThread(boolean timed, long nanos) { event = null; } - setState(PINNED); + setState(timed ? TIMED_PINNED : PINNED); try { if (!parkPermit) { if (!timed) { @@ -719,7 +733,8 @@ void unpark() { Thread currentThread = Thread.currentThread(); if (!getAndSetParkPermit(true) && currentThread != this) { int s = state(); - if (s == PARKED && compareAndSetState(PARKED, RUNNABLE)) { + boolean parked = (s == PARKED) || (s == TIMED_PARKED); + if (parked && compareAndSetState(s, RUNNABLE)) { if (currentThread instanceof VirtualThread vthread) { vthread.switchToCarrierThread(); try { @@ -730,11 +745,11 @@ void unpark() { } else { submitRunContinuation(); } - } else if (s == PINNED) { + } else if ((s == PINNED) || (s == TIMED_PINNED)) { // unpark carrier thread when pinned. synchronized (carrierThreadAccessLock()) { Thread carrier = carrierThread; - if (carrier != null && state() == PINNED) { + if (carrier != null && ((s = state()) == PINNED || s == TIMED_PINNED)) { U.unpark(carrier); } } @@ -871,7 +886,8 @@ boolean getAndClearInterrupt() { @Override Thread.State threadState() { - switch (state()) { + int s = state(); + switch (s & ~SUSPENDED) { case NEW: return Thread.State.NEW; case STARTED: @@ -882,7 +898,6 @@ Thread.State threadState() { return Thread.State.RUNNABLE; } case RUNNABLE: - case RUNNABLE_SUSPENDED: // runnable, not mounted return Thread.State.RUNNABLE; case RUNNING: @@ -896,13 +911,16 @@ Thread.State threadState() { // runnable, mounted return Thread.State.RUNNABLE; case PARKING: + case TIMED_PARKING: case YIELDING: // runnable, mounted, not yet waiting return Thread.State.RUNNABLE; case PARKED: - case PARKED_SUSPENDED: case PINNED: - return Thread.State.WAITING; + return State.WAITING; + case TIMED_PARKED: + case TIMED_PINNED: + return State.TIMED_WAITING; case TERMINATED: return Thread.State.TERMINATED; default: @@ -942,7 +960,7 @@ StackTraceElement[] asyncGetStackTrace() { private StackTraceElement[] tryGetStackTrace() { int initialState = state(); return switch (initialState) { - case RUNNABLE, PARKED -> { + case RUNNABLE, PARKED, TIMED_PARKED -> { int suspendedState = initialState | SUSPENDED; if (compareAndSetState(initialState, suspendedState)) { try { @@ -954,7 +972,7 @@ private StackTraceElement[] tryGetStackTrace() { // re-submit if runnable // re-submit if unparked while suspended if (initialState == RUNNABLE - || (parkPermit && compareAndSetState(PARKED, RUNNABLE))) { + || (parkPermit && compareAndSetState(initialState, RUNNABLE))) { try { submitRunContinuation(); } catch (RejectedExecutionException ignore) { } diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java new file mode 100644 index 00000000000..17c17b9b1f5 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java @@ -0,0 +1,416 @@ +/* + * 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. + */ + +/* + * @test id=default + * @bug 8312498 + * @summary Basic test for JVMTI GetThreadState with virtual threads + * @run junit/othervm/native GetThreadStateTest + */ + +/* + * @test id=no-vmcontinuations + * @requires vm.continuations + * @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations GetThreadStateTest + */ + +import java.util.StringJoiner; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class GetThreadStateTest { + + @BeforeAll + static void setup() { + System.loadLibrary("GetThreadStateTest"); + init(); + } + + /** + * Test state of new/unstarted thread. + */ + @Test + void testUnstarted() { + var thread = Thread.ofVirtual().unstarted(() -> { }); + check(thread, /*new*/ 0); + } + + /** + * Test state of terminated thread. + */ + @Test + void testTerminated() throws Exception { + var thread = Thread.ofVirtual().start(() -> { }); + thread.join(); + check(thread, JVMTI_THREAD_STATE_TERMINATED); + } + + /** + * Test state of runnable thread. + */ + @Test + void testRunnable() throws Exception { + var latch = new CountDownLatch(1); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + latch.countDown(); + + // spin until done + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + // wait for thread to start execution + latch.await(); + + // thread should be runnable + int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE; + check(thread, expected); + + // re-test with interrupt status set + thread.interrupt(); + check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); + } finally { + done.set(true); + thread.join(); + } + } + + /** + * Test state of thread waiting to enter a monitor. + */ + @Test + void testMonitorEnter() throws Exception { + var latch = new CountDownLatch(1); + Object lock = new Object(); + var thread = Thread.ofVirtual().unstarted(() -> { + latch.countDown(); + synchronized (lock) { } + }); + try { + synchronized (lock) { + // start thread and wait for it to start execution + thread.start(); + latch.await(); + + // thread should block on monitor enter + int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; + await(thread, expected); + + // re-test with interrupt status set + thread.interrupt(); + check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); + } + } finally { + thread.join(); + } + } + + /** + * Test state of thread waiting in Object.wait(). + */ + @Test + void testObjectWait() throws Exception { + var latch = new CountDownLatch(1); + Object lock = new Object(); + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + latch.countDown(); + try { + lock.wait(); + } catch (InterruptedException e) { } + } + }); + try { + // wait for thread to own monitor + latch.await(); + + // thread should wait + int expected = JVMTI_THREAD_STATE_ALIVE | + JVMTI_THREAD_STATE_WAITING | + JVMTI_THREAD_STATE_WAITING_INDEFINITELY | + JVMTI_THREAD_STATE_IN_OBJECT_WAIT; + await(thread, expected); + + // notify so thread waits to re-enter monitor + synchronized (lock) { + lock.notifyAll(); + expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; + check(thread, expected); + + // re-test with interrupt status set + thread.interrupt(); + check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); + } + } finally { + thread.interrupt(); + thread.join(); + } + } + + /** + * Test state of thread waiting in Object.wait(millis). + */ + @Test + void testObjectWaitMillis() throws Exception { + var latch = new CountDownLatch(1); + Object lock = new Object(); + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + latch.countDown(); + try { + lock.wait(Long.MAX_VALUE); + } catch (InterruptedException e) { } + } + }); + try { + // wait for thread to own monitor + latch.await(); + + // thread should wait + int expected = JVMTI_THREAD_STATE_ALIVE | + JVMTI_THREAD_STATE_WAITING | + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | + JVMTI_THREAD_STATE_IN_OBJECT_WAIT; + await(thread, expected); + + // notify so thread waits to re-enter monitor + synchronized (lock) { + lock.notifyAll(); + expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; + check(thread, expected); + + // re-test with interrupt status set + thread.interrupt(); + check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); + } + } finally { + thread.interrupt(); + thread.join(); + } + } + + /** + * Test state of thread parked with LockSupport.park. + */ + @Test + void testPark() throws Exception { + var latch = new CountDownLatch(1); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + latch.countDown(); + while (!done.get()) { + LockSupport.park(); + } + }); + try { + // wait for thread to start execution + latch.await(); + + // thread should park + int expected = JVMTI_THREAD_STATE_ALIVE | + JVMTI_THREAD_STATE_WAITING | + JVMTI_THREAD_STATE_WAITING_INDEFINITELY | + JVMTI_THREAD_STATE_PARKED; + await(thread, expected); + } finally { + done.set(true); + LockSupport.unpark(thread); + thread.join(); + } + } + + /** + * Test state of thread parked with LockSupport.parkNanos. + */ + @Test + void testParkNanos() throws Exception { + var latch = new CountDownLatch(1); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + latch.countDown(); + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + }); + try { + // wait for thread to start execution + latch.await(); + + // thread should park + int expected = JVMTI_THREAD_STATE_ALIVE | + JVMTI_THREAD_STATE_WAITING | + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | + JVMTI_THREAD_STATE_PARKED; + await(thread, expected); + } finally { + done.set(true); + LockSupport.unpark(thread); + thread.join(); + } + } + + /** + * Test state of thread parked with LockSupport.park while holding a monitor. + */ + @Test + void testParkWhenPinned() throws Exception { + var latch = new CountDownLatch(1); + Object lock = new Object(); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + latch.countDown(); + while (!done.get()) { + LockSupport.park(); + } + } + }); + try { + // wait for thread to own monitor + latch.await(); + + // thread should park + int expected = JVMTI_THREAD_STATE_ALIVE | + JVMTI_THREAD_STATE_WAITING | + JVMTI_THREAD_STATE_WAITING_INDEFINITELY | + JVMTI_THREAD_STATE_PARKED; + await(thread, expected); + } finally { + done.set(true); + LockSupport.unpark(thread); + thread.join(); + } + } + + /** + * Test state of thread parked with LockSupport.parkNanos while holding a monitor. + */ + @Test + void testParkNanosWhenPinned() throws Exception { + var latch = new CountDownLatch(1); + Object lock = new Object(); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + latch.countDown(); + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + } + }); + try { + // wait for thread to own monitor + latch.await(); + + // thread should park + int expected = JVMTI_THREAD_STATE_ALIVE | + JVMTI_THREAD_STATE_WAITING | + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | + JVMTI_THREAD_STATE_PARKED; + await(thread, expected); + } finally { + done.set(true); + LockSupport.unpark(thread); + thread.join(); + } + } + + /** + * Asserts that the given thread has the expected JVMTI state. + */ + private static void check(Thread thread, int expected) { + System.err.format(" expect state=0x%x (%s) ...%n", expected, jvmtiStateToString(expected)); + int state = jvmtiState(thread); + System.err.format(" thread state=0x%x (%s)%n", state, jvmtiStateToString(state)); + assertEquals(expected, state); + } + + /** + * Waits indefinitely for the given thread to get to the target JVMTI state. + */ + private static void await(Thread thread, int targetState) throws Exception { + System.err.format(" await state=0x%x (%s) ...%n", targetState, jvmtiStateToString(targetState)); + int state = jvmtiState(thread); + System.err.format(" thread state=0x%x (%s)%n", state, jvmtiStateToString(state)); + while (state != targetState) { + assertTrue(thread.isAlive(), "Thread has terminated"); + Thread.sleep(20); + state = jvmtiState(thread); + System.err.format(" thread state=0x%x (%s)%n", state, jvmtiStateToString(state)); + } + } + + private static final int JVMTI_THREAD_STATE_ALIVE = 0x0001; + private static final int JVMTI_THREAD_STATE_TERMINATED = 0x0002; + private static final int JVMTI_THREAD_STATE_RUNNABLE = 0x0004; + private static final int JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400; + private static final int JVMTI_THREAD_STATE_WAITING = 0x0080; + private static final int JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010; + private static final int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020; + private static final int JVMTI_THREAD_STATE_SLEEPING = 0x0040; + private static final int JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100; + private static final int JVMTI_THREAD_STATE_PARKED = 0x0200; + private static final int JVMTI_THREAD_STATE_SUSPENDED = 0x100000; + private static final int JVMTI_THREAD_STATE_INTERRUPTED = 0x200000; + private static final int JVMTI_THREAD_STATE_IN_NATIVE = 0x400000; + + private static native void init(); + private static native int jvmtiState(Thread thread); + + private static String jvmtiStateToString(int state) { + StringJoiner sj = new StringJoiner(" | "); + if ((state & JVMTI_THREAD_STATE_ALIVE) != 0) + sj.add("JVMTI_THREAD_STATE_ALIVE"); + if ((state & JVMTI_THREAD_STATE_TERMINATED) != 0) + sj.add("JVMTI_THREAD_STATE_TERMINATED"); + if ((state & JVMTI_THREAD_STATE_RUNNABLE) != 0) + sj.add("JVMTI_THREAD_STATE_RUNNABLE"); + if ((state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) + sj.add("JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER"); + if ((state & JVMTI_THREAD_STATE_WAITING) != 0) + sj.add("JVMTI_THREAD_STATE_WAITING"); + if ((state & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != 0) + sj.add("JVMTI_THREAD_STATE_WAITING_INDEFINITELY"); + if ((state & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != 0) + sj.add("JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT"); + if ((state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) != 0) + sj.add("JVMTI_THREAD_STATE_IN_OBJECT_WAIT"); + if ((state & JVMTI_THREAD_STATE_PARKED) != 0) + sj.add("JVMTI_THREAD_STATE_PARKED"); + if ((state & JVMTI_THREAD_STATE_SUSPENDED) != 0) + sj.add("JVMTI_THREAD_STATE_SUSPENDED"); + if ((state & JVMTI_THREAD_STATE_INTERRUPTED) != 0) + sj.add("JVMTI_THREAD_STATE_INTERRUPTED"); + if ((state & JVMTI_THREAD_STATE_IN_NATIVE) != 0) + sj.add("JVMTI_THREAD_STATE_IN_NATIVE"); + String s = sj.toString(); + return s.isEmpty() ? "" : s; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/libGetThreadStateTest.c b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/libGetThreadStateTest.c new file mode 100644 index 00000000000..532225670f5 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/libGetThreadStateTest.c @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jvmti.h" + +static jvmtiEnv *jvmti; + +JNIEXPORT void JNICALL Java_GetThreadStateTest_init(JNIEnv *env, jclass clazz) { + JavaVM* vm; + jint res; + res = (*env)->GetJavaVM(env, &vm); + if (res != 0) { + (*env)->FatalError(env, "GetJavaVM failed"); + } else { + res = (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION); + if (res != JNI_OK) { + (*env)->FatalError(env, "GetEnv failed"); + } + } +} + +JNIEXPORT jint JNICALL Java_GetThreadStateTest_jvmtiState(JNIEnv *env, jclass clazz, jobject thread) { + jvmtiError err; + jint state = 0; + err = (*jvmti)->GetThreadState(jvmti, thread, &state); + if (err != JVMTI_ERROR_NONE) { + (*env)->FatalError(env, "GetThreadState failed"); + } + return state; +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java index 554e83c231c..61b94a4484b 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java @@ -149,12 +149,12 @@ public static void main(String[] args) throws Exception { } await(ready0); mready.countDown(); - await(ready1); // to guaranty state is not State.WAITING after await(mready) in test1() - // wait for test1 threads to reach WAITING state in sleep() + await(ready1); // to guarantee state is not State.TIMED_WAITING after await(mready) in test1() + // wait for test1 threads to reach TIMED_WAITING state in sleep() for (Thread t : test1Threads) { Thread.State state = t.getState(); log("DBG: state: " + state); - while (state != Thread.State.WAITING) { + while (state != Thread.State.TIMED_WAITING) { Thread.sleep(10); state = t.getState(); log("DBG: state: " + state); diff --git a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java index 6d4e029f280..5bde81905ec 100644 --- a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java +++ b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java @@ -45,7 +45,6 @@ import static org.junit.jupiter.api.Assumptions.*; class CustomScheduler { - private static final Executor DEFAULT_SCHEDULER = defaultScheduler(); private static ExecutorService scheduler1; private static ExecutorService scheduler2; @@ -216,20 +215,6 @@ void testRunWithInterruptSet() throws Exception { } } - /** - * Returns the default scheduler. - */ - private static Executor defaultScheduler() { - try { - Field defaultScheduler = Class.forName("java.lang.VirtualThread") - .getDeclaredField("DEFAULT_SCHEDULER"); - defaultScheduler.setAccessible(true); - return (Executor) defaultScheduler.get(null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - /** * Returns the scheduler for the given virtual thread. */ diff --git a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java index 8afb472d3f4..27d21a79346 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java +++ b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java @@ -106,7 +106,7 @@ void testCurrentThread1() throws Exception { LockSupport.park(); after.set(Thread.currentThread()); }); - awaitParked(thread); + await(thread, Thread.State.WAITING); LockSupport.unpark(thread); thread.join(); assertTrue(before.get() == thread); @@ -130,7 +130,7 @@ void testCurrentThread2() throws Exception { }); synchronized (lock) { thread.start(); - awaitBlocked(thread); + await(thread, Thread.State.BLOCKED); } thread.join(); assertTrue(ref1.get() == thread); @@ -160,7 +160,7 @@ void testCurrentThread3() throws Exception { lock.lock(); try { thread.start(); - awaitParked(thread); + await(thread, Thread.State.WAITING); } finally { lock.unlock(); } @@ -765,6 +765,7 @@ void testJoin33() throws Exception { assertFalse(thread.join(Duration.ofMillis(100))); } finally { done.set(true); + thread.join(); } } @@ -897,7 +898,7 @@ void testInterrupt6() throws Exception { exception.set(e); } }); - awaitParked(thread); + await(thread, Thread.State.TIMED_WAITING); thread.interrupt(); thread.join(); assertNull(exception.get()); @@ -917,7 +918,7 @@ void testInterrupt7() throws Exception { exception.set(e); } }); - awaitParked(thread); + await(thread, Thread.State.WAITING); thread.interrupt(); thread.join(); assertNull(exception.get()); @@ -1032,16 +1033,16 @@ void testSetName3() throws Exception { void testSetPriority1() throws Exception { VThreadRunner.run(() -> { Thread me = Thread.currentThread(); - assertTrue(me.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, me.getPriority()); me.setPriority(Thread.MAX_PRIORITY); - assertTrue(me.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, me.getPriority()); me.setPriority(Thread.NORM_PRIORITY); - assertTrue(me.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, me.getPriority()); me.setPriority(Thread.MIN_PRIORITY); - assertTrue(me.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, me.getPriority()); assertThrows(IllegalArgumentException.class, () -> me.setPriority(-1)); }); @@ -1055,33 +1056,33 @@ void testSetPriority2() throws Exception { var thread = Thread.ofVirtual().unstarted(LockSupport::park); // not started - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); thread.setPriority(Thread.MAX_PRIORITY); - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); thread.setPriority(Thread.NORM_PRIORITY); - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); thread.setPriority(Thread.MIN_PRIORITY); - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); assertThrows(IllegalArgumentException.class, () -> thread.setPriority(-1)); // running thread.start(); try { - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); thread.setPriority(Thread.NORM_PRIORITY); thread.setPriority(Thread.MAX_PRIORITY); - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); thread.setPriority(Thread.NORM_PRIORITY); - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); thread.setPriority(Thread.MIN_PRIORITY); - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); assertThrows(IllegalArgumentException.class, () -> thread.setPriority(-1)); @@ -1091,7 +1092,7 @@ void testSetPriority2() throws Exception { thread.join(); // terminated - assertTrue(thread.getPriority() == Thread.NORM_PRIORITY); + assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); } /** @@ -1650,53 +1651,79 @@ void testThreadId2() throws Exception { } /** - * Test Thread::getState when thread is not started. + * Test Thread::getState when thread is new/unstarted. */ @Test void testGetState1() { var thread = Thread.ofVirtual().unstarted(() -> { }); - assertTrue(thread.getState() == Thread.State.NEW); + assertEquals(Thread.State.NEW, thread.getState()); } /** - * Test Thread::getState when thread is runnable (mounted). + * Test Thread::getState when thread is terminated. */ @Test void testGetState2() throws Exception { - VThreadRunner.run(() -> { - Thread.State state = Thread.currentThread().getState(); - assertTrue(state == Thread.State.RUNNABLE); + var thread = Thread.ofVirtual().start(() -> { }); + thread.join(); + assertEquals(Thread.State.TERMINATED, thread.getState()); + } + + /** + * Test Thread::getState when thread is runnable (mounted). + */ + @Test + void testGetState3() throws Exception { + var started = new CountDownLatch(1); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + started.countDown(); + + // spin until done + while (!done.get()) { + Thread.onSpinWait(); + } }); + try { + // wait for thread to start + started.await(); + + // thread should be runnable + assertEquals(Thread.State.RUNNABLE, thread.getState()); + } finally { + done.set(true); + thread.join(); + } } /** * Test Thread::getState when thread is runnable (not mounted). */ @Test - void testGetState3() throws Exception { + void testGetState4() throws Exception { assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); AtomicBoolean completed = new AtomicBoolean(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); Thread t1 = builder.start(() -> { Thread t2 = builder.unstarted(LockSupport::park); - assertTrue(t2.getState() == Thread.State.NEW); + assertEquals(Thread.State.NEW, t2.getState()); // start t2 to make it runnable t2.start(); try { - assertTrue(t2.getState() == Thread.State.RUNNABLE); + assertEquals(Thread.State.RUNNABLE, t2.getState()); // yield to allow t2 to run and park Thread.yield(); - assertTrue(t2.getState() == Thread.State.WAITING); + assertEquals(Thread.State.WAITING, t2.getState()); } finally { // unpark t2 to make it runnable again LockSupport.unpark(t2); } // t2 should be runnable (not mounted) - assertTrue(t2.getState() == Thread.State.RUNNABLE); + assertEquals(Thread.State.RUNNABLE, t2.getState()); completed.set(true); }); @@ -1706,77 +1733,147 @@ void testGetState3() throws Exception { } /** - * Test Thread::getState when thread is parked. + * Test Thread::getState when thread is waiting to enter a monitor. */ @Test - void testGetState4() throws Exception { - var thread = Thread.ofVirtual().start(LockSupport::park); - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(20); + void testGetState5() throws Exception { + var started = new CountDownLatch(1); + var thread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { } + }); + synchronized (lock) { + thread.start(); + started.await(); + + // wait for thread to block + await(thread, Thread.State.BLOCKED); } - LockSupport.unpark(thread); thread.join(); } /** - * Test Thread::getState when thread is parked while holding a monitor. + * Test Thread::getState when thread is waiting in Object.wait. */ @Test - void testGetState5() throws Exception { + void testGetState6() throws Exception { var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - LockSupport.park(); + try { lock.wait(); } catch (InterruptedException e) { } } }); - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(20); + try { + // wait for thread to wait + await(thread, Thread.State.WAITING); + } finally { + thread.interrupt(); + thread.join(); } - LockSupport.unpark(thread); - thread.join(); } /** - * Test Thread::getState when thread is waiting for a monitor. + * Test Thread::getState when thread is waiting in Object.wait(millis). */ @Test - void testGetState6() throws Exception { - var thread = Thread.ofVirtual().unstarted(() -> { - synchronized (lock) { } - }); - synchronized (lock) { - thread.start(); - while (thread.getState() != Thread.State.BLOCKED) { - Thread.sleep(20); + void testGetState7() throws Exception { + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + lock.wait(Long.MAX_VALUE); + } catch (InterruptedException e) { } } + }); + try { + // wait for thread to wait + await(thread, Thread.State.TIMED_WAITING); + } finally { + thread.interrupt(); + thread.join(); } - thread.join(); } /** - * Test Thread::getState when thread is waiting in Object.wait. + * Test Thread::getState when thread is parked. */ @Test - void testGetState7() throws Exception { + void testGetState8() throws Exception { + var thread = Thread.ofVirtual().start(LockSupport::park); + try { + await(thread, Thread.State.WAITING); + } finally { + LockSupport.unpark(thread); + thread.join(); + } + } + + /** + * Test Thread::getState when thread is timed parked. + */ + @Test + void testGetState9() throws Exception { + var thread = Thread.ofVirtual().start(() -> LockSupport.parkNanos(Long.MAX_VALUE)); + try { + await(thread, Thread.State.TIMED_WAITING); + } finally { + LockSupport.unpark(thread); + thread.join(); + } + } + + /** + * Test Thread::getState when thread is parked while holding a monitor. + */ + @Test + void testGetState10() throws Exception { + var started = new CountDownLatch(1); + var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { + started.countDown(); synchronized (lock) { - try { lock.wait(); } catch (InterruptedException e) { } + while (!done.get()) { + LockSupport.park(); + } } }); - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(20); + try { + // wait for thread to start + started.await(); + + // wait for thread to park + await(thread, Thread.State.WAITING); + } finally { + done.set(true); + LockSupport.unpark(thread); + thread.join(); } - thread.interrupt(); - thread.join(); } /** - * Test Thread::getState when thread is terminated. + * Test Thread::getState when thread is timed parked while holding a monitor. */ @Test - void testGetState8() throws Exception { - var thread = Thread.ofVirtual().start(() -> { }); - thread.join(); - assertTrue(thread.getState() == Thread.State.TERMINATED); + void testGetState11() throws Exception { + var started = new CountDownLatch(1); + var done = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + started.countDown(); + synchronized (lock) { + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + } + }); + try { + // wait for thread to start + started.await(); + + // wait for thread to park + await(thread, Thread.State.TIMED_WAITING); + } finally { + done.set(true); + LockSupport.unpark(thread); + thread.join(); + } } /** @@ -1899,9 +1996,7 @@ void testGetStackTrace4() throws Exception { } // wait for virtual thread to block in wait - while (vthread.getState() != Thread.State.WAITING) { - Thread.sleep(20); - } + await(vthread, Thread.State.WAITING); // get stack trace of both carrier and virtual thread StackTraceElement[] carrierStackTrace = carrier.getStackTrace(); @@ -1928,12 +2023,7 @@ void testGetStackTrace4() throws Exception { @Test void testGetStackTrace5() throws Exception { var thread = Thread.ofVirtual().start(LockSupport::park); - - // wait for thread to park - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(20); - } - + await(thread, Thread.State.WAITING); try { StackTraceElement[] stack = thread.getStackTrace(); assertTrue(contains(stack, "LockSupport.park")); @@ -1996,9 +2086,7 @@ void testGetAllStackTraces2() throws Exception { } // wait for virtual thread to block in wait - while (vthread.getState() != Thread.State.WAITING) { - Thread.sleep(20); - } + await(vthread, Thread.State.WAITING); // get all stack traces Map map = Thread.getAllStackTraces(); @@ -2034,7 +2122,7 @@ void testThreadGroup1() throws Exception { var vgroup = thread.getThreadGroup(); thread.start(); try { - assertTrue(thread.getThreadGroup() == vgroup); + assertEquals(vgroup, thread.getThreadGroup()); } finally { LockSupport.unpark(thread); thread.join(); @@ -2051,7 +2139,7 @@ void testThreadGroup2() throws Exception { ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); Thread child = new Thread(() -> { }); ThreadGroup group = child.getThreadGroup(); - assertTrue(group == vgroup); + assertEquals(vgroup, group); }); } @@ -2068,19 +2156,19 @@ void testThreadGroup3() throws Exception { thread.join(); ThreadGroup vgroup = ref.get(); - assertTrue(vgroup.getMaxPriority() == Thread.MAX_PRIORITY); + assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); ThreadGroup group = new ThreadGroup(vgroup, "group"); assertTrue(group.getParent() == vgroup); - assertTrue(group.getMaxPriority() == Thread.MAX_PRIORITY); + assertEquals(Thread.MAX_PRIORITY, group.getMaxPriority()); vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1); - assertTrue(vgroup.getMaxPriority() == Thread.MAX_PRIORITY); - assertTrue(group.getMaxPriority() == Thread.MAX_PRIORITY - 1); + assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); + assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority()); vgroup.setMaxPriority(Thread.MIN_PRIORITY); - assertTrue(vgroup.getMaxPriority() == Thread.MAX_PRIORITY); - assertTrue(group.getMaxPriority() == Thread.MIN_PRIORITY); + assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); + assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority()); } /** @@ -2091,20 +2179,19 @@ void testThreadGroup3() throws Exception { void testThreadGroup4() throws Exception { VThreadRunner.run(() -> { ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); - - assertTrue(vgroup.getMaxPriority() == Thread.MAX_PRIORITY); + assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); ThreadGroup group = new ThreadGroup("group"); - assertTrue(group.getParent() == vgroup); - assertTrue(group.getMaxPriority() == Thread.MAX_PRIORITY); + assertEquals(vgroup, group.getParent()); + assertEquals(Thread.MAX_PRIORITY, group.getMaxPriority()); vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1); - assertTrue(vgroup.getMaxPriority() == Thread.MAX_PRIORITY); - assertTrue(group.getMaxPriority() == Thread.MAX_PRIORITY - 1); + assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); + assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority()); vgroup.setMaxPriority(Thread.MIN_PRIORITY); - assertTrue(vgroup.getMaxPriority() == Thread.MAX_PRIORITY); - assertTrue(group.getMaxPriority() == Thread.MIN_PRIORITY); + assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); + assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority()); }); } @@ -2208,9 +2295,7 @@ void testToString3() throws Exception { me.setName("fred"); LockSupport.park(); }); - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(10); - } + await(thread, Thread.State.WAITING); try { assertTrue(thread.toString().contains("fred")); } finally { @@ -2233,23 +2318,11 @@ void testToString4() throws Exception { } /** - * Waits for the given thread to park. - */ - static void awaitParked(Thread thread) throws InterruptedException { - Thread.State state = thread.getState(); - while (state != Thread.State.WAITING && state != Thread.State.TIMED_WAITING) { - assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); - Thread.sleep(10); - state = thread.getState(); - } - } - - /** - * Waits for the given thread to block waiting on a monitor. + * Waits for the given thread to reach a given state. */ - static void awaitBlocked(Thread thread) throws InterruptedException { + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { Thread.State state = thread.getState(); - while (state != Thread.State.BLOCKED) { + 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/ThreadBuilders.java b/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java index 9283bf99321..a6a5d6abdbc 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java +++ b/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java @@ -51,7 +51,6 @@ private ThreadBuilders() { } * @throws UnsupportedOperationException if custom schedulers are not supported */ static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { - Thread.Builder.OfVirtual builder = Thread.ofVirtual(); try { return (Thread.Builder.OfVirtual) VTBUILDER_CTOR.newInstance(scheduler); } catch (InvocationTargetException e) { diff --git a/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java b/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java new file mode 100644 index 00000000000..a5eb98d207d --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java @@ -0,0 +1,94 @@ +/* + * 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. + */ + +/* + * @test + * @summary Stress test parking and unparking + * @requires vm.debug != true + * @run main/othervm ParkALot 500000 + */ + +/* + * @test + * @requires vm.debug == true + * @run main/othervm ParkALot 200000 + */ + +import java.time.Instant; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.locks.LockSupport; + +public class ParkALot { + private static final int ITERATIONS = 1_000_000; + + public static void main(String[] args) { + int iterations; + if (args.length > 0) { + iterations = Integer.parseInt(args[0]); + } else { + iterations = ITERATIONS; + } + + int maxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 2, 1); + for (int nthreads = 1; nthreads <= maxThreads; nthreads++) { + System.out.format("%s %d threads ...%n", Instant.now(), nthreads); + ThreadFactory factory = Thread.ofPlatform().factory(); + try (var executor = Executors.newThreadPerTaskExecutor(factory)) { + for (int i = 0; i < nthreads; i++) { + executor.submit(() -> parkALot(iterations)); + } + } + } + } + + /** + * Creates a virtual thread that alternates between untimed and timed parking. + * A platform thread spins unparking the virtual thread. + */ + private static void parkALot(int iterations) { + Thread vthread = Thread.ofVirtual().start(() -> { + int i = 0; + boolean timed = false; + while (i < iterations) { + if (timed) { + LockSupport.parkNanos(Long.MAX_VALUE); + timed = false; + } else { + LockSupport.park(); + timed = true; + } + i++; + } + }); + + Thread.State state; + while ((state = vthread.getState()) != Thread.State.TERMINATED) { + if (state == Thread.State.WAITING || state == Thread.State.TIMED_WAITING) { + LockSupport.unpark(vthread); + } else { + Thread.onSpinWait(); + } + } + } +} From cd102b01b911a9a7003e8afdb9f62034e199943a Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 20 Oct 2023 05:56:26 +0000 Subject: [PATCH 6/8] 8312777: notifyJvmtiMount before notifyJvmtiUnmount Reviewed-by: mli, sspitsyn --- .../classes/java/lang/VirtualThread.java | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 1ca78e55a0a..d76d277e05b 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -111,7 +111,7 @@ final class VirtualThread extends BaseVirtualThread { * TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier * * RUNNABLE -> RUNNING // continue execution - + * * RUNNING -> YIELDING // Thread.yield * YIELDING -> RUNNABLE // yield successful * YIELDING -> RUNNING // yield failed @@ -234,9 +234,6 @@ private void runContinuation() { return; } - // notify JVMTI before mount - notifyJvmtiMount(/*hide*/true); - mount(); try { cont.run(); @@ -309,7 +306,6 @@ private void submitFailed(RejectedExecutionException ree) { /** * Runs a task in the context of this virtual thread. */ - @ChangesCurrentThread private void run(Runnable task) { assert Thread.currentThread() == this && state == RUNNING; @@ -354,6 +350,9 @@ private void run(Runnable task) { @ChangesCurrentThread @ReservedStackAccess private void mount() { + // notify JVMTI before mount + notifyJvmtiMount(/*hide*/true); + // sets the carrier thread Thread carrier = Thread.currentCarrierThread(); setCarrierThread(carrier); @@ -390,6 +389,9 @@ private void unmount() { setCarrierThread(null); } carrier.clearInterrupt(); + + // notify JVMTI after unmount + notifyJvmtiUnmount(/*hide*/false); } /** @@ -454,13 +456,12 @@ private void afterYield() { assert carrierThread == null; int s = state(); + + // LockSupport.park/parkNanos if (s == PARKING || s == TIMED_PARKING) { int newState = (s == PARKING) ? PARKED : TIMED_PARKED; setState(newState); - // notify JVMTI that unmount has completed, thread is parked - notifyJvmtiUnmount(/*hide*/false); - // may have been unparked while parking if (parkPermit && compareAndSetState(newState, RUNNABLE)) { // lazy submit to continue on the current thread as carrier if possible @@ -471,11 +472,12 @@ private void afterYield() { } } - } else if (s == YIELDING) { // Thread.yield - setState(RUNNABLE); + return; + } - // notify JVMTI that unmount has completed, thread is runnable - notifyJvmtiUnmount(/*hide*/false); + // Thread.yield + if (s == YIELDING) { + setState(RUNNABLE); // external submit if there are no tasks in the local task queue if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) { @@ -483,16 +485,17 @@ private void afterYield() { } else { submitRunContinuation(); } - } else { - assert false; + return; } + + assert false; } /** * Invoked after the continuation completes. */ private void afterDone() { - afterDone(true, true); + afterDone(true); } /** @@ -500,16 +503,11 @@ private void afterDone() { * state to TERMINATED and notifies anyone waiting for the thread to terminate. * * @param notifyContainer true if its container should be notified - * @param executed true if the thread executed, false if it failed to start */ - private void afterDone(boolean notifyContainer, boolean executed) { + private void afterDone(boolean notifyContainer) { assert carrierThread == null; setState(TERMINATED); - if (executed) { - notifyJvmtiUnmount(/*hide*/false); - } - // notify anyone waiting for this virtual thread to terminate CountDownLatch termination = this.termination; if (termination != null) { @@ -558,7 +556,7 @@ void start(ThreadContainer container) { started = true; } finally { if (!started) { - afterDone(addedToContainer, /*executed*/false); + afterDone(addedToContainer); } } } From 874ab83ffe4093a6db55a795f211e67926dc645f Mon Sep 17 00:00:00 2001 From: "Keith W. Campbell" Date: Mon, 18 Sep 2023 15:06:21 -0400 Subject: [PATCH 7/8] Use GetExtensionEvents() to derive JVMTI extension event numbers Signed-off-by: Keith W. Campbell --- .../vthread/VThreadEventTest/libVThreadEventTest.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp index 24b678eb18e..8e1c8232d9e 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp @@ -20,6 +20,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2023, 2023 All Rights Reserved + * =========================================================================== + */ #include #include @@ -90,10 +95,10 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VIRTUAL_THREAD_END, nullptr); check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadEnd"); - err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, EXT_EVENT_VIRTUAL_THREAD_MOUNT, nullptr); + err = set_ext_event_notification_mode(jvmti, JVMTI_ENABLE, "VirtualThreadMount", NULL); check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadMount"); - err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, EXT_EVENT_VIRTUAL_THREAD_UNMOUNT, nullptr); + err = set_ext_event_notification_mode(jvmti, JVMTI_ENABLE, "VirtualThreadUnmount", NULL); check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadUnmount"); LOG("vthread events enabled\n"); From f3eda87b076cc5f60fb068efb9e58dae6ff64188 Mon Sep 17 00:00:00 2001 From: Babneet Singh Date: Mon, 13 Nov 2023 13:34:45 -0800 Subject: [PATCH 8/8] Reduce the number of park and unpark iterations in ParkALot ParkALot launches a number of virtual threads and expects each thread to park and unpark 500_000 times. The test takes a lot of time to execute, leading to timeouts in Jenkins testing. The number of park and unpark iterations is reduced to 5_000, which is sufficient to verify functional behaviour. Further, the reduced iterations allow the test to pass in a reasonable time in Jenkins testing. vm.debug is set to false for OpenJ9. So, the vm.debug=true variant is left unmodified. Related: https://github.com/eclipse-openj9/openj9/issues/18444 Signed-off-by: Babneet Singh --- test/jdk/java/lang/Thread/virtual/stress/ParkALot.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java b/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java index a5eb98d207d..cee6795b330 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/ParkALot.java @@ -20,12 +20,17 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2023, 2023 All Rights Reserved + * =========================================================================== + */ /* * @test * @summary Stress test parking and unparking * @requires vm.debug != true - * @run main/othervm ParkALot 500000 + * @run main/othervm ParkALot 5000 */ /*