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