1 /*
  2  * Copyright (c) 2021, 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.Objects;
 34 import java.util.concurrent.Executor;
 35 import java.util.concurrent.ThreadFactory;
 36 
 37 /**
 38  * Defines static methods to create platform and virtual thread builders.
 39  */
 40 class ThreadBuilders {
 41 
 42     /**
 43      * Base implementation of ThreadBuilder.
 44      */
 45     static abstract non-sealed
 46     class BaseThreadBuilder<T extends Builder> implements Builder {
 47         private String name;
 48         private long counter;
 49         private int characteristics;
 50         private UncaughtExceptionHandler uhe;
 51 
 52         String name() {
 53             return name;
 54         }
 55 
 56         long counter() {
 57             return counter;
 58         }
 59 
 60         int characteristics() {
 61             return characteristics;
 62         }
 63 
 64         UncaughtExceptionHandler uncaughtExceptionHandler() {
 65             return uhe;
 66         }
 67 
 68         String nextThreadName() {
 69             if (name != null && counter >= 0) {
 70                 return name + (counter++);
 71             } else {
 72                 return name;
 73             }
 74         }
 75 
 76         @Override
 77         @SuppressWarnings("unchecked")
 78         public T name(String name) {
 79             this.name = Objects.requireNonNull(name);
 80             this.counter = -1;
 81             return (T) this;
 82         }
 83 
 84         @Override
 85         @SuppressWarnings("unchecked")
 86         public T name(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             return (T) this;
 93         }
 94 
 95         @Override
 96         @SuppressWarnings("unchecked")
 97         public T allowSetThreadLocals(boolean allow) {
 98             if (allow) {
 99                 characteristics &= ~Thread.NO_THREAD_LOCALS;
100             } else {
101                 characteristics |= Thread.NO_THREAD_LOCALS;
102             }
103             return (T) this;
104         }
105 
106         @Override
107         @SuppressWarnings("unchecked")
108         public T inheritInheritableThreadLocals(boolean inherit) {
109             if (inherit) {
110                 characteristics &= ~Thread.NO_INHERIT_THREAD_LOCALS;
111             } else {
112                 characteristics |= Thread.NO_INHERIT_THREAD_LOCALS;
113             }
114             return (T) this;
115         }
116 
117         @Override
118         @SuppressWarnings("unchecked")
119         public T uncaughtExceptionHandler(UncaughtExceptionHandler ueh) {
120             this.uhe = Objects.requireNonNull(ueh);
121             return (T) this;
122         }
123     }
124 
125     /**
126      * ThreadBuilder.OfPlatform implementation.
127      */
128     static final class PlatformThreadBuilder
129             extends BaseThreadBuilder<OfPlatform> implements OfPlatform {
130         private ThreadGroup group;
131         private boolean daemon;
132         private boolean daemonChanged;
133         private int priority;
134         private long stackSize;
135 
136         @Override
137         String nextThreadName() {
138             String name = super.nextThreadName();
139             return (name != null) ? name : Thread.nextThreadName();
140         }
141 
142         @Override
143         public OfPlatform group(ThreadGroup group) {
144             this.group = Objects.requireNonNull(group);
145             return this;
146         }
147 
148         @Override
149         public OfPlatform daemon(boolean on) {
150             daemon = on;
151             daemonChanged = true;
152             return this;
153         }
154 
155         @Override
156         public OfPlatform priority(int priority) {
157             if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY)
158                 throw new IllegalArgumentException();
159             this.priority = priority;
160             return this;
161         }
162 
163         @Override
164         public OfPlatform stackSize(long stackSize) {
165             if (stackSize < 0L)
166                 throw new IllegalArgumentException();
167             this.stackSize = stackSize;
168             return this;
169         }
170 
171         @Override
172         public Thread unstarted(Runnable task) {
173             Objects.requireNonNull(task);
174             String name = nextThreadName();
175             var thread = new Thread(group, name, characteristics(), task, stackSize, null);
176             if (daemonChanged)
177                 thread.daemon(daemon);
178             if (priority != 0)
179                 thread.priority(priority);
180             UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
181             if (uhe != null)
182                 thread.uncaughtExceptionHandler(uhe);
183             return thread;
184         }
185 
186         @Override
187         public ThreadFactory factory() {
188             return new PlatformThreadFactory(group, name(), counter(), characteristics(),
189                     daemonChanged, daemon, priority, stackSize, uncaughtExceptionHandler());
190         }
191 
192     }
193 
194     /**
195      * ThreadBuilder.OfVirtual implementation.
196      */
197     static final class VirtualThreadBuilder
198             extends BaseThreadBuilder<OfVirtual> implements OfVirtual {
199         private Executor scheduler;  // set by tests
200 
201         @Override
202         public Thread unstarted(Runnable task) {
203             Objects.requireNonNull(task);
204             var thread = new VirtualThread(scheduler, nextThreadName(), characteristics(), task);
205             UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
206             if (uhe != null)
207                 thread.uncaughtExceptionHandler(uhe);
208             return thread;
209         }
210 
211         @Override
212         public ThreadFactory factory() {
213             return new VirtualThreadFactory(scheduler, name(), counter(), characteristics(),
214                     uncaughtExceptionHandler());
215         }
216     }
217 
218     /**
219      * Base ThreadFactory implementation.
220      */
221     private static abstract class BaseThreadFactory implements ThreadFactory {
222         private static final VarHandle COUNT;
223         static {
224             try {
225                 MethodHandles.Lookup l = MethodHandles.lookup();
226                 COUNT = l.findVarHandle(BaseThreadFactory.class, "count", long.class);
227             } catch (Exception e) {
228                 throw new InternalError(e);
229             }
230         }
231         private final String name;
232         private final int characteristics;
233         private final UncaughtExceptionHandler uhe;
234 
235         private final boolean hasCounter;
236         private volatile long count;
237 
238         BaseThreadFactory(String name,
239                           long start,
240                           int characteristics,
241                           UncaughtExceptionHandler uhe)  {
242             this.name = name;
243             if (name != null && start >= 0) {
244                 this.hasCounter = true;
245                 this.count = start;
246             } else {
247                 this.hasCounter = false;
248             }
249             this.characteristics = characteristics;
250             this.uhe = uhe;
251         }
252 
253         int characteristics() {
254             return characteristics;
255         }
256 
257         UncaughtExceptionHandler uncaughtExceptionHandler() {
258             return uhe;
259         }
260 
261         String nextThreadName() {
262             if (hasCounter) {
263                 return name + (long) COUNT.getAndAdd(this, 1);
264             } else {
265                 return name;
266             }
267         }
268     }
269 
270     /**
271      * ThreadFactory for platform threads.
272      */
273     private static class PlatformThreadFactory extends BaseThreadFactory {
274         private final ThreadGroup group;
275         private final boolean daemonChanged;
276         private final boolean daemon;
277         private final int priority;
278         private final long stackSize;
279 
280         PlatformThreadFactory(ThreadGroup group,
281                               String name,
282                               long start,
283                               int characteristics,
284                               boolean daemonChanged,
285                               boolean daemon,
286                               int priority,
287                               long stackSize,
288                               UncaughtExceptionHandler uhe) {
289             super(name, start, characteristics, uhe);
290             this.group = group;
291             this.daemonChanged = daemonChanged;
292             this.daemon = daemon;
293             this.priority = priority;
294             this.stackSize = stackSize;
295         }
296 
297         @Override
298         String nextThreadName() {
299             String name = super.nextThreadName();
300             return (name != null) ? name : Thread.nextThreadName();
301         }
302 
303         @Override
304         public Thread newThread(Runnable task) {
305             Objects.requireNonNull(task);
306             String name = nextThreadName();
307             Thread thread = new Thread(group, name, characteristics(), task, stackSize, null);
308             if (daemonChanged)
309                 thread.daemon(daemon);
310             if (priority != 0)
311                 thread.priority(priority);
312             UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
313             if (uhe != null)
314                 thread.uncaughtExceptionHandler(uhe);
315             return thread;
316         }
317     }
318 
319     /**
320      * ThreadFactory for virtual threads.
321      */
322     private static class VirtualThreadFactory extends BaseThreadFactory {
323         private final Executor scheduler;
324 
325         VirtualThreadFactory(Executor scheduler,
326                              String name,
327                              long start,
328                              int characteristics,
329                              UncaughtExceptionHandler uhe) {
330             super(name, start, characteristics, uhe);
331             this.scheduler = scheduler;
332         }
333 
334         @Override
335         public Thread newThread(Runnable task) {
336             Objects.requireNonNull(task);
337             String name = nextThreadName();
338             Thread thread = new VirtualThread(scheduler, name, characteristics(), task);
339             UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
340             if (uhe != null)
341                 thread.uncaughtExceptionHandler(uhe);
342             return thread;
343         }
344     }
345 }