8338890: Add monitoring/management interface for the virtual thread scheduler

Reviewed-by: kevinw
This commit is contained in:
Alan Bateman 2024-09-10 07:23:35 +00:00
parent 5e822c24bb
commit 7e2bcf6d00
23 changed files with 711 additions and 53 deletions

View File

@ -65,6 +65,7 @@ import java.util.PropertyPermission;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
@ -2766,6 +2767,10 @@ public final class System {
}
}
public Executor virtualThreadDefaultScheduler() {
return VirtualThread.defaultScheduler();
}
public StackWalker newStackWalkerInstance(Set<StackWalker.Option> options,
ContinuationScope contScope,
Continuation continuation) {

View File

@ -142,6 +142,14 @@ final class VirtualThread extends BaseVirtualThread {
// termination object when joining, created lazily if needed
private volatile CountDownLatch termination;
/**
* Returns the default scheduler.
*/
static Executor defaultScheduler() {
return DEFAULT_SCHEDULER;
}
/**
* Returns the continuation scope used for virtual threads.
*/

View File

@ -44,6 +44,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.stream.Stream;
@ -601,6 +602,11 @@ public interface JavaLangAccess {
*/
void unparkVirtualThread(Thread thread);
/**
* Returns the virtual thread default scheduler.
*/
Executor virtualThreadDefaultScheduler();
/**
* Creates a new StackWalker
*/

View File

@ -172,6 +172,7 @@ module java.base {
jdk.jartool,
jdk.jlink,
jdk.jfr,
jdk.management,
jdk.net,
jdk.sctp,
jdk.crypto.cryptoki;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -41,6 +41,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.management.DynamicMBean;
import jdk.management.VirtualThreadSchedulerMXBean;
import sun.management.ManagementFactoryHelper;
import sun.management.spi.PlatformMBeanProvider;
@ -163,6 +164,41 @@ public final class PlatformMBeanProviderImpl extends PlatformMBeanProvider {
}
});
/**
* VirtualThreadSchedulerMXBean.
*/
initMBeanList.add(new PlatformComponent<VirtualThreadSchedulerMXBean>() {
private final Set<Class<? extends VirtualThreadSchedulerMXBean>> mbeanInterfaces =
Set.of(VirtualThreadSchedulerMXBean.class);
private final Set<String> mbeanInterfaceNames =
Set.of(VirtualThreadSchedulerMXBean.class.getName());
private VirtualThreadSchedulerMXBean impl;
@Override
public Set<Class<? extends VirtualThreadSchedulerMXBean>> mbeanInterfaces() {
return mbeanInterfaces;
}
@Override
public Set<String> mbeanInterfaceNames() {
return mbeanInterfaceNames;
}
@Override
public String getObjectNamePattern() {
return "jdk.management:type=VirtualThreadScheduler";
}
@Override
public Map<String, VirtualThreadSchedulerMXBean> nameToMBeanMap() {
VirtualThreadSchedulerMXBean impl = this.impl;
if (impl == null) {
this.impl = impl = VirtualThreadSchedulerImpls.create();
}
return Map.of("jdk.management:type=VirtualThreadScheduler", impl);
}
});
/**
* OperatingSystemMXBean
*/

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.management.internal;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import javax.management.ObjectName;
import jdk.management.VirtualThreadSchedulerMXBean;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.ContinuationSupport;
import sun.management.Util;
/**
* Provides the implementation of the management interface for the JDK's default virtual
* thread scheduler.
*/
public class VirtualThreadSchedulerImpls {
private VirtualThreadSchedulerImpls() {
}
public static VirtualThreadSchedulerMXBean create() {
if (ContinuationSupport.isSupported()) {
return new VirtualThreadSchedulerImpl();
} else {
return new BoundVirtualThreadSchedulerImpl();
}
}
/**
* Base implementation of VirtualThreadSchedulerMXBean.
*/
private abstract static class BaseVirtualThreadSchedulerImpl
implements VirtualThreadSchedulerMXBean {
abstract void implSetParallelism(int size);
@Override
public final void setParallelism(int size) {
Util.checkControlAccess();
implSetParallelism(size);
}
@Override
public final ObjectName getObjectName() {
return Util.newObjectName("jdk.management:type=VirtualThreadScheduler");
}
@Override
public String toString() {
var sb = new StringBuilder("[parallelism=");
sb.append(getParallelism());
append(sb, "size", getPoolSize());
append(sb, "mounted", getMountedVirtualThreadCount());
append(sb, "queued", getQueuedVirtualThreadCount());
sb.append(']');
return sb.toString();
}
private void append(StringBuilder sb, String name, long value) {
sb.append(", ").append(name).append('=');
if (value >= 0) {
sb.append(value);
} else {
sb.append("<unavailable>");
}
}
}
/**
* Implementation of VirtualThreadSchedulerMXBean when virtual threads are
* implemented with continuations + scheduler.
*/
private static final class VirtualThreadSchedulerImpl extends BaseVirtualThreadSchedulerImpl {
/**
* Holder class for scheduler.
*/
private static class Scheduler {
private static final Executor scheduler =
SharedSecrets.getJavaLangAccess().virtualThreadDefaultScheduler();
static Executor instance() {
return scheduler;
}
}
@Override
public int getParallelism() {
if (Scheduler.instance() instanceof ForkJoinPool pool) {
return pool.getParallelism();
}
throw new InternalError(); // should not get here
}
@Override
void implSetParallelism(int size) {
if (Scheduler.instance() instanceof ForkJoinPool pool) {
pool.setParallelism(size);
if (pool.getPoolSize() < size) {
// FJ worker thread creation is on-demand
Thread.startVirtualThread(() -> { });
}
return;
}
throw new UnsupportedOperationException(); // should not get here
}
@Override
public int getPoolSize() {
if (Scheduler.instance() instanceof ForkJoinPool pool) {
return pool.getPoolSize();
}
return -1; // should not get here
}
@Override
public int getMountedVirtualThreadCount() {
if (Scheduler.instance() instanceof ForkJoinPool pool) {
return pool.getActiveThreadCount();
}
return -1; // should not get here
}
@Override
public long getQueuedVirtualThreadCount() {
if (Scheduler.instance() instanceof ForkJoinPool pool) {
return pool.getQueuedTaskCount() + pool.getQueuedSubmissionCount();
}
return -1L; // should not get here
}
}
/**
* Implementation of VirtualThreadSchedulerMXBean when virtual threads are backed
* by platform threads.
*/
private static final class BoundVirtualThreadSchedulerImpl extends BaseVirtualThreadSchedulerImpl {
@Override
public int getParallelism() {
return Integer.MAX_VALUE;
}
@Override
void implSetParallelism(int size) {
throw new UnsupportedOperationException();
}
@Override
public int getPoolSize() {
return -1;
}
@Override
public int getMountedVirtualThreadCount() {
return -1;
}
@Override
public long getQueuedVirtualThreadCount() {
return -1L;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -24,9 +24,8 @@
*/
/**
* This package contains the JDK's extension to
* the standard implementation of the
* {@link java.lang.management} API and also defines the management
* This package contains JDK extensions to the standard implementation of
* the {@link java.lang.management} API and also defines the management
* interface for some other components of the platform.
*
* <p>

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management;
import java.lang.management.ManagementFactory;
import java.lang.management.PlatformManagedObject;
import java.util.concurrent.ForkJoinPool;
import javax.management.MBeanServer;
import javax.management.ObjectName;
/**
* Management interface for the JDK's {@linkplain Thread##virtual-threads virtual thread}
* scheduler.
*
* <p> {@code VirtualThreadSchedulerMXBean} supports monitoring of the virtual thread
* scheduler's target parallelism, the {@linkplain Thread##platform-threads platform threads}
* used by the scheduler, and the number of virtual threads queued to the scheduler. It
* also supports dynamically changing the scheduler's target parallelism.
*
* <p> The management interface is registered with the platform {@link MBeanServer
* MBeanServer}. The {@link ObjectName ObjectName} that uniquely identifies the management
* interface within the {@code MBeanServer} is: "jdk.management:type=VirtualThreadScheduler".
*
* <p> Direct access to the MXBean interface can be obtained with
* {@link ManagementFactory#getPlatformMXBean(Class)}.
*
* @since 24
*/
public interface VirtualThreadSchedulerMXBean extends PlatformManagedObject {
/**
* {@return the scheduler's target parallelism}
*
* @see ForkJoinPool#getParallelism()
*/
int getParallelism();
/**
* Sets the scheduler's target parallelism.
*
* <p> Increasing the target parallelism allows the scheduler to use more platform
* threads to <i>carry</i> virtual threads if required. Decreasing the target parallelism
* reduces the number of threads that the scheduler may use to carry virtual threads.
*
* @apiNote If virtual threads are mounting and unmounting frequently then downward
* adjustment of the target parallelism will likely come into effect quickly.
*
* @implNote The JDK's virtual thread scheduler is a {@link ForkJoinPool}. Target
* parallelism defaults to the number of {@linkplain Runtime#availableProcessors()
* available processors}. The minimum target parallelism is 1, the maximum target
* parallelism is 32767.
*
* @param size the target parallelism level
* @throws IllegalArgumentException if size is less than the minimum, or
* greater than the maximum, supported by the scheduler
* @throws UnsupportedOperationException if changing the target
* parallelism is not suppored by the scheduler
*
* @see ForkJoinPool#setParallelism(int)
*/
void setParallelism(int size);
/**
* {@return the current number of platform threads that the scheduler has started
* but have not terminated; {@code -1} if not known}
*
* <p> The count includes the platform threads that are currently <i>carrying</i>
* virtual threads and the platform threads that are not currently carrying virtual
* threads. The thread count may be greater than the scheduler's target parallelism.
*
* @implNote The JDK's virtual thread scheduler is a {@link ForkJoinPool}. The pool
* size is the {@linkplain ForkJoinPool#getPoolSize() number of worker threads}.
*/
int getPoolSize();
/**
* {@return an estimate of the number of virtual threads that are currently
* <i>mounted</i> by the scheduler; {@code -1} if not known}
*
* <p> The number of mounted virtual threads is equal to the number of platform
* threads carrying virtual threads.
*
* @implNote This method may overestimate the number of virtual threads that are mounted.
*/
int getMountedVirtualThreadCount();
/**
* {@return an estimate of the number of virtual threads that are queued to
* the scheduler to start or continue execution; {@code -1} if not known}
*
* @implNote This method may overestimate the number of virtual threads that are
* queued to execute.
*/
long getQueuedVirtualThreadCount();
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/**
* This package contains JDK extensions to the standard implementation of the
* {@link java.lang.management} API.
*
* @since 24
*/
package jdk.management;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,9 +23,18 @@
* questions.
*/
import java.lang.management.ManagementFactory;
/**
* Defines JDK-specific management interfaces for the JVM.
*
* <p> This module contains the JDK's extensions to the standard implementation
* of the {@link java.lang.management} API and also defines the management
* interfaces for some other components of the platform.
*
* <p> All platform MBeans are registered in the <em>platform MBeanServer</em>
* which can be obtained with {@link ManagementFactory#getPlatformMBeanServer}.
*
* @moduleGraph
* @since 9
*/
@ -33,6 +42,7 @@ module jdk.management {
requires transitive java.management;
exports com.sun.management;
exports jdk.management;
provides sun.management.spi.PlatformMBeanProvider with
com.sun.management.internal.PlatformMBeanProviderImpl;

View File

@ -25,7 +25,7 @@
* @test id=default
* @bug 8312498
* @summary Basic test for JVMTI GetThreadState with virtual threads
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run junit/othervm/native --enable-native-access=ALL-UNNAMED GetThreadStateTest
*/
@ -33,7 +33,7 @@
/*
* @test id=no-vmcontinuations
* @requires vm.continuations
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations --enable-native-access=ALL-UNNAMED GetThreadStateTest
*/
@ -42,7 +42,7 @@ import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadPinner;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

View File

@ -28,7 +28,7 @@
* @requires vm.continuations
* @requires vm.jvmti
* @requires vm.compMode != "Xcomp"
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run main/othervm/native
* -Djdk.attach.allowAttachSelf=true -XX:+EnableDynamicAgentLoading VThreadEventTest attach

View File

@ -262,6 +262,7 @@ jdk_text = \
jdk_management = \
java/lang/management \
jdk/management \
com/sun/management \
sun/management \
jdk/internal/agent
@ -287,6 +288,7 @@ jdk_launcher = \
jdk_loom = \
com/sun/management/HotSpotDiagnosticMXBean \
com/sun/management/ThreadMXBean \
jdk/management/VirtualThreadSchedulerMXBean \
java/lang/Thread \
java/lang/ThreadGroup \
java/lang/management/ThreadMXBean \

View File

@ -25,7 +25,7 @@
* @test
* @summary Basic test for JFR jdk.VirtualThreadXXX events
* @requires vm.continuations
* @modules jdk.jfr java.base/java.lang:+open
* @modules jdk.jfr java.base/java.lang:+open jdk.management
* @library /test/lib
* @run junit/othervm --enable-native-access=ALL-UNNAMED JfrEvents
*/
@ -50,7 +50,7 @@ import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
import jdk.test.lib.thread.VThreadPinner;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadScheduler;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;

View File

@ -24,7 +24,7 @@
/*
* @test id=default
* @summary Test virtual thread with monitor enter/exit
* @modules java.base/java.lang:+open
* @modules java.base/java.lang:+open jdk.management
* @library /test/lib
* @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorEnterExit
*/
@ -43,7 +43,7 @@ import java.util.stream.IntStream;
import java.util.stream.Stream;
import jdk.test.lib.thread.VThreadPinner;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadScheduler;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;

View File

@ -24,7 +24,7 @@
/*
* @test id=default
* @summary Test virtual threads using Object.wait/notifyAll
* @modules java.base/java.lang:+open
* @modules java.base/java.lang:+open jdk.management
* @library /test/lib
* @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify
*/
@ -46,8 +46,7 @@ import java.util.stream.Stream;
import java.util.stream.Collectors;
import jdk.test.lib.thread.VThreadScheduler;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadPinner;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;

View File

@ -25,7 +25,7 @@
* @test id=default
* @bug 8284161 8286788 8321270
* @summary Test Thread API with virtual threads
* @modules java.base/java.lang:+open
* @modules java.base/java.lang:+open jdk.management
* @library /test/lib
* @run junit/othervm --enable-native-access=ALL-UNNAMED ThreadAPI
*/
@ -33,7 +33,7 @@
/*
* @test id=no-vmcontinuations
* @requires vm.continuations
* @modules java.base/java.lang:+open
* @modules java.base/java.lang:+open jdk.management
* @library /test/lib
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations
* --enable-native-access=ALL-UNNAMED ThreadAPI
@ -62,7 +62,7 @@ import java.util.stream.Stream;
import java.nio.channels.Selector;
import jdk.test.lib.thread.VThreadPinner;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadScheduler;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;

View File

@ -24,7 +24,7 @@
/**
* @test
* @summary Test parking when pinned and emitting the JFR VirtualThreadPinnedEvent throws
* @modules java.base/java.lang:+open java.base/jdk.internal.event
* @modules java.base/java.lang:+open java.base/jdk.internal.event jdk.management
* @library /test/lib
* @compile/module=java.base jdk/internal/event/VirtualThreadPinnedEvent.java
* @run junit/othervm --enable-native-access=ALL-UNNAMED VirtualThreadPinnedEventThrows
@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import jdk.internal.event.VirtualThreadPinnedEvent;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadPinner;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;

View File

@ -26,7 +26,7 @@
* @summary Stress test Thread.getStackTrace on virtual threads that are blocking or
* blocked on monitorenter
* @requires vm.debug != true
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run main/othervm GetStackTraceALotWhenBlocking 500000
*/
@ -34,7 +34,7 @@
/*
* @test
* @requires vm.debug == true & vm.continuations
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run main/othervm/timeout=300 GetStackTraceALotWhenBlocking 50000
*/
@ -42,7 +42,7 @@
import java.time.Instant;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
public class GetStackTraceALotWhenBlocking {

View File

@ -26,7 +26,7 @@
* @bug 8322818
* @summary Stress test Thread.getStackTrace on a virtual thread that is pinned
* @requires vm.debug != true
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run main/othervm --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 500000
*/
@ -34,7 +34,7 @@
/*
* @test
* @requires vm.debug == true
* @modules java.base/java.lang:+open
* @modules jdk.management
* @library /test/lib
* @run main/othervm/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 200000
*/
@ -42,7 +42,7 @@
import java.time.Instant;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
import jdk.test.lib.thread.VThreadPinner;
public class GetStackTraceALotWhenPinned {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,7 @@
* @bug 8284161 8287103
* @summary Test ThredMXBean.findMonitorDeadlockedThreads with cycles of
* platform and virtual threads in deadlock
* @modules java.base/java.lang:+open java.management
* @modules java.management jdk.management
* @library /test/lib
* @run main/othervm VirtualThreadDeadlocks PP
* @run main/othervm VirtualThreadDeadlocks PV
@ -36,7 +36,7 @@
/**
* @test id=no-vmcontinuations
* @requires vm.continuations
* @modules java.base/java.lang:+open java.management
* @modules java.management jdk.management
* @library /test/lib
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadDeadlocks PP
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadDeadlocks PV
@ -48,7 +48,7 @@ import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import java.util.concurrent.CyclicBarrier;
import java.util.stream.Stream;
import jdk.test.lib.thread.VThreadRunner;
import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
public class VirtualThreadDeadlocks {
private static final Object LOCK1 = new Object();

View File

@ -0,0 +1,260 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8338890
* @summary Basic test for jdk.management.VirtualThreadSchedulerMXBean
* @requires vm.continuations
* @modules jdk.management
* @library /test/lib
* @run junit/othervm VirtualThreadSchedulerMXBeanTest
*/
import java.lang.management.ManagementFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
import java.util.stream.IntStream;
import javax.management.MBeanServer;
import jdk.management.VirtualThreadSchedulerMXBean;
import jdk.test.lib.thread.VThreadRunner;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;
class VirtualThreadSchedulerMXBeanTest {
/**
* VirtualThreadSchedulerMXBean objects to test.
*/
private static Stream<VirtualThreadSchedulerMXBean> managedBeans() throws Exception {
var bean1 = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
var bean2 = ManagementFactory.newPlatformMXBeanProxy(server,
"jdk.management:type=VirtualThreadScheduler",
VirtualThreadSchedulerMXBean.class);
return Stream.of(bean1, bean2);
}
/**
* Test default parallelism.
*/
@ParameterizedTest
@MethodSource("managedBeans")
void testDefaultParallelism(VirtualThreadSchedulerMXBean bean) {
assertEquals(Runtime.getRuntime().availableProcessors(), bean.getParallelism());
}
/**
* Test increasing parallelism.
*/
@ParameterizedTest
@MethodSource("managedBeans")
void testIncreaseParallelism(VirtualThreadSchedulerMXBean bean) throws Exception {
assumeFalse(Thread.currentThread().isVirtual(), "Main thread is a virtual thread");
final int parallelism = bean.getParallelism();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var done = new AtomicBoolean();
Runnable busyTask = () -> {
while (!done.get()) {
Thread.onSpinWait();
}
};
try {
// saturate
IntStream.range(0, parallelism).forEach(_ -> executor.submit(busyTask));
awaitPoolSizeGte(bean, parallelism);
awaitMountedVirtualThreadCountGte(bean, parallelism);
// increase parallelism
for (int k = 1; k <= 4; k++) {
int newParallelism = parallelism + k;
bean.setParallelism(newParallelism);
executor.submit(busyTask);
// pool size and mounted virtual thread should increase
awaitPoolSizeGte(bean, newParallelism);
awaitMountedVirtualThreadCountGte(bean, newParallelism);
}
} finally {
done.set(true);
}
} finally {
bean.setParallelism(parallelism); // restore
}
}
/**
* Test reducing parallelism.
*/
@ParameterizedTest
@MethodSource("managedBeans")
void testReduceParallelism(VirtualThreadSchedulerMXBean bean) throws Exception {
assumeFalse(Thread.currentThread().isVirtual(), "Main thread is a virtual thread");
final int parallelism = bean.getParallelism();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var done = new AtomicBoolean();
var sleep = new AtomicBoolean();
// spin when !sleep
Runnable busyTask = () -> {
while (!done.get()) {
if (sleep.get()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) { }
} else {
Thread.onSpinWait();
}
}
};
try {
// increase parallelism + saturate
int newParallelism = parallelism + 4;
bean.setParallelism(newParallelism);
IntStream.range(0, newParallelism).forEach(_ -> executor.submit(busyTask));
awaitMountedVirtualThreadCountGte(bean, newParallelism);
// reduce parallelism and workload
newParallelism = Math.clamp(parallelism / 2, 1, parallelism);
bean.setParallelism(newParallelism);
sleep.set(true);
// mounted virtual thread count should reduce
awaitMountedVirtualThreadCountLte(bean, newParallelism);
// increase workload, the mounted virtual thread count should not increase
sleep.set(false);
for (int i = 0; i < 5; i++) {
Thread.sleep(100);
assertTrue(bean.getMountedVirtualThreadCount() <= newParallelism);
}
} finally {
done.set(true);
}
} finally {
bean.setParallelism(parallelism); // restore
}
}
/**
* Test getPoolSize.
*/
@ParameterizedTest
@MethodSource("managedBeans")
void testPoolSize(VirtualThreadSchedulerMXBean bean) {
assertTrue(bean.getPoolSize() >= 0);
VThreadRunner.run(() -> {
assertTrue(Thread.currentThread().isVirtual());
assertTrue(bean.getPoolSize() >= 1);
});
}
/**
* Test getMountedVirtualThreadCount.
*/
@ParameterizedTest
@MethodSource("managedBeans")
void testMountedVirtualThreadCount(VirtualThreadSchedulerMXBean bean) {
assertTrue(bean.getMountedVirtualThreadCount() >= 0);
VThreadRunner.run(() -> {
assertTrue(Thread.currentThread().isVirtual());
assertTrue(bean.getMountedVirtualThreadCount() >= 1);
});
}
/**
* Test getQueuedVirtualThreadCount.
*/
@ParameterizedTest
@MethodSource("managedBeans")
void testQueuedVirtualThreadCount(VirtualThreadSchedulerMXBean bean) throws Exception {
assumeFalse(Thread.currentThread().isVirtual(), "Main thread is a virtual thread");
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var done = new AtomicBoolean();
Runnable busyTask = () -> {
while (!done.get()) {
Thread.onSpinWait();
}
};
try {
// saturate
int parallelism = bean.getParallelism();
IntStream.range(0, parallelism).forEach(_ -> executor.submit(busyTask));
awaitMountedVirtualThreadCountGte(bean, parallelism);
// start 5 virtual threads, their tasks will be queued to execute
for (int i = 0; i < 5; i++) {
executor.submit(() -> { });
}
assertTrue(bean.getQueuedVirtualThreadCount() >= 5);
} finally {
done.set(true);
}
}
}
/**
* Waits for pool size >= target to be true.
*/
void awaitPoolSizeGte(VirtualThreadSchedulerMXBean bean, int target) throws InterruptedException {
System.err.format("await pool size >= %d ...%n", target);
while (bean.getPoolSize() < target) {
Thread.sleep(10);
}
}
/**
* Waits for the mounted virtual thread count >= target to be true.
*/
void awaitMountedVirtualThreadCountGte(VirtualThreadSchedulerMXBean bean,
int target) throws InterruptedException {
System.err.format("await mounted virtual thread count >= %d ...%n", target);
while (bean.getMountedVirtualThreadCount() < target) {
Thread.sleep(10);
}
}
/**
* Waits for the mounted virtual thread count <= target to be true.
*/
void awaitMountedVirtualThreadCountLte(VirtualThreadSchedulerMXBean bean,
int target) throws InterruptedException {
System.err.format("await mounted virtual thread count <= %d ...%n", target);
while (bean.getMountedVirtualThreadCount() > target) {
Thread.sleep(10);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,10 +23,10 @@
package jdk.test.lib.thread;
import java.lang.reflect.Field;
import java.lang.management.ManagementFactory;
import java.time.Duration;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicReference;
import jdk.management.VirtualThreadSchedulerMXBean;
/**
* Helper class to support tests running tasks in a virtual thread.
@ -133,39 +133,36 @@ public class VThreadRunner {
run(null, 0, task);
}
/**
* Returns the virtual thread scheduler.
*/
private static ForkJoinPool defaultScheduler() {
try {
var clazz = Class.forName("java.lang.VirtualThread");
var field = clazz.getDeclaredField("DEFAULT_SCHEDULER");
field.setAccessible(true);
return (ForkJoinPool) field.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Sets the virtual thread scheduler's target parallelism.
*
* <p> Tests using this method should use "{@code @modules jdk.management}" to help
* test selection.
*
* @return the previous parallelism level
*/
public static int setParallelism(int size) {
return defaultScheduler().setParallelism(size);
var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
int parallelism = bean.getParallelism();
bean.setParallelism(size);
return parallelism;
}
/**
* Ensures that the virtual thread scheduler's target parallelism is at least
* the given size. If the target parallelism is less than the given size then
* it is changed to the given size.
*
* <p> Tests using this method should use "{@code @modules jdk.management}" to help
* test selection.
*
* @return the previous parallelism level
*/
public static int ensureParallelism(int size) {
ForkJoinPool pool = defaultScheduler();
int parallelism = pool.getParallelism();
var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
int parallelism = bean.getParallelism();
if (size > parallelism) {
pool.setParallelism(size);
bean.setParallelism(size);
}
return parallelism;
}