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 Thread.Builder.OfVirtual builder = Thread.ofVirtual(); 64 if (name != null) 65 builder.name(name); 66 if ((characteristics & NO_INHERIT_THREAD_LOCALS) != 0) 67 builder.inheritInheritableThreadLocals(false); 68 run(builder, task); 69 } 70 71 /** 72 * Run a task in a virtual thread and wait for it to terminate. 73 * If the task completes with an exception then it is thrown by this method. 74 * 75 * @param builder the builder to create the thread 76 * @param task the task to run 77 * @throws X the exception thrown by the task 78 */ 79 public static <X extends Throwable> void run(Thread.Builder.OfVirtual builder, 80 ThrowingRunnable<X> task) throws X {
81 var throwableRef = new AtomicReference<Throwable>();
82 Runnable target = () -> {
83 try {
84 task.run();
85 } catch (Throwable ex) {
86 throwableRef.set(ex);
87 }
88 };
89 Thread thread = builder.start(target);
90
91 // wait for thread to terminate
92 try {
93 while (thread.join(Duration.ofSeconds(10)) == false) {
94 System.out.println("-- " + thread + " --");
95 for (StackTraceElement e : thread.getStackTrace()) {
96 System.out.println(" " + e);
97 }
98 }
99 } catch (InterruptedException e) {
100 throw new RuntimeException(e);
101 }
102
103 Throwable ex = throwableRef.get();
104 if (ex != null) {
105 if (ex instanceof RuntimeException e)
106 throw e;
107 if (ex instanceof Error e)
108 throw e;
109 @SuppressWarnings("unchecked")
110 var x = (X) ex;
111 throw x;
112 }
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 name thread name, can be null
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(String name, ThrowingRunnable<X> task) throws X {
124 run(name, 0, 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 characteristics thread characteristics
132 * @param task the task to run
133 * @throws X the exception thrown by the task
134 */
135 public static <X extends Throwable> void run(int characteristics, ThrowingRunnable<X> task) throws X {
136 run(null, characteristics, task);
137 }
138
139 /**
140 * Run a task in a virtual thread and wait for it to terminate.
141 * If the task completes with an exception then it is thrown by this method.
142 *
143 * @param task the task to run
144 * @throws X the exception thrown by the task
145 */
146 public static <X extends Throwable> void run(ThrowingRunnable<X> task) throws X {
147 run(null, 0, task);
148 }
149
150 /**
151 * Sets the virtual thread scheduler's target parallelism.
152 *
153 * <p> Tests using this method should use "{@code @modules jdk.management}" to help
154 * test selection.
155 *
156 * @return the previous parallelism level
157 */
158 public static int setParallelism(int size) {
159 var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
160 int parallelism = bean.getParallelism();
161 bean.setParallelism(size);
162 return parallelism;
163 }
164
165 /**
166 * Ensures that the virtual thread scheduler's target parallelism is at least
167 * the given size. If the target parallelism is less than the given size then
168 * it is changed to the given size.
169 *
170 * <p> Tests using this method should use "{@code @modules jdk.management}" to help
171 * test selection.
172 *
173 * @return the previous parallelism level
174 */
175 public static int ensureParallelism(int size) {
176 var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class);
177 int parallelism = bean.getParallelism();
178 if (size > parallelism) {
179 bean.setParallelism(size);
180 }
181 return parallelism;
182 }
183 }
--- EOF ---