1 /*
2 * Copyright (c) 2021, 2024, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.lang;
26
27 import java.lang.Thread.Builder.OfPlatform;
28 import java.lang.Thread.Builder.OfVirtual;
29 import java.lang.Thread.UncaughtExceptionHandler;
30 import java.lang.invoke.MethodHandles;
31 import java.lang.invoke.VarHandle;
32 import java.util.Locale;
33 import java.util.Objects;
34 import java.util.concurrent.ThreadFactory;
35 import jdk.internal.misc.Unsafe;
36 import jdk.internal.invoke.MhUtil;
37 import jdk.internal.reflect.CallerSensitive;
38 import jdk.internal.reflect.Reflection;
39 import jdk.internal.vm.ContinuationSupport;
40
41 /**
42 * Defines static methods to create platform and virtual thread builders.
43 */
44 class ThreadBuilders {
45 private ThreadBuilders() { }
46
47 /**
48 * Base class for Thread.Builder implementations.
49 */
50 private static class BaseThreadBuilder {
51 private String name;
52 private long counter;
53 private int characteristics;
54 private UncaughtExceptionHandler uhe;
55
56 String name() {
57 return name;
58 }
59
60 long counter() {
61 return counter;
62 }
63
64 int characteristics() {
65 return characteristics;
66 }
67
68 UncaughtExceptionHandler uncaughtExceptionHandler() {
69 return uhe;
70 }
71
72 String nextThreadName() {
73 if (name != null && counter >= 0) {
74 return name + (counter++);
75 } else {
76 return name;
77 }
78 }
79
80 void setName(String name) {
81 this.name = Objects.requireNonNull(name);
82 this.counter = -1;
83 }
84
85 void setName(String prefix, long start) {
86 Objects.requireNonNull(prefix);
87 if (start < 0)
88 throw new IllegalArgumentException("'start' is negative");
89 this.name = prefix;
90 this.counter = start;
91 }
92
93 void setInheritInheritableThreadLocals(boolean inherit) {
94 if (inherit) {
95 characteristics &= ~Thread.NO_INHERIT_THREAD_LOCALS;
96 } else {
97 characteristics |= Thread.NO_INHERIT_THREAD_LOCALS;
98 }
99 }
100
101 void setUncaughtExceptionHandler(UncaughtExceptionHandler ueh) {
102 this.uhe = Objects.requireNonNull(ueh);
103 }
104 }
105
106 /**
107 * ThreadBuilder.OfPlatform implementation.
108 */
109 static final class PlatformThreadBuilder
110 extends BaseThreadBuilder implements OfPlatform {
111 private ThreadGroup group;
112 private boolean daemon;
113 private boolean daemonChanged;
114 private int priority;
115 private long stackSize;
116
117 PlatformThreadBuilder() {
118 }
119
120 @Override
121 String nextThreadName() {
122 String name = super.nextThreadName();
123 return (name != null) ? name : Thread.genThreadName();
124 }
125
126 @Override
127 public OfPlatform name(String name) {
128 setName(name);
129 return this;
130 }
131
132 @Override
133 public OfPlatform name(String prefix, long start) {
134 setName(prefix, start);
135 return this;
136 }
137
138 @Override
139 public OfPlatform inheritInheritableThreadLocals(boolean inherit) {
140 setInheritInheritableThreadLocals(inherit);
141 return this;
142 }
143
144 @Override
145 public OfPlatform uncaughtExceptionHandler(UncaughtExceptionHandler ueh) {
146 setUncaughtExceptionHandler(ueh);
147 return this;
148 }
149
150 @Override
151 public OfPlatform group(ThreadGroup group) {
152 this.group = Objects.requireNonNull(group);
153 return this;
154 }
155
156 @Override
157 public OfPlatform daemon(boolean on) {
158 daemon = on;
159 daemonChanged = true;
160 return this;
161 }
162
163 @Override
164 public OfPlatform priority(int priority) {
165 if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY)
166 throw new IllegalArgumentException();
167 this.priority = priority;
168 return this;
169 }
170
171 @Override
172 public OfPlatform stackSize(long stackSize) {
173 if (stackSize < 0L)
174 throw new IllegalArgumentException();
175 this.stackSize = stackSize;
176 return this;
177 }
178
179 @Override
180 public Thread unstarted(Runnable task) {
181 Objects.requireNonNull(task);
182 String name = nextThreadName();
183 var thread = new Thread(group, name, characteristics(), task, stackSize);
184 if (daemonChanged)
185 thread.daemon(daemon);
186 if (priority != 0)
187 thread.priority(priority);
188 UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
189 if (uhe != null)
190 thread.uncaughtExceptionHandler(uhe);
191 return thread;
192 }
193
194 @Override
195 public Thread start(Runnable task) {
196 Thread thread = unstarted(task);
197 thread.start();
198 return thread;
199 }
200
201 @Override
202 public ThreadFactory factory() {
203 return new PlatformThreadFactory(group, name(), counter(), characteristics(),
204 daemonChanged, daemon, priority, stackSize, uncaughtExceptionHandler());
205 }
206 }
207
208 /**
209 * ThreadBuilder.OfVirtual implementation.
210 */
211 static final class VirtualThreadBuilder
212 extends BaseThreadBuilder implements OfVirtual {
213 private Thread.VirtualThreadScheduler scheduler;
214
215 VirtualThreadBuilder() {
216 }
217
218 @Override
219 public OfVirtual name(String name) {
220 setName(name);
221 return this;
222 }
223
224 @Override
225 public OfVirtual name(String prefix, long start) {
226 setName(prefix, start);
227 return this;
228 }
229
230 @Override
231 public OfVirtual inheritInheritableThreadLocals(boolean inherit) {
232 setInheritInheritableThreadLocals(inherit);
233 return this;
234 }
235
236 @Override
237 public OfVirtual uncaughtExceptionHandler(UncaughtExceptionHandler ueh) {
238 setUncaughtExceptionHandler(ueh);
239 return this;
240 }
241
242 @Override
243 public Thread unstarted(Runnable task) {
244 Objects.requireNonNull(task);
245 var thread = newVirtualThread(scheduler,
246 null,
247 nextThreadName(),
248 characteristics(),
249 task,
250 null);
251 UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
252 if (uhe != null)
253 thread.uncaughtExceptionHandler(uhe);
254 return thread;
255 }
256
257 @Override
258 public Thread start(Runnable task) {
259 Thread thread = unstarted(task);
260 thread.start();
261 return thread;
262 }
263
264 @Override
265 public ThreadFactory factory() {
266 return new VirtualThreadFactory(scheduler, name(), counter(), characteristics(),
267 uncaughtExceptionHandler());
268 }
269
270 @CallerSensitive
271 @Override
272 public Thread unstarted(Runnable task, Thread preferredCarrier, Object att) {
273 Objects.requireNonNull(task);
274 if (preferredCarrier != null && preferredCarrier.isVirtual()) {
275 throw new IllegalArgumentException("Preferred carrier cannot be a virtual thread");
276 }
277 Class<?> caller = Reflection.getCallerClass();
278 caller.getModule().ensureNativeAccess(OfVirtual.class, "unstarted", caller, false);
279 var thread = newVirtualThread(scheduler,
280 preferredCarrier,
281 nextThreadName(),
282 characteristics(),
283 task,
284 att);
285 UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
286 if (uhe != null)
287 thread.uncaughtExceptionHandler(uhe);
288 return thread;
289 }
290
291 @CallerSensitive
292 @Override
293 public OfVirtual scheduler(Thread.VirtualThreadScheduler scheduler) {
294 if (!ContinuationSupport.isSupported()) {
295 throw new UnsupportedOperationException();
296 }
297 // can't mix custom default scheduler and API prototypes at this time
298 if (scheduler != VirtualThread.defaultScheduler()
299 && VirtualThread.defaultScheduler() != VirtualThread.builtinScheduler()) {
300 throw new UnsupportedOperationException();
301 }
302 Class<?> caller = Reflection.getCallerClass();
303 caller.getModule().ensureNativeAccess(OfVirtual.class, "scheduler", caller, false);
304 this.scheduler = Objects.requireNonNull(scheduler);
305 return this;
306 }
307 }
308
309 /**
310 * Base ThreadFactory implementation.
311 */
312 private abstract static class BaseThreadFactory implements ThreadFactory {
313 private static final VarHandle COUNT = MhUtil.findVarHandle(
314 MethodHandles.lookup(), "count", long.class);
315
316 private final String name;
317 private final int characteristics;
318 private final UncaughtExceptionHandler uhe;
319
320 private final boolean hasCounter;
321 private volatile long count;
322
323 BaseThreadFactory(String name,
324 long start,
325 int characteristics,
326 UncaughtExceptionHandler uhe) {
327 this.name = name;
328 if (name != null && start >= 0) {
329 this.hasCounter = true;
330 this.count = start;
331 } else {
332 this.hasCounter = false;
333 }
334 this.characteristics = characteristics;
335 this.uhe = uhe;
336 }
337
338 int characteristics() {
339 return characteristics;
340 }
341
342 UncaughtExceptionHandler uncaughtExceptionHandler() {
343 return uhe;
344 }
345
346 String nextThreadName() {
347 if (hasCounter) {
348 return name + (long) COUNT.getAndAdd(this, 1L);
349 } else {
350 return name;
351 }
352 }
353 }
354
355 /**
356 * ThreadFactory for platform threads.
357 */
358 private static class PlatformThreadFactory extends BaseThreadFactory {
359 private final ThreadGroup group;
360 private final boolean daemonChanged;
361 private final boolean daemon;
362 private final int priority;
363 private final long stackSize;
364
365 PlatformThreadFactory(ThreadGroup group,
366 String name,
367 long start,
368 int characteristics,
369 boolean daemonChanged,
370 boolean daemon,
371 int priority,
372 long stackSize,
373 UncaughtExceptionHandler uhe) {
374 super(name, start, characteristics, uhe);
375 this.group = group;
376 this.daemonChanged = daemonChanged;
377 this.daemon = daemon;
378 this.priority = priority;
379 this.stackSize = stackSize;
380 }
381
382 @Override
383 String nextThreadName() {
384 String name = super.nextThreadName();
385 return (name != null) ? name : Thread.genThreadName();
386 }
387
388 @Override
389 public Thread newThread(Runnable task) {
390 Objects.requireNonNull(task);
391 String name = nextThreadName();
392 Thread thread = new Thread(group, name, characteristics(), task, stackSize);
393 if (daemonChanged)
394 thread.daemon(daemon);
395 if (priority != 0)
396 thread.priority(priority);
397 UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
398 if (uhe != null)
399 thread.uncaughtExceptionHandler(uhe);
400 return thread;
401 }
402 }
403
404 /**
405 * ThreadFactory for virtual threads.
406 */
407 private static class VirtualThreadFactory extends BaseThreadFactory {
408 private final Thread.VirtualThreadScheduler scheduler;
409
410 VirtualThreadFactory(Thread.VirtualThreadScheduler scheduler,
411 String name,
412 long start,
413 int characteristics,
414 UncaughtExceptionHandler uhe) {
415 super(name, start, characteristics, uhe);
416 this.scheduler = scheduler;
417 }
418
419 @Override
420 public Thread newThread(Runnable task) {
421 Objects.requireNonNull(task);
422 String name = nextThreadName();
423 Thread thread = newVirtualThread(scheduler, null, name, characteristics(), task, null);
424 UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
425 if (uhe != null)
426 thread.uncaughtExceptionHandler(uhe);
427 return thread;
428 }
429 }
430
431 /**
432 * Creates a new virtual thread to run the given task.
433 */
434 private static Thread newVirtualThread(Thread.VirtualThreadScheduler scheduler,
435 Thread preferredCarrier,
436 String name,
437 int characteristics,
438 Runnable task,
439 Object att) {
440 if (ContinuationSupport.isSupported()) {
441 return new VirtualThread(scheduler, preferredCarrier, name, characteristics, task, att);
442 } else {
443 if (scheduler != null)
444 throw new UnsupportedOperationException();
445 return new BoundVirtualThread(name, characteristics, task);
446 }
447 }
448
449 static Thread newVirtualThread(String name, int characteristics, Runnable task) {
450 return newVirtualThread(null, null, name, characteristics, task, null);
451 }
452
453 /**
454 * A "virtual thread" that is backed by a platform thread. This implementation
455 * is intended for platforms that don't have the underlying VM support for
456 * continuations. It can also be used for testing.
457 */
458 static final class BoundVirtualThread extends BaseVirtualThread {
459 private static final Unsafe U = Unsafe.getUnsafe();
460 private final Runnable task;
461 private boolean runInvoked;
462
463 BoundVirtualThread(String name, int characteristics, Runnable task) {
464 super(name, characteristics, true);
465 this.task = task;
466 }
467
468 @Override
469 public void run() {
470 // run is specified to do nothing when Thread is a virtual thread
471 if (Thread.currentThread() == this && !runInvoked) {
472 runInvoked = true;
473 Object bindings = Thread.scopedValueBindings();
474 runWith(bindings, task);
475 }
476 }
477
478 @Override
479 void park() {
480 U.park(false, 0L);
481 }
482
483 @Override
484 void parkNanos(long nanos) {
485 U.park(false, nanos);
486 }
487
488 @Override
489 void unpark() {
490 U.unpark(this);
491 }
492
493 @Override
494 public String toString() {
495 StringBuilder sb = new StringBuilder("VirtualThread[#");
496 sb.append(threadId());
497 String name = getName();
498 if (!name.isEmpty()) {
499 sb.append(",");
500 sb.append(name);
501 }
502 sb.append("]/");
503 String stateAsString = threadState().toString();
504 sb.append(stateAsString.toLowerCase(Locale.ROOT));
505 return sb.toString();
506 }
507 }
508 }