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 }