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 }