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