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 }