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 }