1 /*
2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 package jdk.test.lib.thread;
25
26 import java.lang.management.ManagementFactory;
27 import java.time.Duration;
28 import java.util.concurrent.atomic.AtomicReference;
29 import jdk.management.VirtualThreadSchedulerMXBean;
30
31 /**
32 * Helper class to support tests running tasks in a virtual thread.
33 */
34 public class VThreadRunner {
35 private VThreadRunner() { }
36
37 /**
38 * Characteristic value signifying that initial values for inheritable
39 * thread locals are not inherited from the constructing thread.
40 */
41 public static final int NO_INHERIT_THREAD_LOCALS = 1 << 2;
42
43 /**
44 * Represents a task that does not return a result but may throw an exception.
45 */
46 @FunctionalInterface
47 public interface ThrowingRunnable<X extends Throwable> {
48 void run() throws X;
49 }
50
51 /**
52 * Run a task in a virtual thread and wait for it to terminate.
53 * If the task completes with an exception then it is thrown by this method.
54 *
55 * @param name thread name, can be null
56 * @param characteristics thread characteristics
57 * @param task the task to run
58 * @throws X the exception thrown by the task
59 */
60 public static <X extends Throwable> void run(String name,
61 int characteristics,
62 ThrowingRunnable<X> task) throws X {
63 var throwableRef = new AtomicReference<Throwable>();
64 Runnable target = () -> {
65 try {
66 task.run();
67 } catch (Throwable ex) {
68 throwableRef.set(ex);
69 }
70 };
71
72 Thread.Builder builder = Thread.ofVirtual();
73 if (name != null)
74 builder.name(name);
75 if ((characteristics & NO_INHERIT_THREAD_LOCALS) != 0)
76 builder.inheritInheritableThreadLocals(false);
77 Thread thread = builder.start(target);
78
79 // wait for thread to terminate
80 try {
81 while (thread.join(Duration.ofSeconds(10)) == false) {
82 System.out.println("-- " + thread + " --");
83 for (StackTraceElement e : thread.getStackTrace()) {
84 System.out.println(" " + e);
85 }
86 }
87 } catch (InterruptedException e) {
88 throw new RuntimeException(e);
89 }
90
91 Throwable ex = throwableRef.get();
92 if (ex != null) {
93 if (ex instanceof RuntimeException e)
94 throw e;
95 if (ex instanceof Error e)
96 throw e;
97 @SuppressWarnings("unchecked")
98 var x = (X) ex;
99 throw x;
100 }
101 }
102
103 /**
104 * Run a task in a virtual thread and wait for it to terminate.
105 * If the task completes with an exception then it is thrown by this method.
106 *
107 * @param name thread name, can be null
108 * @param task the task to run
109 * @throws X the exception thrown by the task
110 */
111 public static <X extends Throwable> void run(String name, ThrowingRunnable<X> task) throws X {
112 run(name, 0, task);
113 }
114
115 /**
116 * Run a task in a virtual thread and wait for it to terminate.
117 * If the task completes with an exception then it is thrown by this method.
118 *
119 * @param characteristics thread characteristics
120 * @param task the task to run
121 * @throws X the exception thrown by the task
122 */
123 public static <X extends Throwable> void run(int characteristics, ThrowingRunnable<X> task) throws X {
124 run(null, characteristics, task);
125 }
126
127 /**
128 * Run a task in a virtual thread and wait for it to terminate.
129 * If the task completes with an exception then it is thrown by this method.
130 *
131 * @param task the task to run
132 * @throws X the exception thrown by the task
133 */
134 public static <X extends Throwable> void run(ThrowingRunnable<X> task) throws X {
135 run(null, 0, task);
136 }
137
138 /**
139 * Sets the virtual thread scheduler's target parallelism.
140 *
141 * <p> Tests using this method should use "{@code @modules jdk.management}" to help
142 * test selection.
143 *
144 * @return the previous parallelism level
145 */
146 public static int setParallelism(int size) {
147 var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
148 int parallelism = bean.getParallelism();
149 bean.setParallelism(size);
150 return parallelism;
151 }
152
153 /**
154 * Ensures that the virtual thread scheduler's target parallelism is at least
155 * the given size. If the target parallelism is less than the given size then
156 * it is changed to the given size.
157 *
158 * <p> Tests using this method should use "{@code @modules jdk.management}" to help
159 * test selection.
160 *
161 * @return the previous parallelism level
162 */
163 public static int ensureParallelism(int size) {
164 var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
165 int parallelism = bean.getParallelism();
166 if (size > parallelism) {
167 bean.setParallelism(size);
168 }
169 return parallelism;
170 }
171 }