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