-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from babsingh/main1
Backport Virtual Thread Changes from JDK-next
- Loading branch information
Showing
10 changed files
with
1,160 additions
and
210 deletions.
There are no files selected for viewing
174 changes: 90 additions & 84 deletions
174
src/java.base/share/classes/java/lang/VirtualThread.java
Large diffs are not rendered by default.
Oops, something went wrong.
416 changes: 416 additions & 0 deletions
416
test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java
Large diffs are not rendered by default.
Oops, something went wrong.
51 changes: 51 additions & 0 deletions
51
test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/libGetThreadStateTest.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
210 changes: 210 additions & 0 deletions
210
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Thread> 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 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.TIMED_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"); | ||
} | ||
} | ||
|
||
} | ||
|
109 changes: 109 additions & 0 deletions
109
test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* | ||
* 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. | ||
*/ | ||
/* | ||
* =========================================================================== | ||
* (c) Copyright IBM Corp. 2023, 2023 All Rights Reserved | ||
* =========================================================================== | ||
*/ | ||
|
||
#include <cstring> | ||
#include <jvmti.h> | ||
#include <atomic> | ||
#include "jvmti_common.h" | ||
|
||
extern "C" { | ||
|
||
static jvmtiEnv *jvmti = nullptr; | ||
static std::atomic<int> thread_end_cnt(0); | ||
static std::atomic<int> thread_unmount_cnt(0); | ||
static std::atomic<int> 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<void **>(&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 = set_ext_event_notification_mode(jvmti, JVMTI_ENABLE, "VirtualThreadMount", NULL); | ||
check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadMount"); | ||
|
||
err = set_ext_event_notification_mode(jvmti, JVMTI_ENABLE, "VirtualThreadUnmount", NULL); | ||
check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadUnmount"); | ||
|
||
LOG("vthread events enabled\n"); | ||
return JVMTI_ERROR_NONE; | ||
} | ||
|
||
} // extern "C" | ||
|
Oops, something went wrong.