8303242: ThreadMXBean issues with virtual threads

Reviewed-by: mchung, pchilanomate
This commit is contained in:
Alan Bateman 2023-03-04 07:33:33 +00:00
parent 5b2e2e4695
commit 629a9053f0
8 changed files with 397 additions and 250 deletions

View File

@ -450,6 +450,16 @@ static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
}
}
// Returns true if the JavaThread's Java object is a platform thread
static bool is_platform_thread(JavaThread* jt) {
if (jt != nullptr) {
oop thread_obj = jt->threadObj();
return (thread_obj != nullptr) && !thread_obj->is_a(vmClasses::BoundVirtualThread_klass());
} else {
return false;
}
}
#if INCLUDE_MANAGEMENT
static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) {
@ -462,6 +472,11 @@ static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) {
}
}
// Returns true if the ThreadSnapshot's Java object is a platform thread
static bool is_platform_thread(ThreadSnapshot* ts) {
oop thread_obj = ts->threadObj();
return (thread_obj != nullptr) && !thread_obj->is_a(vmClasses::BoundVirtualThread_klass());
}
static MemoryManager* get_memory_manager_from_jobject(jobject obj, TRAPS) {
if (obj == nullptr) {
@ -1044,7 +1059,7 @@ static void do_thread_dump(ThreadDumpResult* dump_result,
for (int i = 0; i < num_threads; i++) {
jlong tid = ids_ah->long_at(i);
JavaThread* jt = tlh.list()->find_JavaThread_from_java_tid(tid);
oop thread_obj = (jt != nullptr ? jt->threadObj() : (oop)nullptr);
oop thread_obj = is_platform_thread(jt) ? jt->threadObj() : (oop)nullptr;
instanceHandle threadObj_h(THREAD, (instanceOop) thread_obj);
thread_handle_array->append(threadObj_h);
}
@ -1145,8 +1160,8 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo
// For each thread, create an java/lang/management/ThreadInfo object
// and fill with the thread information
if (ts->threadObj() == nullptr) {
// if the thread does not exist or now it is terminated, set threadinfo to null
if (!is_platform_thread(ts)) {
// if the thread does not exist, has terminated, or is a virtual thread, then set threadinfo to null
infoArray_h->obj_at_put(index, nullptr);
continue;
}
@ -1211,8 +1226,8 @@ JVM_ENTRY(jobjectArray, jmm_DumpThreads(JNIEnv *env, jlongArray thread_ids, jboo
int index = 0;
for (ThreadSnapshot* ts = dump_result.snapshots(); ts != nullptr; ts = ts->next(), index++) {
if (ts->threadObj() == nullptr) {
// if the thread does not exist or now it is terminated, set threadinfo to null
if (!is_platform_thread(ts)) {
// if the thread does not exist, has terminated, or is a virtual thread, then set threadinfo to null
result_h->obj_at_put(index, nullptr);
continue;
}
@ -1408,7 +1423,7 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTime(JNIEnv *env, jlong thread_id))
} else {
ThreadsListHandle tlh;
java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
if (java_thread != nullptr) {
if (is_platform_thread(java_thread)) {
return os::thread_cpu_time((Thread*) java_thread);
}
}
@ -2101,8 +2116,7 @@ JVM_ENTRY(jlong, jmm_GetOneThreadAllocatedMemory(JNIEnv *env, jlong thread_id))
ThreadsListHandle tlh;
JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
if (java_thread != nullptr) {
if (is_platform_thread(java_thread)) {
return java_thread->cooked_allocated_bytes();
}
return -1;
@ -2141,7 +2155,7 @@ JVM_ENTRY(void, jmm_GetThreadAllocatedMemory(JNIEnv *env, jlongArray ids,
ThreadsListHandle tlh;
for (int i = 0; i < num_threads; i++) {
JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i));
if (java_thread != nullptr) {
if (is_platform_thread(java_thread)) {
sizeArray_h->long_at_put(i, java_thread->cooked_allocated_bytes());
}
}
@ -2169,11 +2183,8 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTimeWithKind(JNIEnv *env, jlong thread_id, jboo
} else {
ThreadsListHandle tlh;
java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
if (java_thread != nullptr) {
oop thread_obj = java_thread->threadObj();
if (thread_obj != nullptr && !thread_obj->is_a(vmClasses::BaseVirtualThread_klass())) {
return os::thread_cpu_time((Thread*) java_thread, user_sys_cpu_time != 0);
}
if (is_platform_thread(java_thread)) {
return os::thread_cpu_time((Thread*) java_thread, user_sys_cpu_time != 0);
}
}
return -1;
@ -2215,7 +2226,7 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
ThreadsListHandle tlh;
for (int i = 0; i < num_threads; i++) {
JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i));
if (java_thread != nullptr) {
if (is_platform_thread(java_thread)) {
timeArray_h->long_at_put(i, os::thread_cpu_time((Thread*)java_thread,
user_sys_cpu_time != 0));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -92,7 +92,6 @@ import sun.management.ThreadInfoCompositeData;
*/
public class ThreadInfo {
private boolean virtual; // accessed by ThreadImpl
private String threadName;
private long threadId;
private long blockedTime;
@ -224,7 +223,6 @@ public class ThreadInfo {
StackTraceElement[] stackTrace,
MonitorInfo[] lockedMonitors,
LockInfo[] lockedSynchronizers) {
this.virtual = t.isVirtual();
this.threadId = t.threadId();
this.threadName = t.getName();
this.threadState = ManagementFactoryHelper.toThreadState(state);

View File

@ -65,17 +65,16 @@ import java.util.Map;
* of thread IDs as the input parameter and return per-thread information.
*
* <h2>Thread CPU time</h2>
* A Java virtual machine implementation may support measuring
* the CPU time for the current thread, for any thread, or for no threads.
* A Java virtual machine implementation may support measuring the CPU time
* for the current platform thread, for any platform thread, or for no threads.
*
* <p>
* The {@link #isThreadCpuTimeSupported} method can be used to determine
* if a Java virtual machine supports measuring of the CPU time for any
* thread. The {@link #isCurrentThreadCpuTimeSupported} method can
* be used to determine if a Java virtual machine supports measuring of
* the CPU time for the current thread.
* A Java virtual machine implementation that supports CPU time measurement
* for any thread will also support that for the current thread.
* platform thread. The {@link #isCurrentThreadCpuTimeSupported()} method
* can be used to determine if a Java virtual machine supports measuring of
* the CPU time with the {@link #getCurrentThreadCpuTime()} and
* {@link #getCurrentThreadUserTime()} methods from a platform thread.
*
* <p> The CPU time provided by this interface has nanosecond precision
* but not necessarily nanosecond accuracy.
@ -411,8 +410,9 @@ public interface ThreadMXBean extends PlatformManagedObject {
* {@link #getThreadCpuTime getThreadCpuTime}(Thread.currentThread().threadId());
* </pre></blockquote>
*
* @return the total CPU time for the current thread if CPU time
* measurement is enabled; {@code -1} otherwise.
* @return the total CPU time for the current thread if the current
* thread is a platform thread and if CPU time measurement is enabled;
* {@code -1} otherwise.
*
* @throws UnsupportedOperationException if the Java
* virtual machine does not support CPU time measurement for
@ -438,8 +438,9 @@ public interface ThreadMXBean extends PlatformManagedObject {
* {@link #getThreadUserTime getThreadUserTime}(Thread.currentThread().threadId());
* </pre></blockquote>
*
* @return the user-level CPU time for the current thread if CPU time
* measurement is enabled; {@code -1} otherwise.
* @return the user-level CPU time for the current thread if the current
* thread is a platform thread and if CPU time measurement is enabled;
* {@code -1} otherwise.
*
* @throws UnsupportedOperationException if the Java
* virtual machine does not support CPU time measurement for
@ -472,8 +473,8 @@ public interface ThreadMXBean extends PlatformManagedObject {
* where CPU time measurement starts.
*
* @param id the thread ID of a thread
* @return the total CPU time for a thread of the specified ID
* if the thread of the specified ID exists, the thread is alive,
* @return the total CPU time for a thread of the specified ID if the
* thread of the specified ID is a platform thread, the thread is alive,
* and CPU time measurement is enabled;
* {@code -1} otherwise.
*
@ -507,8 +508,8 @@ public interface ThreadMXBean extends PlatformManagedObject {
* where CPU time measurement starts.
*
* @param id the thread ID of a thread
* @return the user-level CPU time for a thread of the specified ID
* if the thread of the specified ID exists, the thread is alive,
* @return the user-level CPU time for a thread of the specified ID if the
* thread of the specified ID is a platform thread, the thread is alive,
* and CPU time measurement is enabled;
* {@code -1} otherwise.
*
@ -526,29 +527,31 @@ public interface ThreadMXBean extends PlatformManagedObject {
/**
* Tests if the Java virtual machine implementation supports CPU time
* measurement for any thread.
* measurement for any platform thread.
* A Java virtual machine implementation that supports CPU time
* measurement for any thread will also support CPU time
* measurement for the current thread.
* measurement for any platform thread will also support CPU time
* measurement for the current thread, when the current thread is a
* platform thread.
*
* @return
* {@code true}
* if the Java virtual machine supports CPU time
* measurement for any thread;
* measurement for any platform thread;
* {@code false} otherwise.
*/
public boolean isThreadCpuTimeSupported();
/**
* Tests if the Java virtual machine supports CPU time
* measurement for the current thread.
* Tests if the Java virtual machine supports CPU time measurement from
* a platform thread with the {@link #getCurrentThreadCpuTime()} and
* {@link #getCurrentThreadUserTime()} methods.
* This method returns {@code true} if {@link #isThreadCpuTimeSupported}
* returns {@code true}.
*
* @return
* {@code true}
* if the Java virtual machine supports CPU time
* measurement for current thread;
* if the Java virtual machine supports CPU time measurement
* of the current platform thread;
* {@code false} otherwise.
*/
public boolean isCurrentThreadCpuTimeSupported();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -30,6 +30,7 @@ import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.stream.Stream;
import javax.management.ObjectName;
import java.util.Arrays;
import java.util.Objects;
/**
@ -130,7 +131,7 @@ public class ThreadImpl implements ThreadMXBean {
public long[] getAllThreadIds() {
Util.checkMonitorAccess();
Thread[] threads = getThreads();
return platformThreadIds(threads);
return threadIds(threads);
}
@Override
@ -141,12 +142,7 @@ public class ThreadImpl implements ThreadMXBean {
@Override
public ThreadInfo getThreadInfo(long id, int maxDepth) {
long[] ids = new long[] { id };
ThreadInfo ti = getThreadInfo(ids, maxDepth)[0];
if (ti == null || Util.isVirtual(ti)) {
return null;
} else {
return ti;
}
return getThreadInfo(ids, maxDepth)[0];
}
@Override
@ -190,7 +186,6 @@ public class ThreadImpl implements ThreadMXBean {
} else {
getThreadInfo1(ids, maxDepth, infos);
}
nullVirtualThreads(infos);
return infos;
}
@ -220,10 +215,6 @@ public class ThreadImpl implements ThreadMXBean {
}
private boolean verifyCurrentThreadCpuTime() {
// check if Thread CPU time measurement is supported.
if (Thread.currentThread().isVirtual()) {
throw new UnsupportedOperationException("Not supported by virtual threads");
}
if (!isCurrentThreadCpuTimeSupported()) {
throw new UnsupportedOperationException(
"Current thread CPU time measurement is not supported.");
@ -233,7 +224,7 @@ public class ThreadImpl implements ThreadMXBean {
@Override
public long getCurrentThreadCpuTime() {
if (verifyCurrentThreadCpuTime()) {
if (verifyCurrentThreadCpuTime() && !Thread.currentThread().isVirtual()) {
return getThreadTotalCpuTime0(0);
}
return -1;
@ -283,11 +274,7 @@ public class ThreadImpl implements ThreadMXBean {
long id = ids[0];
Thread thread = Thread.currentThread();
if (id == thread.threadId()) {
if (thread.isVirtual()) {
times[0] = -1;
} else {
times[0] = getThreadTotalCpuTime0(0);
}
times[0] = thread.isVirtual() ? -1L : getThreadTotalCpuTime0(0);
} else {
times[0] = getThreadTotalCpuTime0(id);
}
@ -300,7 +287,7 @@ public class ThreadImpl implements ThreadMXBean {
@Override
public long getCurrentThreadUserTime() {
if (verifyCurrentThreadCpuTime()) {
if (verifyCurrentThreadCpuTime() && !Thread.currentThread().isVirtual()) {
return getThreadUserCpuTime0(0);
}
return -1;
@ -326,11 +313,7 @@ public class ThreadImpl implements ThreadMXBean {
long id = ids[0];
Thread thread = Thread.currentThread();
if (id == thread.threadId()) {
if (thread.isVirtual()) {
times[0] = -1;
} else {
times[0] = getThreadUserCpuTime0(0);
}
times[0] = thread.isVirtual() ? -1L : getThreadTotalCpuTime0(0);
} else {
times[0] = getThreadUserCpuTime0(id);
}
@ -376,11 +359,7 @@ public class ThreadImpl implements ThreadMXBean {
if (verified) {
Thread thread = Thread.currentThread();
if (id == thread.threadId()) {
if (thread.isVirtual()) {
return -1L;
} else {
return getThreadAllocatedMemory0(0);
}
return thread.isVirtual() ? -1L : getThreadAllocatedMemory0(0);
} else {
return getThreadAllocatedMemory0(id);
}
@ -431,19 +410,16 @@ public class ThreadImpl implements ThreadMXBean {
* of threads is empty.
*/
private long[] threadsToIds(Thread[] threads) {
if (threads != null) {
long[] tids = platformThreadIds(threads);
if (tids.length > 0) {
return tids;
}
if (threads != null && threads.length > 0) {
return threadIds(threads);
} else {
return null;
}
return null;
}
@Override
public long[] findMonitorDeadlockedThreads() {
Util.checkMonitorAccess();
Thread[] threads = findMonitorDeadlockedThreads0();
return threadsToIds(threads);
}
@ -496,12 +472,10 @@ public class ThreadImpl implements ThreadMXBean {
public ThreadInfo[] getThreadInfo(long[] ids,
boolean lockedMonitors,
boolean lockedSynchronizers) {
ThreadInfo[] infos = dumpThreads0(ids, lockedMonitors, lockedSynchronizers,
Integer.MAX_VALUE);
nullVirtualThreads(infos);
return infos;
return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, Integer.MAX_VALUE);
}
@Override
public ThreadInfo[] getThreadInfo(long[] ids,
boolean lockedMonitors,
boolean lockedSynchronizers,
@ -516,19 +490,16 @@ public class ThreadImpl implements ThreadMXBean {
if (ids.length == 0) return new ThreadInfo[0];
verifyDumpThreads(lockedMonitors, lockedSynchronizers);
ThreadInfo[] infos = dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
nullVirtualThreads(infos);
return infos;
return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
}
@Override
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
boolean lockedSynchronizers) {
ThreadInfo[] infos = dumpAllThreads(lockedMonitors, lockedSynchronizers,
Integer.MAX_VALUE);
return platformThreads(infos);
return dumpAllThreads(lockedMonitors, lockedSynchronizers, Integer.MAX_VALUE);
}
@Override
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
boolean lockedSynchronizers,
int maxDepth) {
@ -538,7 +509,9 @@ public class ThreadImpl implements ThreadMXBean {
}
verifyDumpThreads(lockedMonitors, lockedSynchronizers);
ThreadInfo[] infos = dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth);
return platformThreads(infos);
return Arrays.stream(infos)
.filter(ti -> ti != null)
.toArray(ThreadInfo[]::new);
}
// VM support where maxDepth == -1 to request entire stack dump
@ -572,34 +545,11 @@ public class ThreadImpl implements ThreadMXBean {
}
/**
* Returns the thread identifiers of the platform threads in the given array.
* Returns the thread identifiers of the threads in the given array.
*/
private static long[] platformThreadIds(Thread[] threads) {
private static long[] threadIds(Thread[] threads) {
return Stream.of(threads)
.filter(t -> !t.isVirtual())
.mapToLong(Thread::threadId)
.toArray();
}
/**
* Returns the ThreadInfo objects from the given array that correspond to platform
* threads.
*/
private ThreadInfo[] platformThreads(ThreadInfo[] infos) {
return Stream.of(infos)
.filter(ti -> !Util.isVirtual(ti))
.toArray(ThreadInfo[]::new);
}
/**
* Set the elements of the given array to null if they correspond to a virtual thread.
*/
private static void nullVirtualThreads(ThreadInfo[] infos) {
for (int i = 0; i < infos.length; i++) {
ThreadInfo ti = infos[i];
if (ti != null && Util.isVirtual(ti)) {
infos[i] = null;
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -25,17 +25,11 @@
package sun.management;
import java.lang.reflect.Field;
import java.lang.management.ManagementPermission;
import java.lang.management.ThreadInfo;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import javax.management.ObjectName;
import javax.management.MalformedObjectNameException;
public class Util {
private Util() {} // there are no instances of this class
@ -86,31 +80,4 @@ public class Util {
public static void checkControlAccess() throws SecurityException {
checkAccess(controlPermission);
}
/**
* Returns true if the given ThreadInfo is for a virtual thread.
*/
public static boolean isVirtual(ThreadInfo threadInfo) {
try {
return (boolean) THREADINFO_VIRTUAL.get(threadInfo);
} catch (Exception e) {
throw new InternalError(e);
}
}
@SuppressWarnings("removal")
private static Field threadInfoVirtual() {
PrivilegedExceptionAction<Field> pa = () -> {
Field f = ThreadInfo.class.getDeclaredField("virtual");
f.setAccessible(true);
return f;
};
try {
return AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw new InternalError(e);
}
}
private static final Field THREADINFO_VIRTUAL = threadInfoVirtual();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 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
@ -53,9 +53,8 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean {
* @param ids an array of thread IDs.
* @return an array of long values, each of which is the amount of CPU
* time the thread whose ID is in the corresponding element of the input
* array of IDs has used,
* if the thread of a specified ID exists, the thread is alive,
* and CPU time measurement is enabled;
* array of IDs has used, if the thread of a specified ID is a platform
* thread, the thread is alive, and CPU time measurement is enabled;
* {@code -1} otherwise.
*
* @throws NullPointerException if {@code ids} is {@code null}
@ -87,9 +86,8 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean {
* @param ids an array of thread IDs.
* @return an array of long values, each of which is the amount of user
* mode CPU time the thread whose ID is in the corresponding element of
* the input array of IDs has used,
* if the thread of a specified ID exists, the thread is alive,
* and CPU time measurement is enabled;
* the input array of IDs has used, if the thread of a specified ID is a
* platform thread, the thread is alive, and CPU time measurement is enabled;
* {@code -1} otherwise.
*
* @throws NullPointerException if {@code ids} is {@code null}
@ -161,10 +159,9 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean {
*
* @param id the thread ID of a thread
* @return an approximation of the total memory allocated, in bytes, in
* heap memory for the thread with the specified ID
* if the thread with the specified ID exists, the thread is alive,
* and thread memory allocation measurement is enabled;
* {@code -1} otherwise.
* heap memory for the thread with the specified ID if the thread with the
* specified ID is a platform thread, the thread is alive, and thread memory
* allocation measurement is enabled; {@code -1} otherwise.
*
* @throws IllegalArgumentException if {@code id} {@code <=} {@code 0}.
* @throws UnsupportedOperationException if the Java virtual

View File

@ -0,0 +1,169 @@
/*
* 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 8284161 8303242
* @summary Test com.sun.management.ThreadMXBean with virtual threads
* @enablePreview
* @library /test/lib
* @run junit/othervm VirtualThreads
*/
/**
* @test id=no-vmcontinuations
* @requires vm.continuations
* @enablePreview
* @library /test/lib
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreads
*/
import java.lang.management.ManagementFactory;
import java.util.concurrent.locks.LockSupport;
import com.sun.management.ThreadMXBean;
import jdk.test.lib.thread.VThreadRunner;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;
public class VirtualThreads {
/**
* Test that ThreadMXBean::getCurrentThreadAllocatedBytes() returns -1 when
* invoked from a virtual thread.
*/
@Test
void testGetCurrentThreadAllocatedBytes() throws Exception {
ThreadMXBean bean = ManagementFactory.getPlatformMXBean(ThreadMXBean.class);
assumeTrue(bean.isThreadAllocatedMemorySupported(),
"Thread memory allocation measurement not supported");
VThreadRunner.run(() -> {
assertEquals(-1L, bean.getCurrentThreadAllocatedBytes());
});
}
/**
* Test that ThreadMXBean.getThreadAllocatedBytes(long) returns -1 when invoked
* with the thread ID of a virtual thread.
*/
@Test
void testGetThreadAllocatedBytes1() {
ThreadMXBean bean = ManagementFactory.getPlatformMXBean(ThreadMXBean.class);
assumeTrue(bean.isThreadAllocatedMemorySupported(),
"Thread memory allocation measurement not supported");
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long allocated = bean.getThreadAllocatedBytes(vthread.threadId());
assertEquals(-1L, allocated);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean.getThreadAllocatedBytes(long) returns -1 when invoked
* by a virtual thread with its own thread id.
*/
@Test
void testGetThreadAllocatedBytes2() throws Exception {
ThreadMXBean bean = ManagementFactory.getPlatformMXBean(ThreadMXBean.class);
assumeTrue(bean.isThreadAllocatedMemorySupported(),
"Thread memory allocation measurement not supported");
VThreadRunner.run(() -> {
long tid = Thread.currentThread().threadId();
long allocated = bean.getThreadAllocatedBytes(tid);
assertEquals(-1L, allocated);
});
}
/**
* Test that ThreadMXBean.getThreadAllocatedBytes(long[]) returns -1 for
* elements that correspond to a virtual thread.
*/
@Test
void testGetThreadAllocatedBytes3() {
ThreadMXBean bean = ManagementFactory.getPlatformMXBean(ThreadMXBean.class);
assumeTrue(bean.isThreadAllocatedMemorySupported(),
"Thread memory allocation measurement not supported");
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid0 = Thread.currentThread().threadId();
long tid1 = vthread.threadId();
long[] tids = new long[] { tid0, tid1 };
long[] allocated = bean.getThreadAllocatedBytes(tids);
assertTrue(allocated[0] >= 0L);
assertEquals(-1L, allocated[1]);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean.getThreadCpuTime(long[]) returns -1 for
* elements that correspond to a virtual thread.
*/
@Test
void testGetThreadCpuTime() {
ThreadMXBean bean = ManagementFactory.getPlatformMXBean(ThreadMXBean.class);
assumeTrue(bean.isThreadCpuTimeSupported(), "Thread CPU time measurement not supported");
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid0 = Thread.currentThread().threadId();
long tid1 = vthread.threadId();
long[] tids = new long[] { tid0, tid1 };
long[] cpuTimes = bean.getThreadCpuTime(tids);
assertTrue(cpuTimes[0] >= 0L);
assertEquals(-1L, cpuTimes[1]);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean.getThreadUserTime(long[])returns -1 for
* elements that correspond to a virtual thread.
*/
@Test
void testGetThreadUserTime() {
ThreadMXBean bean = ManagementFactory.getPlatformMXBean(ThreadMXBean.class);
assumeTrue(bean.isThreadCpuTimeSupported(), "Thread CPU time measurement not supported");
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid0 = Thread.currentThread().threadId();
long tid1 = vthread.threadId();
long[] tids = new long[] { tid0, tid1 };
long[] userTimes = bean.getThreadUserTime(tids);
assertTrue(userTimes[0] >= 0L);
assertEquals(-1L, userTimes[1]);
} finally {
LockSupport.unpark(vthread);
}
}
}

View File

@ -23,10 +23,11 @@
/**
* @test id=default
* @bug 8284161 8290562
* @bug 8284161 8290562 8303242
* @summary Test java.lang.management.ThreadMXBean with virtual threads
* @enablePreview
* @modules java.base/java.lang:+open java.management
* @library /test/lib
* @run junit/othervm VirtualThreads
*/
@ -35,6 +36,7 @@
* @requires vm.continuations
* @enablePreview
* @modules java.base/java.lang:+open java.management
* @library /test/lib
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreads
*/
@ -45,24 +47,52 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.Selector;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;
import jdk.test.lib.thread.VThreadRunner;
import org.junit.jupiter.api.Test;
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.*;
public class VirtualThreads {
/**
* Test that ThreadMXBean::getAllThreadsIds does not include thread ids for
* virtual threads.
* Test that ThreadMXBean.dumpAllThreads does not include virtual threads.
*/
@ParameterizedTest
@ValueSource(ints = {0, Integer.MAX_VALUE})
void testDumpAllThreads(int maxDepth) {
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(false, false, maxDepth);
Set<Long> tids = Arrays.stream(infos)
.map(ThreadInfo::getThreadId)
.collect(Collectors.toSet());
// current thread should be included
assertTrue(tids.contains(Thread.currentThread().threadId()));
// virtual thread should not be included
assertFalse(tids.contains(vthread.threadId()));
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean::getAllThreadsIds does not include virtual threads.
*/
@Test
void testGetAllThreadIds() throws Exception {
void testGetAllThreadIds() {
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long[] tids = ManagementFactory.getThreadMXBean().getAllThreadIds();
@ -80,39 +110,83 @@ public class VirtualThreads {
}
/**
* Test that ThreadMXBean.getThreadInfo(long) returns null for a virtual thread.
* Test that ThreadMXBean.getThreadInfo(long, maxDepth) returns null for a virtual
* thread.
*/
@Test
void testGetThreadInfo1() throws Exception {
@ParameterizedTest
@ValueSource(ints = {0, Integer.MAX_VALUE})
void testGetThreadInfo1(int maxDepth) {
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid = vthread.threadId();
ThreadInfo info = ManagementFactory.getThreadMXBean().getThreadInfo(tid);
assertTrue(info == null);
ThreadInfo info = ManagementFactory.getThreadMXBean().getThreadInfo(tid, maxDepth);
assertNull(info);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean.getThreadInfo(long) returns null when invoked by a virtual
* thread with its own thread id.
* Test that ThreadMXBean.getThreadInfo(long, maxDepth) returns null when invoked
* by a virtual thread with its own thread id.
*/
@Test
void testGetThreadInfo2() throws Exception {
runInVirtualThread(() -> {
@ParameterizedTest
@ValueSource(ints = {0, Integer.MAX_VALUE})
void testGetThreadInfo2(int maxDepth) throws Exception {
VThreadRunner.run(() -> {
long tid = Thread.currentThread().threadId();
ThreadInfo info = ManagementFactory.getThreadMXBean().getThreadInfo(tid);
assertTrue(info == null);
ThreadInfo info = ManagementFactory.getThreadMXBean().getThreadInfo(tid, maxDepth);
assertNull(info);
});
}
/**
* Test that ThreadMXBean.getThreadInfo(long[], maxDepth) returns a null ThreadInfo
* for elements that correspond to a virtual thread.
*/
@ParameterizedTest
@ValueSource(ints = {0, Integer.MAX_VALUE})
void testGetThreadInfo3(int maxDepth) {
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid0 = Thread.currentThread().threadId();
long tid1 = vthread.threadId();
long[] tids = new long[] { tid0, tid1 };
ThreadInfo[] infos = ManagementFactory.getThreadMXBean().getThreadInfo(tids, maxDepth);
assertEquals(tid0, infos[0].getThreadId());
assertNull(infos[1]);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean.getThreadInfo(long[], boolean, boolean, maxDepth) returns
* a null ThreadInfo for elements that correspond to a virtual thread.
*/
@ParameterizedTest
@ValueSource(ints = {0, Integer.MAX_VALUE})
void testGetThreadInfo4(int maxDepth) {
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid0 = Thread.currentThread().threadId();
long tid1 = vthread.threadId();
long[] tids = new long[] { tid0, tid1 };
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.getThreadInfo(tids, false, false, maxDepth);
assertEquals(tid0, infos[0].getThreadId());
assertNull(infos[1]);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test ThreadMXBean.getThreadInfo(long) with the thread id of a carrier thread.
* The stack frames of the virtual thread should not be returned.
*/
@Test
void testGetThreadInfo3() throws Exception {
void testGetThreadInfoCarrierThread() throws Exception {
assumeTrue(supportsCustomScheduler(), "No support for custom schedulers");
try (ExecutorService pool = Executors.newFixedThreadPool(1)) {
var carrierRef = new AtomicReference<Thread>();
@ -146,40 +220,24 @@ public class VirtualThreads {
assertFalse(contains(stack, "java.nio.channels.Selector"));
// carrier should not be holding any monitors
assertTrue(info.getLockedMonitors().length == 0);
assertEquals(0, info.getLockedMonitors().length);
}
}
}
/**
* Test that ThreadMXBean.getThreadInfo(long[]) returns a null ThreadInfo for
* elements that correspond to a virtual thread.
*/
@Test
void testGetThreadInfo4() throws Exception {
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid0 = Thread.currentThread().threadId();
long tid1 = vthread.threadId();
long[] tids = new long[] { tid0, tid1 };
ThreadInfo[] infos = ManagementFactory.getThreadMXBean().getThreadInfo(tids);
assertTrue(infos[0].getThreadId() == tid0);
assertTrue(infos[1] == null);
} finally {
LockSupport.unpark(vthread);
}
}
/**
* Test that ThreadMXBean.getThreadCpuTime(long) returns -1 for a virtual thread.
*/
@Test
void testGetThreadCpuTime1() {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isThreadCpuTimeSupported(), "Thread CPU time measurement not supported");
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid = vthread.threadId();
long cpuTime = ManagementFactory.getThreadMXBean().getThreadCpuTime(tid);
assertTrue(cpuTime == -1L);
long cpuTime = bean.getThreadCpuTime(tid);
assertEquals(-1L, cpuTime);
} finally {
LockSupport.unpark(vthread);
}
@ -191,10 +249,13 @@ public class VirtualThreads {
*/
@Test
void testGetThreadCpuTime2() throws Exception {
runInVirtualThread(() -> {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isThreadCpuTimeSupported(), "Thread CPU time measurement not supported");
VThreadRunner.run(() -> {
long tid = Thread.currentThread().threadId();
long cpuTime = ManagementFactory.getThreadMXBean().getThreadCpuTime(tid);
assertTrue(cpuTime == -1L);
long cpuTime = bean.getThreadCpuTime(tid);
assertEquals(-1L, cpuTime);
});
}
@ -203,11 +264,14 @@ public class VirtualThreads {
*/
@Test
void testGetThreadUserTime1() {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isThreadCpuTimeSupported(), "Thread CPU time measurement not supported");
Thread vthread = Thread.startVirtualThread(LockSupport::park);
try {
long tid = vthread.threadId();
long userTime = ManagementFactory.getThreadMXBean().getThreadUserTime(tid);
assertTrue(userTime == -1L);
assertEquals(-1L, userTime);
} finally {
LockSupport.unpark(vthread);
}
@ -219,73 +283,61 @@ public class VirtualThreads {
*/
@Test
void testGetThreadUserTime2() throws Exception {
runInVirtualThread(() -> {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isThreadCpuTimeSupported(), "Thread CPU time measurement not supported");
VThreadRunner.run(() -> {
long tid = Thread.currentThread().threadId();
long userTime = ManagementFactory.getThreadMXBean().getThreadUserTime(tid);
assertTrue(userTime == -1L);
assertEquals(-1L, userTime);
});
}
/**
* Test that ThreadMXBean::getCurrentThreadCpuTime throws UOE when invoked
* on a virtual thread.
* Test that ThreadMXBean::isCurrentThreadCpuTimeSupported returns true when
* CPU time measurement for the current thread is supported.
*/
@Test
void testIsCurrentThreadCpuTimeSupported() throws Exception {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isCurrentThreadCpuTimeSupported(),
"Thread CPU time measurement for the current thread not supported");
VThreadRunner.run(() -> {
assertTrue(bean.isCurrentThreadCpuTimeSupported());
});
}
/**
* Test that ThreadMXBean::getCurrentThreadCpuTime returns -1 when invoked
* from a virtual thread.
*/
@Test
void testGetCurrentThreadCpuTime() throws Exception {
runInVirtualThread(() -> {
assertThrows(UnsupportedOperationException.class,
() -> ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime());
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isCurrentThreadCpuTimeSupported(),
"Thread CPU time measurement for the current thread not supported");
VThreadRunner.run(() -> {
assertEquals(-1L, bean.getCurrentThreadCpuTime());
});
}
/**
* Test that ThreadMXBean::getCurrentThreadUserTime throws UOE when
* invoked on a virtual thread.
* Test that ThreadMXBean::getCurrentThreadUserTime returns -1 when invoked
* from a virtual thread.
*/
@Test
void testGetCurrentThreadUserTime() throws Exception {
runInVirtualThread(() -> {
assertThrows(UnsupportedOperationException.class,
() -> ManagementFactory.getThreadMXBean().getCurrentThreadUserTime());
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
assumeTrue(bean.isCurrentThreadCpuTimeSupported(),
"Thread CPU time measurement for the current thread not supported");
VThreadRunner.run(() -> {
assertEquals(-1L, bean.getCurrentThreadUserTime());
});
}
/**
* Test that ThreadMXBean::getCurrentThreadAllocatedBytes returns -1 when
* invoked on a virtual thread.
*/
@Test
void testGetCurrentThreadAllocatedBytes() throws Exception {
runInVirtualThread(() -> {
long allocated = ManagementFactory.getPlatformMXBean(com.sun.management.ThreadMXBean.class)
.getCurrentThreadAllocatedBytes();
assertTrue(allocated == -1L);
});
}
interface ThrowingRunnable {
void run() throws Exception;
}
private static void runInVirtualThread(ThrowingRunnable task) throws Exception {
AtomicReference<Exception> exc = new AtomicReference<>();
Runnable target = () -> {
try {
task.run();
} catch (Error e) {
exc.set(new RuntimeException(e));
} catch (Exception e) {
exc.set(e);
}
};
Thread thread = Thread.ofVirtual().start(target);
thread.join();
Exception e = exc.get();
if (e != null) {
throw e;
}
}
private static boolean contains(StackTraceElement[] stack, String className) {
return Arrays.stream(stack)
.map(StackTraceElement::getClassName)