1 /* 2 * Copyright (c) 2018, 2026, 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.invoke.MethodHandles; 28 import java.lang.invoke.VarHandle; 29 import java.lang.reflect.Constructor; 30 import java.util.Locale; 31 import java.util.Objects; 32 import java.util.concurrent.Executors; 33 import java.util.concurrent.ForkJoinPool; 34 import java.util.concurrent.ForkJoinTask; 35 import java.util.concurrent.Future; 36 import java.util.concurrent.LinkedTransferQueue; 37 import java.util.concurrent.RejectedExecutionException; 38 import java.util.concurrent.ScheduledExecutorService; 39 import java.util.concurrent.ScheduledFuture; 40 import java.util.concurrent.ScheduledThreadPoolExecutor; 41 import java.util.concurrent.ThreadFactory; 42 import java.util.concurrent.ThreadPoolExecutor; 43 import java.util.concurrent.TimeUnit; 44 import jdk.internal.event.VirtualThreadEndEvent; 45 import jdk.internal.event.VirtualThreadStartEvent; 46 import jdk.internal.event.VirtualThreadSubmitFailedEvent; 47 import jdk.internal.invoke.MhUtil; 48 import jdk.internal.misc.CarrierThread; 49 import jdk.internal.misc.InnocuousThread; 50 import jdk.internal.misc.Unsafe; 51 import jdk.internal.vm.Continuation; 52 import jdk.internal.vm.ContinuationScope; 53 import jdk.internal.vm.StackableScope; 54 import jdk.internal.vm.ThreadContainer; 55 import jdk.internal.vm.ThreadContainers; 56 import jdk.internal.vm.annotation.ChangesCurrentThread; 57 import jdk.internal.vm.annotation.Hidden; 58 import jdk.internal.vm.annotation.IntrinsicCandidate; 59 import jdk.internal.vm.annotation.JvmtiHideEvents; 60 import jdk.internal.vm.annotation.JvmtiMountTransition; 61 import jdk.internal.vm.annotation.ReservedStackAccess; 62 import sun.nio.ch.Interruptible; 63 import static java.util.concurrent.TimeUnit.*; 64 65 /** 66 * A thread that is scheduled by the Java virtual machine rather than the operating system. 67 */ 68 final class VirtualThread extends BaseVirtualThread { 69 private static final Unsafe U = Unsafe.getUnsafe(); 70 private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads"); 71 72 private static final VirtualThreadScheduler BUILTIN_SCHEDULER; 73 private static final VirtualThreadScheduler DEFAULT_SCHEDULER; 74 private static final VirtualThreadScheduler EXTERNAL_VIEW; 75 private static final boolean USE_STPE; 76 static { 77 // experimental 78 String propValue = System.getProperty("jdk.virtualThreadScheduler.implClass"); 79 if (propValue != null) { 80 VirtualThreadScheduler builtinScheduler = createBuiltinScheduler(true); 81 VirtualThreadScheduler externalView = createExternalView(builtinScheduler); 82 VirtualThreadScheduler defaultScheduler = loadCustomScheduler(externalView, propValue); 83 BUILTIN_SCHEDULER = builtinScheduler; 84 DEFAULT_SCHEDULER = defaultScheduler; 85 EXTERNAL_VIEW = externalView; 86 } else { 87 var builtinScheduler = createBuiltinScheduler(false); 88 BUILTIN_SCHEDULER = builtinScheduler; 89 DEFAULT_SCHEDULER = builtinScheduler; 90 EXTERNAL_VIEW = createExternalView(builtinScheduler); 91 } 92 propValue = System.getProperty("jdk.virtualThreadScheduler.useSTPE"); 93 if (propValue != null) { 94 USE_STPE = Boolean.parseBoolean(propValue); 95 } else { 96 USE_STPE = Runtime.getRuntime().availableProcessors() < 4; 97 } 98 } 99 100 private static final long STATE = U.objectFieldOffset(VirtualThread.class, "state"); 101 private static final long PARK_PERMIT = U.objectFieldOffset(VirtualThread.class, "parkPermit"); 102 private static final long CARRIER_THREAD = U.objectFieldOffset(VirtualThread.class, "carrierThread"); 103 private static final long ON_WAITING_LIST = U.objectFieldOffset(VirtualThread.class, "onWaitingList"); 104 105 // scheduler and continuation 106 private final VirtualThreadScheduler scheduler; 107 private final Continuation cont; 108 private final VThreadTask runContinuation; 109 110 // virtual thread state, accessed by VM 111 private volatile int state; 112 113 /* 114 * Virtual thread state transitions: 115 * 116 * NEW -> STARTED // Thread.start, schedule to run 117 * STARTED -> TERMINATED // failed to start 118 * STARTED -> RUNNING // first run 119 * RUNNING -> TERMINATED // done 120 * 121 * RUNNING -> PARKING // Thread parking with LockSupport.park 122 * PARKING -> PARKED // cont.yield successful, parked indefinitely 123 * PARKED -> UNPARKED // unparked, may be scheduled to continue 124 * UNPARKED -> RUNNING // continue execution after park 125 * 126 * PARKING -> RUNNING // cont.yield failed, need to park on carrier 127 * RUNNING -> PINNED // park on carrier 128 * PINNED -> RUNNING // unparked, continue execution on same carrier 129 * 130 * RUNNING -> TIMED_PARKING // Thread parking with LockSupport.parkNanos 131 * TIMED_PARKING -> TIMED_PARKED // cont.yield successful, timed-parked 132 * TIMED_PARKED -> UNPARKED // unparked, may be scheduled to continue 133 * 134 * TIMED_PARKING -> RUNNING // cont.yield failed, need to park on carrier 135 * RUNNING -> TIMED_PINNED // park on carrier 136 * TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier 137 * 138 * RUNNING -> BLOCKING // blocking on monitor enter 139 * BLOCKING -> BLOCKED // blocked on monitor enter 140 * BLOCKED -> UNBLOCKED // unblocked, may be scheduled to continue 141 * UNBLOCKED -> RUNNING // continue execution after blocked on monitor enter 142 * 143 * RUNNING -> WAITING // transitional state during wait on monitor 144 * WAITING -> WAIT // waiting on monitor 145 * WAIT -> BLOCKED // notified, waiting to be unblocked by monitor owner 146 * WAIT -> UNBLOCKED // interrupted 147 * 148 * RUNNING -> TIMED_WAITING // transition state during timed-waiting on monitor 149 * TIMED_WAITING -> TIMED_WAIT // timed-waiting on monitor 150 * TIMED_WAIT -> BLOCKED // notified, waiting to be unblocked by monitor owner 151 * TIMED_WAIT -> UNBLOCKED // timed-out/interrupted 152 * 153 * RUNNING -> YIELDING // Thread.yield 154 * YIELDING -> YIELDED // cont.yield successful, may be scheduled to continue 155 * YIELDING -> RUNNING // cont.yield failed 156 * YIELDED -> RUNNING // continue execution after Thread.yield 157 */ 158 private static final int NEW = 0; 159 private static final int STARTED = 1; 160 private static final int RUNNING = 2; // runnable-mounted 161 162 // untimed and timed parking 163 private static final int PARKING = 3; 164 private static final int PARKED = 4; // unmounted 165 private static final int PINNED = 5; // mounted 166 private static final int TIMED_PARKING = 6; 167 private static final int TIMED_PARKED = 7; // unmounted 168 private static final int TIMED_PINNED = 8; // mounted 169 private static final int UNPARKED = 9; // unmounted but runnable 170 171 // Thread.yield 172 private static final int YIELDING = 10; 173 private static final int YIELDED = 11; // unmounted but runnable 174 175 // monitor enter 176 private static final int BLOCKING = 12; 177 private static final int BLOCKED = 13; // unmounted 178 private static final int UNBLOCKED = 14; // unmounted but runnable 179 180 // monitor wait/timed-wait 181 private static final int WAITING = 15; 182 private static final int WAIT = 16; // waiting in Object.wait 183 private static final int TIMED_WAITING = 17; 184 private static final int TIMED_WAIT = 18; // waiting in timed-Object.wait 185 186 private static final int TERMINATED = 99; // final state 187 188 // parking permit made available by LockSupport.unpark 189 private volatile boolean parkPermit; 190 191 // blocking permit made available by unblocker thread when another thread exits monitor 192 private volatile boolean blockPermit; 193 194 // true when on the list of virtual threads waiting to be unblocked 195 private volatile boolean onWaitingList; 196 197 // next virtual thread on the list of virtual threads waiting to be unblocked 198 private volatile VirtualThread next; 199 200 // notified by Object.notify/notifyAll while waiting in Object.wait 201 private volatile boolean notified; 202 203 // true when waiting in Object.wait, false for VM internal uninterruptible Object.wait 204 private volatile boolean interruptibleWait; 205 206 // timed-wait support 207 private byte timedWaitSeqNo; 208 209 // timeout for timed-park and timed-wait, only accessed on current/carrier thread 210 private long timeout; 211 212 // timer task for timed-park and timed-wait, only accessed on current/carrier thread 213 private Future<?> timeoutTask; 214 215 // carrier thread when mounted, accessed by VM 216 private volatile Thread carrierThread; 217 218 // true to notifyAll after this virtual thread terminates 219 private volatile boolean notifyAllAfterTerminate; 220 221 /** 222 * Return the built-in scheduler. 223 * @param trusted true if caller is trusted, false if not trusted 224 */ 225 static VirtualThreadScheduler builtinScheduler(boolean trusted) { 226 return trusted ? BUILTIN_SCHEDULER : EXTERNAL_VIEW; 227 } 228 229 /** 230 * Returns the default scheduler, usually the same as the built-in scheduler. 231 */ 232 static VirtualThreadScheduler defaultScheduler() { 233 return DEFAULT_SCHEDULER; 234 } 235 236 /** 237 * Returns the continuation scope used for virtual threads. 238 */ 239 static ContinuationScope continuationScope() { 240 return VTHREAD_SCOPE; 241 } 242 243 /** 244 * Returns the task to start/continue this virtual thread. 245 */ 246 VirtualThreadTask virtualThreadTask() { 247 return runContinuation; 248 } 249 250 /** 251 * Creates a new {@code VirtualThread} to run the given task with the given scheduler. 252 * 253 * @param scheduler the scheduler or null for default scheduler 254 * @param preferredCarrier the preferred carrier or null 255 * @param name thread name 256 * @param characteristics characteristics 257 * @param task the task to execute 258 */ 259 VirtualThread(VirtualThreadScheduler scheduler, 260 Thread preferredCarrier, 261 String name, 262 int characteristics, 263 Runnable task) { 264 super(name, characteristics, /*bound*/ false); 265 Objects.requireNonNull(task); 266 267 // use default scheduler if not provided 268 if (scheduler == null) { 269 scheduler = DEFAULT_SCHEDULER; 270 } else if (scheduler == EXTERNAL_VIEW) { 271 throw new UnsupportedOperationException(); 272 } 273 this.scheduler = scheduler; 274 this.cont = new VThreadContinuation(this, task); 275 276 if (scheduler == BUILTIN_SCHEDULER) { 277 this.runContinuation = new VThreadTask(this); 278 } else { 279 this.runContinuation = new CustomVThreadTask(this, preferredCarrier); 280 } 281 } 282 283 /** 284 * The task to start/continue a virtual thread. 285 */ 286 static non-sealed class VThreadTask implements VirtualThreadTask { 287 private final VirtualThread vthread; 288 VThreadTask(VirtualThread vthread) { 289 this.vthread = vthread; 290 } 291 @Override 292 public final Thread thread() { 293 return vthread; 294 } 295 @Override 296 public final void run() { 297 vthread.runContinuation();; 298 } 299 @Override 300 public Thread preferredCarrier() { 301 throw new UnsupportedOperationException(); 302 } 303 @Override 304 public Object attach(Object att) { 305 throw new UnsupportedOperationException(); 306 } 307 @Override 308 public Object attachment() { 309 throw new UnsupportedOperationException(); 310 } 311 } 312 313 /** 314 * The task to start/continue a virtual thread when using a custom scheduler. 315 */ 316 static final class CustomVThreadTask extends VThreadTask { 317 private static final VarHandle ATT = 318 MhUtil.findVarHandle(MethodHandles.lookup(), "att", Object.class); 319 private final Thread preferredCarrier; 320 private volatile Object att; 321 CustomVThreadTask(VirtualThread vthread, Thread preferredCarrier) { 322 super(vthread); 323 this.preferredCarrier = preferredCarrier; 324 } 325 @Override 326 public Thread preferredCarrier() { 327 return preferredCarrier; 328 } 329 @Override 330 public Object attach(Object att) { 331 return ATT.getAndSet(this, att); 332 } 333 @Override 334 public Object attachment() { 335 return att; 336 } 337 } 338 339 /** 340 * The continuation that a virtual thread executes. 341 */ 342 private static class VThreadContinuation extends Continuation { 343 VThreadContinuation(VirtualThread vthread, Runnable task) { 344 super(VTHREAD_SCOPE, wrap(vthread, task)); 345 } 346 @Override 347 protected void onPinned(Continuation.Pinned reason) { 348 } 349 private static Runnable wrap(VirtualThread vthread, Runnable task) { 350 return new Runnable() { 351 @Hidden 352 @JvmtiHideEvents 353 public void run() { 354 vthread.endFirstTransition(); 355 try { 356 vthread.run(task); 357 } finally { 358 vthread.startFinalTransition(); 359 } 360 } 361 }; 362 } 363 } 364 365 /** 366 * Runs or continues execution on the current thread. The virtual thread is mounted 367 * on the current thread before the task runs or continues. It unmounts when the 368 * task completes or yields. 369 */ 370 @ChangesCurrentThread // allow mount/unmount to be inlined 371 private void runContinuation() { 372 // the carrier must be a platform thread 373 if (Thread.currentThread().isVirtual()) { 374 throw new WrongThreadException(); 375 } 376 377 // set state to RUNNING 378 int initialState = state(); 379 if (initialState == STARTED || initialState == UNPARKED 380 || initialState == UNBLOCKED || initialState == YIELDED) { 381 // newly started or continue after parking/blocking/Thread.yield 382 if (!compareAndSetState(initialState, RUNNING)) { 383 return; 384 } 385 // consume permit when continuing after parking or blocking. If continue 386 // after a timed-park or timed-wait then the timeout task is cancelled. 387 if (initialState == UNPARKED) { 388 cancelTimeoutTask(); 389 setParkPermit(false); 390 } else if (initialState == UNBLOCKED) { 391 cancelTimeoutTask(); 392 blockPermit = false; 393 } 394 } else { 395 // not runnable 396 return; 397 } 398 399 mount(); 400 try { 401 cont.run(); 402 } finally { 403 unmount(); 404 if (cont.isDone()) { 405 afterDone(); 406 } else { 407 afterYield(); 408 } 409 } 410 } 411 412 /** 413 * Cancel timeout task when continuing after timed-park or timed-wait. 414 * The timeout task may be executing, or may have already completed. 415 */ 416 private void cancelTimeoutTask() { 417 if (timeoutTask != null) { 418 timeoutTask.cancel(false); 419 timeoutTask = null; 420 } 421 } 422 423 /** 424 * Submits the runContinuation task to the scheduler. For the built-in scheduler, 425 * the task will be pushed to the local queue if possible, otherwise it will be 426 * pushed to an external submission queue. 427 * @param retryOnOOME true to retry indefinitely if OutOfMemoryError is thrown 428 * @throws RejectedExecutionException 429 */ 430 private void submitRunContinuation(boolean retryOnOOME) { 431 boolean done = false; 432 while (!done) { 433 try { 434 // Pin the continuation to prevent the virtual thread from unmounting 435 // when submitting a task. For the default scheduler this ensures that 436 // the carrier doesn't change when pushing a task. For other schedulers 437 // it avoids deadlock that could arise due to carriers and virtual 438 // threads contending for a lock. 439 if (currentThread().isVirtual()) { 440 Continuation.pin(); 441 try { 442 scheduler.onContinue(runContinuation); 443 } finally { 444 Continuation.unpin(); 445 } 446 } else { 447 scheduler.onContinue(runContinuation); 448 } 449 done = true; 450 } catch (RejectedExecutionException ree) { 451 submitFailed(ree); 452 throw ree; 453 } catch (OutOfMemoryError e) { 454 if (retryOnOOME) { 455 U.park(false, 100_000_000); // 100ms 456 } else { 457 throw e; 458 } 459 } 460 } 461 } 462 463 /** 464 * Submits the runContinuation task to the scheduler. For the default scheduler, 465 * and calling it on a worker thread, the task will be pushed to the local queue, 466 * otherwise it will be pushed to an external submission queue. 467 * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. 468 * @throws RejectedExecutionException 469 */ 470 private void submitRunContinuation() { 471 submitRunContinuation(true); 472 } 473 474 /** 475 * Invoked from a carrier thread to lazy submit the runContinuation task to the 476 * carrier's local queue if the queue is empty. If not empty, or invoked by a thread 477 * for a custom scheduler, then it just submits the task to the scheduler. 478 * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. 479 * @throws RejectedExecutionException 480 * @see ForkJoinPool#lazySubmit(ForkJoinTask) 481 */ 482 private void lazySubmitRunContinuation() { 483 assert !currentThread().isVirtual(); 484 if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) { 485 try { 486 ct.getPool().lazySubmit(ForkJoinTask.adapt(runContinuation)); 487 } catch (RejectedExecutionException ree) { 488 submitFailed(ree); 489 throw ree; 490 } catch (OutOfMemoryError e) { 491 submitRunContinuation(); 492 } 493 } else { 494 submitRunContinuation(); 495 } 496 } 497 498 /** 499 * Invoked from a carrier thread to externally submit the runContinuation task to the 500 * scheduler. If invoked by a thread for a custom scheduler, then it just submits the 501 * task to the scheduler. 502 * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. 503 * @throws RejectedExecutionException 504 * @see ForkJoinPool#externalSubmit(ForkJoinTask) 505 */ 506 private void externalSubmitRunContinuation() { 507 assert !currentThread().isVirtual(); 508 if (currentThread() instanceof CarrierThread ct) { 509 try { 510 ct.getPool().externalSubmit(ForkJoinTask.adapt(runContinuation)); 511 } catch (RejectedExecutionException ree) { 512 submitFailed(ree); 513 throw ree; 514 } catch (OutOfMemoryError e) { 515 submitRunContinuation(); 516 } 517 } else { 518 submitRunContinuation(); 519 } 520 } 521 522 /** 523 * If enabled, emits a JFR VirtualThreadSubmitFailedEvent. 524 */ 525 private void submitFailed(RejectedExecutionException ree) { 526 var event = new VirtualThreadSubmitFailedEvent(); 527 if (event.isEnabled()) { 528 event.javaThreadId = threadId(); 529 event.exceptionMessage = ree.getMessage(); 530 event.commit(); 531 } 532 } 533 534 /** 535 * Runs a task in the context of this virtual thread. 536 */ 537 private void run(Runnable task) { 538 assert Thread.currentThread() == this && state == RUNNING; 539 540 // emit JFR event if enabled 541 if (VirtualThreadStartEvent.isTurnedOn()) { 542 var event = new VirtualThreadStartEvent(); 543 event.javaThreadId = threadId(); 544 event.commit(); 545 } 546 547 Object bindings = Thread.scopedValueBindings(); 548 try { 549 runWith(bindings, task); 550 } catch (Throwable exc) { 551 dispatchUncaughtException(exc); 552 } finally { 553 // pop any remaining scopes from the stack, this may block 554 StackableScope.popAll(); 555 556 // emit JFR event if enabled 557 if (VirtualThreadEndEvent.isTurnedOn()) { 558 var event = new VirtualThreadEndEvent(); 559 event.javaThreadId = threadId(); 560 event.commit(); 561 } 562 } 563 } 564 565 /** 566 * Mounts this virtual thread onto the current platform thread. On 567 * return, the current thread is the virtual thread. 568 */ 569 @ChangesCurrentThread 570 @ReservedStackAccess 571 private void mount() { 572 startTransition(/*mount*/true); 573 // We assume following volatile accesses provide equivalent 574 // of acquire ordering, otherwise we need U.loadFence() here. 575 576 // sets the carrier thread 577 Thread carrier = Thread.currentCarrierThread(); 578 setCarrierThread(carrier); 579 580 // sync up carrier thread interrupted status if needed 581 if (interrupted) { 582 carrier.setInterrupt(); 583 } else if (carrier.isInterrupted()) { 584 synchronized (interruptLock) { 585 // need to recheck interrupted status 586 if (!interrupted) { 587 carrier.clearInterrupt(); 588 } 589 } 590 } 591 592 // set Thread.currentThread() to return this virtual thread 593 carrier.setCurrentThread(this); 594 } 595 596 /** 597 * Unmounts this virtual thread from the carrier. On return, the 598 * current thread is the current platform thread. 599 */ 600 @ChangesCurrentThread 601 @ReservedStackAccess 602 private void unmount() { 603 assert !Thread.holdsLock(interruptLock); 604 605 // set Thread.currentThread() to return the platform thread 606 Thread carrier = this.carrierThread; 607 carrier.setCurrentThread(carrier); 608 609 // break connection to carrier thread, synchronized with interrupt 610 synchronized (interruptLock) { 611 setCarrierThread(null); 612 } 613 carrier.clearInterrupt(); 614 615 // We assume previous volatile accesses provide equivalent 616 // of release ordering, otherwise we need U.storeFence() here. 617 endTransition(/*mount*/false); 618 } 619 620 /** 621 * Invokes Continuation.yield, notifying JVMTI (if enabled) to hide frames until 622 * the continuation continues. 623 */ 624 @Hidden 625 private boolean yieldContinuation() { 626 startTransition(/*mount*/false); 627 try { 628 return Continuation.yield(VTHREAD_SCOPE); 629 } finally { 630 endTransition(/*mount*/true); 631 } 632 } 633 634 /** 635 * Invoked in the context of the carrier thread after the Continuation yields when 636 * parking, blocking on monitor enter, Object.wait, or Thread.yield. 637 */ 638 private void afterYield() { 639 assert carrierThread == null; 640 641 // re-adjust parallelism if the virtual thread yielded when compensating 642 if (currentThread() instanceof CarrierThread ct) { 643 ct.endBlocking(); 644 } 645 646 int s = state(); 647 648 // LockSupport.park/parkNanos 649 if (s == PARKING || s == TIMED_PARKING) { 650 int newState; 651 if (s == PARKING) { 652 setState(newState = PARKED); 653 } else { 654 // schedule unpark 655 long timeout = this.timeout; 656 assert timeout > 0; 657 timeoutTask = schedule(this::parkTimeoutExpired, timeout, NANOSECONDS); 658 setState(newState = TIMED_PARKED); 659 } 660 661 // may have been unparked while parking 662 if (parkPermit && compareAndSetState(newState, UNPARKED)) { 663 // lazy submit if local queue is empty 664 lazySubmitRunContinuation(); 665 } 666 return; 667 } 668 669 // Thread.yield 670 if (s == YIELDING) { 671 setState(YIELDED); 672 673 // external submit if there are no tasks in the local task queue 674 if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) { 675 externalSubmitRunContinuation(); 676 } else { 677 submitRunContinuation(); 678 } 679 return; 680 } 681 682 // blocking on monitorenter 683 if (s == BLOCKING) { 684 setState(BLOCKED); 685 686 // may have been unblocked while blocking 687 if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) { 688 // lazy submit if local queue is empty 689 lazySubmitRunContinuation(); 690 } 691 return; 692 } 693 694 // Object.wait 695 if (s == WAITING || s == TIMED_WAITING) { 696 int newState; 697 boolean blocked; 698 boolean interruptible = interruptibleWait; 699 if (s == WAITING) { 700 setState(newState = WAIT); 701 // may have been notified while in transition 702 blocked = notified && compareAndSetState(WAIT, BLOCKED); 703 } else { 704 // For timed-wait, a timeout task is scheduled to execute. The timeout 705 // task will change the thread state to UNBLOCKED and submit the thread 706 // to the scheduler. A sequence number is used to ensure that the timeout 707 // task only unblocks the thread for this timed-wait. We synchronize with 708 // the timeout task to coordinate access to the sequence number and to 709 // ensure the timeout task doesn't execute until the thread has got to 710 // the TIMED_WAIT state. 711 long timeout = this.timeout; 712 assert timeout > 0; 713 synchronized (timedWaitLock()) { 714 byte seqNo = ++timedWaitSeqNo; 715 timeoutTask = schedule(() -> waitTimeoutExpired(seqNo), timeout, MILLISECONDS); 716 setState(newState = TIMED_WAIT); 717 // May have been notified while in transition. This must be done while 718 // holding the monitor to avoid changing the state of a new timed wait call. 719 blocked = notified && compareAndSetState(TIMED_WAIT, BLOCKED); 720 } 721 } 722 723 if (blocked) { 724 // may have been unblocked already 725 if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) { 726 lazySubmitRunContinuation(); 727 } 728 } else { 729 // may have been interrupted while in transition to wait state 730 if (interruptible && interrupted && compareAndSetState(newState, UNBLOCKED)) { 731 lazySubmitRunContinuation(); 732 } 733 } 734 return; 735 } 736 737 assert false; 738 } 739 740 /** 741 * Invoked after the continuation completes. 742 */ 743 private void afterDone() { 744 afterDone(true); 745 } 746 747 /** 748 * Invoked after the continuation completes (or start failed). Sets the thread 749 * state to TERMINATED and notifies anyone waiting for the thread to terminate. 750 * 751 * @param notifyContainer true if its container should be notified 752 */ 753 private void afterDone(boolean notifyContainer) { 754 assert carrierThread == null; 755 setState(TERMINATED); 756 757 // notifyAll to wakeup any threads waiting for this thread to terminate 758 if (notifyAllAfterTerminate) { 759 synchronized (this) { 760 notifyAll(); 761 } 762 } 763 764 // notify container 765 if (notifyContainer) { 766 threadContainer().remove(this); 767 } 768 769 // clear references to thread locals 770 clearReferences(); 771 } 772 773 /** 774 * Schedules this {@code VirtualThread} to execute. 775 * 776 * @throws IllegalStateException if the container is shutdown or closed 777 * @throws IllegalThreadStateException if the thread has already been started 778 * @throws RejectedExecutionException if the scheduler cannot accept a task 779 */ 780 @Override 781 void start(ThreadContainer container) { 782 if (!compareAndSetState(NEW, STARTED)) { 783 throw new IllegalThreadStateException("Already started"); 784 } 785 786 // bind thread to container 787 assert threadContainer() == null; 788 setThreadContainer(container); 789 790 // start thread 791 boolean addedToContainer = false; 792 boolean started = false; 793 try { 794 container.add(this); // may throw 795 addedToContainer = true; 796 797 // scoped values may be inherited 798 inheritScopedValueBindings(container); 799 800 // submit task to schedule 801 try { 802 if (currentThread().isVirtual()) { 803 Continuation.pin(); 804 try { 805 if (scheduler == BUILTIN_SCHEDULER 806 && currentCarrierThread() instanceof CarrierThread ct) { 807 ForkJoinPool pool = ct.getPool(); 808 ForkJoinTask<?> task = ForkJoinTask.adapt(runContinuation); 809 pool.externalSubmit(task); 810 } else { 811 scheduler.onStart(runContinuation); 812 } 813 } finally { 814 Continuation.unpin(); 815 } 816 } else { 817 scheduler.onStart(runContinuation); 818 } 819 } catch (RejectedExecutionException ree) { 820 submitFailed(ree); 821 throw ree; 822 } 823 824 started = true; 825 } finally { 826 if (!started) { 827 afterDone(addedToContainer); 828 } 829 } 830 } 831 832 @Override 833 public void start() { 834 start(ThreadContainers.root()); 835 } 836 837 @Override 838 public void run() { 839 // do nothing 840 } 841 842 /** 843 * Invoked by Thread.join before a thread waits for this virtual thread to terminate. 844 */ 845 void beforeJoin() { 846 notifyAllAfterTerminate = true; 847 } 848 849 /** 850 * Parks until unparked or interrupted. If already unparked then the parking 851 * permit is consumed and this method completes immediately (meaning it doesn't 852 * yield). It also completes immediately if the interrupted status is set. 853 */ 854 @Override 855 void park() { 856 assert Thread.currentThread() == this; 857 858 // complete immediately if parking permit available or interrupted 859 if (getAndSetParkPermit(false) || interrupted) 860 return; 861 862 // park the thread 863 boolean yielded = false; 864 setState(PARKING); 865 try { 866 yielded = yieldContinuation(); 867 } catch (OutOfMemoryError e) { 868 // park on carrier 869 } finally { 870 assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING)); 871 if (!yielded) { 872 assert state() == PARKING; 873 setState(RUNNING); 874 } 875 } 876 877 // park on the carrier thread when pinned 878 if (!yielded) { 879 parkOnCarrierThread(false, 0); 880 } 881 } 882 883 /** 884 * Parks up to the given waiting time or until unparked or interrupted. 885 * If already unparked then the parking permit is consumed and this method 886 * completes immediately (meaning it doesn't yield). It also completes immediately 887 * if the interrupted status is set or the waiting time is {@code <= 0}. 888 * 889 * @param nanos the maximum number of nanoseconds to wait. 890 */ 891 @Override 892 void parkNanos(long nanos) { 893 assert Thread.currentThread() == this; 894 895 // complete immediately if parking permit available or interrupted 896 if (getAndSetParkPermit(false) || interrupted) 897 return; 898 899 // park the thread for the waiting time 900 if (nanos > 0) { 901 long startTime = System.nanoTime(); 902 903 // park the thread, afterYield will schedule the thread to unpark 904 boolean yielded = false; 905 timeout = nanos; 906 setState(TIMED_PARKING); 907 try { 908 yielded = yieldContinuation(); 909 } catch (OutOfMemoryError e) { 910 // park on carrier 911 } finally { 912 assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING)); 913 if (!yielded) { 914 assert state() == TIMED_PARKING; 915 setState(RUNNING); 916 } 917 } 918 919 // park on carrier thread for remaining time when pinned (or OOME) 920 if (!yielded) { 921 long remainingNanos = nanos - (System.nanoTime() - startTime); 922 parkOnCarrierThread(true, remainingNanos); 923 } 924 } 925 } 926 927 /** 928 * Parks the current carrier thread up to the given waiting time or until 929 * unparked or interrupted. If the virtual thread is interrupted then the 930 * interrupted status will be propagated to the carrier thread. 931 * @param timed true for a timed park, false for untimed 932 * @param nanos the waiting time in nanoseconds 933 */ 934 private void parkOnCarrierThread(boolean timed, long nanos) { 935 assert state() == RUNNING; 936 937 setState(timed ? TIMED_PINNED : PINNED); 938 try { 939 if (!parkPermit) { 940 if (!timed) { 941 U.park(false, 0); 942 } else if (nanos > 0) { 943 U.park(false, nanos); 944 } 945 } 946 } finally { 947 setState(RUNNING); 948 } 949 950 // consume parking permit 951 setParkPermit(false); 952 953 // JFR jdk.VirtualThreadPinned event 954 postPinnedEvent("LockSupport.park"); 955 } 956 957 /** 958 * Call into VM when pinned to record a JFR jdk.VirtualThreadPinned event. 959 * Recording the event in the VM avoids having JFR event recorded in Java 960 * with the same name, but different ID, to events recorded by the VM. 961 */ 962 @Hidden 963 private static native void postPinnedEvent(String op); 964 965 /** 966 * Re-enables this virtual thread for scheduling. If this virtual thread is parked 967 * then its task is scheduled to continue, otherwise its next call to {@code park} or 968 * {@linkplain #parkNanos(long) parkNanos} is guaranteed not to block. 969 * @param lazySubmit to use lazySubmit if possible 970 * @throws RejectedExecutionException if the scheduler cannot accept a task 971 */ 972 private void unpark(boolean lazySubmit) { 973 if (!getAndSetParkPermit(true) && currentThread() != this) { 974 int s = state(); 975 976 // unparked while parked 977 if ((s == PARKED || s == TIMED_PARKED) && compareAndSetState(s, UNPARKED)) { 978 979 if (lazySubmit && currentThread().isVirtual()) { 980 Continuation.pin(); 981 try { 982 if (scheduler == BUILTIN_SCHEDULER 983 && currentCarrierThread() instanceof CarrierThread ct) { 984 ct.getPool().lazySubmit(ForkJoinTask.adapt(runContinuation)); 985 return; 986 } 987 } catch (RejectedExecutionException ree) { 988 submitFailed(ree); 989 throw ree; 990 } catch (OutOfMemoryError e) { 991 // fall-though 992 } finally { 993 Continuation.unpin(); 994 } 995 } 996 997 submitRunContinuation(); 998 return; 999 } 1000 1001 // unparked while parked when pinned 1002 if (s == PINNED || s == TIMED_PINNED) { 1003 // unpark carrier thread when pinned 1004 disableSuspendAndPreempt(); 1005 try { 1006 synchronized (carrierThreadAccessLock()) { 1007 Thread carrier = carrierThread; 1008 if (carrier != null && ((s = state()) == PINNED || s == TIMED_PINNED)) { 1009 U.unpark(carrier); 1010 } 1011 } 1012 } finally { 1013 enableSuspendAndPreempt(); 1014 } 1015 return; 1016 } 1017 } 1018 } 1019 1020 @Override 1021 void unpark() { 1022 unpark(false); 1023 } 1024 1025 /** 1026 * Invoked by unblocker thread to unblock this virtual thread. 1027 */ 1028 private void unblock() { 1029 assert !Thread.currentThread().isVirtual(); 1030 blockPermit = true; 1031 if (state() == BLOCKED && compareAndSetState(BLOCKED, UNBLOCKED)) { 1032 submitRunContinuation(); 1033 } 1034 } 1035 1036 /** 1037 * Invoked by FJP worker thread or STPE thread when park timeout expires. 1038 */ 1039 private void parkTimeoutExpired() { 1040 assert !VirtualThread.currentThread().isVirtual(); 1041 unpark(true); 1042 } 1043 1044 /** 1045 * Invoked by FJP worker thread or STPE thread when wait timeout expires. 1046 * If the virtual thread is in timed-wait then this method will unblock the thread 1047 * and submit its task so that it continues and attempts to reenter the monitor. 1048 * This method does nothing if the thread has been woken by notify or interrupt. 1049 */ 1050 private void waitTimeoutExpired(byte seqNo) { 1051 assert !Thread.currentThread().isVirtual(); 1052 1053 synchronized (timedWaitLock()) { 1054 if (seqNo != timedWaitSeqNo) { 1055 // this timeout task is for a past timed-wait 1056 return; 1057 } 1058 if (!compareAndSetState(TIMED_WAIT, UNBLOCKED)) { 1059 // already notified (or interrupted) 1060 return; 1061 } 1062 } 1063 1064 lazySubmitRunContinuation(); 1065 } 1066 1067 /** 1068 * Attempts to yield the current virtual thread (Thread.yield). 1069 */ 1070 void tryYield() { 1071 assert Thread.currentThread() == this; 1072 setState(YIELDING); 1073 boolean yielded = false; 1074 try { 1075 yielded = yieldContinuation(); // may throw 1076 } finally { 1077 assert (Thread.currentThread() == this) && (yielded == (state() == RUNNING)); 1078 if (!yielded) { 1079 assert state() == YIELDING; 1080 setState(RUNNING); 1081 } 1082 } 1083 } 1084 1085 /** 1086 * Sleep the current thread for the given sleep time (in nanoseconds). If 1087 * nanos is 0 then the thread will attempt to yield. 1088 * 1089 * @implNote This implementation parks the thread for the given sleeping time 1090 * and will therefore be observed in PARKED state during the sleep. Parking 1091 * will consume the parking permit so this method makes available the parking 1092 * permit after the sleep. This may be observed as a spurious, but benign, 1093 * wakeup when the thread subsequently attempts to park. 1094 * 1095 * @param nanos the maximum number of nanoseconds to sleep 1096 * @throws InterruptedException if interrupted while sleeping 1097 */ 1098 void sleepNanos(long nanos) throws InterruptedException { 1099 assert Thread.currentThread() == this && nanos >= 0; 1100 if (getAndClearInterrupt()) 1101 throw new InterruptedException(); 1102 if (nanos == 0) { 1103 tryYield(); 1104 } else { 1105 // park for the sleep time 1106 try { 1107 long remainingNanos = nanos; 1108 long startNanos = System.nanoTime(); 1109 while (remainingNanos > 0) { 1110 parkNanos(remainingNanos); 1111 if (getAndClearInterrupt()) { 1112 throw new InterruptedException(); 1113 } 1114 remainingNanos = nanos - (System.nanoTime() - startNanos); 1115 } 1116 } finally { 1117 // may have been unparked while sleeping 1118 setParkPermit(true); 1119 } 1120 } 1121 } 1122 1123 @Override 1124 void blockedOn(Interruptible b) { 1125 disableSuspendAndPreempt(); 1126 try { 1127 super.blockedOn(b); 1128 } finally { 1129 enableSuspendAndPreempt(); 1130 } 1131 } 1132 1133 @Override 1134 public void interrupt() { 1135 if (Thread.currentThread() != this) { 1136 // if current thread is a virtual thread then prevent it from being 1137 // suspended or unmounted when entering or holding interruptLock 1138 Interruptible blocker; 1139 disableSuspendAndPreempt(); 1140 try { 1141 synchronized (interruptLock) { 1142 interrupted = true; 1143 blocker = nioBlocker(); 1144 if (blocker != null) { 1145 blocker.interrupt(this); 1146 } 1147 1148 // interrupt carrier thread if mounted 1149 Thread carrier = carrierThread; 1150 if (carrier != null) carrier.setInterrupt(); 1151 } 1152 } finally { 1153 enableSuspendAndPreempt(); 1154 } 1155 1156 // notify blocker after releasing interruptLock 1157 if (blocker != null) { 1158 blocker.postInterrupt(); 1159 } 1160 1161 // make available parking permit, unpark thread if parked 1162 unpark(); 1163 1164 // if thread is waiting in Object.wait then schedule to try to reenter 1165 int s = state(); 1166 if ((s == WAIT || s == TIMED_WAIT) && compareAndSetState(s, UNBLOCKED)) { 1167 submitRunContinuation(); 1168 } 1169 1170 } else { 1171 interrupted = true; 1172 carrierThread.setInterrupt(); 1173 setParkPermit(true); 1174 } 1175 } 1176 1177 @Override 1178 public boolean isInterrupted() { 1179 return interrupted; 1180 } 1181 1182 @Override 1183 boolean getAndClearInterrupt() { 1184 assert Thread.currentThread() == this; 1185 boolean oldValue = interrupted; 1186 if (oldValue) { 1187 disableSuspendAndPreempt(); 1188 try { 1189 synchronized (interruptLock) { 1190 interrupted = false; 1191 carrierThread.clearInterrupt(); 1192 } 1193 } finally { 1194 enableSuspendAndPreempt(); 1195 } 1196 } 1197 return oldValue; 1198 } 1199 1200 @Override 1201 Thread.State threadState() { 1202 switch (state()) { 1203 case NEW: 1204 return Thread.State.NEW; 1205 case STARTED: 1206 // return NEW if thread container not yet set 1207 if (threadContainer() == null) { 1208 return Thread.State.NEW; 1209 } else { 1210 return Thread.State.RUNNABLE; 1211 } 1212 case UNPARKED: 1213 case UNBLOCKED: 1214 case YIELDED: 1215 // runnable, not mounted 1216 return Thread.State.RUNNABLE; 1217 case RUNNING: 1218 // if mounted then return state of carrier thread 1219 if (Thread.currentThread() != this) { 1220 disableSuspendAndPreempt(); 1221 try { 1222 synchronized (carrierThreadAccessLock()) { 1223 Thread carrierThread = this.carrierThread; 1224 if (carrierThread != null) { 1225 return carrierThread.threadState(); 1226 } 1227 } 1228 } finally { 1229 enableSuspendAndPreempt(); 1230 } 1231 } 1232 // runnable, mounted 1233 return Thread.State.RUNNABLE; 1234 case PARKING: 1235 case TIMED_PARKING: 1236 case WAITING: 1237 case TIMED_WAITING: 1238 case YIELDING: 1239 // runnable, in transition 1240 return Thread.State.RUNNABLE; 1241 case PARKED: 1242 case PINNED: 1243 case WAIT: 1244 return Thread.State.WAITING; 1245 case TIMED_PARKED: 1246 case TIMED_PINNED: 1247 case TIMED_WAIT: 1248 return Thread.State.TIMED_WAITING; 1249 case BLOCKING: 1250 case BLOCKED: 1251 return Thread.State.BLOCKED; 1252 case TERMINATED: 1253 return Thread.State.TERMINATED; 1254 default: 1255 throw new InternalError(); 1256 } 1257 } 1258 1259 @Override 1260 boolean alive() { 1261 int s = state; 1262 return (s != NEW && s != TERMINATED); 1263 } 1264 1265 @Override 1266 boolean isTerminated() { 1267 return (state == TERMINATED); 1268 } 1269 1270 @Override 1271 public String toString() { 1272 StringBuilder sb = new StringBuilder("VirtualThread[#"); 1273 sb.append(threadId()); 1274 String name = getName(); 1275 if (!name.isEmpty()) { 1276 sb.append(","); 1277 sb.append(name); 1278 } 1279 sb.append("]/"); 1280 1281 // add the carrier state and thread name when mounted 1282 boolean mounted; 1283 if (Thread.currentThread() == this) { 1284 mounted = appendCarrierInfo(sb); 1285 } else { 1286 disableSuspendAndPreempt(); 1287 try { 1288 synchronized (carrierThreadAccessLock()) { 1289 mounted = appendCarrierInfo(sb); 1290 } 1291 } finally { 1292 enableSuspendAndPreempt(); 1293 } 1294 } 1295 1296 // add virtual thread state when not mounted 1297 if (!mounted) { 1298 String stateAsString = threadState().toString(); 1299 sb.append(stateAsString.toLowerCase(Locale.ROOT)); 1300 } 1301 1302 return sb.toString(); 1303 } 1304 1305 /** 1306 * Appends the carrier state and thread name to the string buffer if mounted. 1307 * @return true if mounted, false if not mounted 1308 */ 1309 private boolean appendCarrierInfo(StringBuilder sb) { 1310 assert Thread.currentThread() == this || Thread.holdsLock(carrierThreadAccessLock()); 1311 Thread carrier = carrierThread; 1312 if (carrier != null) { 1313 String stateAsString = carrier.threadState().toString(); 1314 sb.append(stateAsString.toLowerCase(Locale.ROOT)); 1315 sb.append('@'); 1316 sb.append(carrier.getName()); 1317 return true; 1318 } else { 1319 return false; 1320 } 1321 } 1322 1323 @Override 1324 public int hashCode() { 1325 return (int) threadId(); 1326 } 1327 1328 @Override 1329 public boolean equals(Object obj) { 1330 return obj == this; 1331 } 1332 1333 /** 1334 * Returns the lock object to synchronize on when accessing carrierThread. 1335 * The lock prevents carrierThread from being reset to null during unmount. 1336 */ 1337 private Object carrierThreadAccessLock() { 1338 // return interruptLock as unmount has to coordinate with interrupt 1339 return interruptLock; 1340 } 1341 1342 /** 1343 * Returns a lock object for coordinating timed-wait setup and timeout handling. 1344 */ 1345 private Object timedWaitLock() { 1346 // use this object for now to avoid the overhead of introducing another lock 1347 return runContinuation; 1348 } 1349 1350 /** 1351 * Disallow the current thread be suspended or preempted. 1352 */ 1353 private void disableSuspendAndPreempt() { 1354 notifyJvmtiDisableSuspend(true); 1355 Continuation.pin(); 1356 } 1357 1358 /** 1359 * Allow the current thread be suspended or preempted. 1360 */ 1361 private void enableSuspendAndPreempt() { 1362 Continuation.unpin(); 1363 notifyJvmtiDisableSuspend(false); 1364 } 1365 1366 // -- wrappers for get/set of state, parking permit, and carrier thread -- 1367 1368 private int state() { 1369 return state; // volatile read 1370 } 1371 1372 private void setState(int newValue) { 1373 state = newValue; // volatile write 1374 } 1375 1376 private boolean compareAndSetState(int expectedValue, int newValue) { 1377 return U.compareAndSetInt(this, STATE, expectedValue, newValue); 1378 } 1379 1380 private boolean compareAndSetOnWaitingList(boolean expectedValue, boolean newValue) { 1381 return U.compareAndSetBoolean(this, ON_WAITING_LIST, expectedValue, newValue); 1382 } 1383 1384 private void setParkPermit(boolean newValue) { 1385 if (parkPermit != newValue) { 1386 parkPermit = newValue; 1387 } 1388 } 1389 1390 private boolean getAndSetParkPermit(boolean newValue) { 1391 if (parkPermit != newValue) { 1392 return U.getAndSetBoolean(this, PARK_PERMIT, newValue); 1393 } else { 1394 return newValue; 1395 } 1396 } 1397 1398 private void setCarrierThread(Thread carrier) { 1399 // U.putReferenceRelease(this, CARRIER_THREAD, carrier); 1400 this.carrierThread = carrier; 1401 } 1402 1403 // The following four methods notify the VM when a "transition" starts and ends. 1404 // A "mount transition" embodies the steps to transfer control from a platform 1405 // thread to a virtual thread, changing the thread identity, and starting or 1406 // resuming the virtual thread's continuation on the carrier. 1407 // An "unmount transition" embodies the steps to transfer control from a virtual 1408 // thread to its carrier, suspending the virtual thread's continuation, and 1409 // restoring the thread identity to the platform thread. 1410 // The notifications to the VM are necessary in order to coordinate with functions 1411 // (JVMTI mostly) that disable transitions for one or all virtual threads. Starting 1412 // a transition may block if transitions are disabled. Ending a transition may 1413 // notify a thread that is waiting to disable transitions. The notifications are 1414 // also used to post JVMTI events for virtual thread start and end. 1415 1416 @IntrinsicCandidate 1417 @JvmtiMountTransition 1418 private native void endFirstTransition(); 1419 1420 @IntrinsicCandidate 1421 @JvmtiMountTransition 1422 private native void startFinalTransition(); 1423 1424 @IntrinsicCandidate 1425 @JvmtiMountTransition 1426 private native void startTransition(boolean mount); 1427 1428 @IntrinsicCandidate 1429 @JvmtiMountTransition 1430 private native void endTransition(boolean mount); 1431 1432 @IntrinsicCandidate 1433 private static native void notifyJvmtiDisableSuspend(boolean enter); 1434 1435 private static native void registerNatives(); 1436 static { 1437 registerNatives(); 1438 1439 // ensure VTHREAD_GROUP is created, may be accessed by JVMTI 1440 var group = Thread.virtualThreadGroup(); 1441 } 1442 1443 /** 1444 * Loads a VirtualThreadScheduler with the given class name. The class must be public 1445 * in an exported package, with public one-arg or no-arg constructor, and be visible 1446 * to the system class loader. 1447 * @param delegate the scheduler that the custom scheduler may delegate to 1448 * @param cn the class name of the custom scheduler 1449 */ 1450 private static VirtualThreadScheduler loadCustomScheduler(VirtualThreadScheduler delegate, String cn) { 1451 VirtualThreadScheduler scheduler; 1452 try { 1453 Class<?> clazz = Class.forName(cn, true, ClassLoader.getSystemClassLoader()); 1454 // 1-arg constructor 1455 try { 1456 Constructor<?> ctor = clazz.getConstructor(VirtualThreadScheduler.class); 1457 return (VirtualThreadScheduler) ctor.newInstance(delegate); 1458 } catch (NoSuchMethodException e) { 1459 // 0-arg constructor 1460 Constructor<?> ctor = clazz.getConstructor(); 1461 scheduler = (VirtualThreadScheduler) ctor.newInstance(); 1462 } 1463 } catch (Exception ex) { 1464 throw new Error(ex); 1465 } 1466 System.err.println("WARNING: Using custom default scheduler, this is an experimental feature!"); 1467 return scheduler; 1468 } 1469 1470 /** 1471 * Creates the built-in ForkJoinPool scheduler. 1472 * @param wrapped true if wrapped by a custom default scheduler 1473 */ 1474 private static VirtualThreadScheduler createBuiltinScheduler(boolean wrapped) { 1475 int parallelism, maxPoolSize, minRunnable; 1476 String parallelismValue = System.getProperty("jdk.virtualThreadScheduler.parallelism"); 1477 String maxPoolSizeValue = System.getProperty("jdk.virtualThreadScheduler.maxPoolSize"); 1478 String minRunnableValue = System.getProperty("jdk.virtualThreadScheduler.minRunnable"); 1479 if (parallelismValue != null) { 1480 parallelism = Integer.parseInt(parallelismValue); 1481 } else { 1482 parallelism = Runtime.getRuntime().availableProcessors(); 1483 } 1484 if (maxPoolSizeValue != null) { 1485 maxPoolSize = Integer.parseInt(maxPoolSizeValue); 1486 parallelism = Integer.min(parallelism, maxPoolSize); 1487 } else { 1488 maxPoolSize = Integer.max(parallelism, 256); 1489 } 1490 if (minRunnableValue != null) { 1491 minRunnable = Integer.parseInt(minRunnableValue); 1492 } else { 1493 minRunnable = Integer.max(parallelism / 2, 1); 1494 } 1495 if (Boolean.getBoolean("jdk.virtualThreadScheduler.useTPE")) { 1496 return new BuiltinThreadPoolExecutorScheduler(parallelism); 1497 } else { 1498 return new BuiltinForkJoinPoolScheduler(parallelism, maxPoolSize, minRunnable, wrapped); 1499 } 1500 } 1501 1502 /** 1503 * The built-in ForkJoinPool scheduler. 1504 */ 1505 private static class BuiltinForkJoinPoolScheduler 1506 extends ForkJoinPool implements VirtualThreadScheduler { 1507 1508 BuiltinForkJoinPoolScheduler(int parallelism, int maxPoolSize, int minRunnable, boolean wrapped) { 1509 ForkJoinWorkerThreadFactory factory = wrapped 1510 ? ForkJoinPool.defaultForkJoinWorkerThreadFactory 1511 : CarrierThread::new; 1512 Thread.UncaughtExceptionHandler handler = (t, e) -> { }; 1513 boolean asyncMode = true; // FIFO 1514 super(parallelism, factory, handler, asyncMode, 1515 0, maxPoolSize, minRunnable, pool -> true, 30L, SECONDS); 1516 } 1517 1518 @Override 1519 public void onStart(VirtualThreadTask task) { 1520 execute(ForkJoinTask.adapt(task)); 1521 } 1522 1523 @Override 1524 public void onContinue(VirtualThreadTask task) { 1525 execute(ForkJoinTask.adapt(task)); 1526 } 1527 1528 @Override 1529 public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) { 1530 return super.schedule(task, delay, unit); 1531 } 1532 } 1533 1534 /** 1535 * Built-in ThreadPoolExecutor scheduler. 1536 */ 1537 private static class BuiltinThreadPoolExecutorScheduler 1538 extends ThreadPoolExecutor implements VirtualThreadScheduler { 1539 1540 BuiltinThreadPoolExecutorScheduler(int maxPoolSize) { 1541 ThreadFactory factory = task -> { 1542 Thread t = InnocuousThread.newThread(task); 1543 t.setDaemon(true); 1544 return t; 1545 }; 1546 super(maxPoolSize, maxPoolSize, 1547 0L, SECONDS, 1548 new LinkedTransferQueue<>(), 1549 factory); 1550 } 1551 1552 @Override 1553 public void onStart(VirtualThreadTask task) { 1554 execute(task); 1555 } 1556 1557 @Override 1558 public void onContinue(VirtualThreadTask task) { 1559 execute(task); 1560 } 1561 } 1562 1563 /** 1564 * Wraps the scheduler to avoid leaking a direct reference to built-in scheduler. 1565 */ 1566 static VirtualThreadScheduler createExternalView(VirtualThreadScheduler delegate) { 1567 return new VirtualThreadScheduler() { 1568 private void check(VirtualThreadTask task) { 1569 var vthread = (VirtualThread) task.thread(); 1570 VirtualThreadScheduler scheduler = vthread.scheduler; 1571 if (scheduler != this && scheduler != DEFAULT_SCHEDULER) { 1572 throw new IllegalArgumentException(); 1573 } 1574 } 1575 @Override 1576 public void onStart(VirtualThreadTask task) { 1577 check(task); 1578 delegate.onStart(task); 1579 } 1580 @Override 1581 public void onContinue(VirtualThreadTask task) { 1582 check(task); 1583 delegate.onContinue(task); 1584 } 1585 @Override 1586 public String toString() { 1587 return delegate.toString(); 1588 } 1589 }; 1590 } 1591 1592 /** 1593 * Schedule a runnable task to run after a delay. 1594 */ 1595 private Future<?> schedule(Runnable command, long delay, TimeUnit unit) { 1596 if (USE_STPE) { 1597 return DelayedTaskSchedulers.schedule(command, delay, unit); 1598 } else { 1599 return scheduler.schedule(command, delay, unit); 1600 } 1601 } 1602 1603 /** 1604 * Supports scheduling a runnable task to run after a delay. It uses a number 1605 * of ScheduledThreadPoolExecutor instances to reduce contention on the delayed 1606 * work queue used. This class is used when using a custom scheduler. 1607 */ 1608 static class DelayedTaskSchedulers { 1609 private static final ScheduledExecutorService[] INSTANCE = createDelayedTaskSchedulers(); 1610 1611 static Future<?> schedule(Runnable command, long delay, TimeUnit unit) { 1612 long tid = Thread.currentThread().threadId(); 1613 int index = (int) tid & (INSTANCE.length - 1); 1614 return INSTANCE[index].schedule(command, delay, unit); 1615 } 1616 1617 private static ScheduledExecutorService[] createDelayedTaskSchedulers() { 1618 String propName = "jdk.virtualThreadScheduler.timerQueues"; 1619 String propValue = System.getProperty(propName); 1620 int queueCount; 1621 if (propValue != null) { 1622 queueCount = Integer.parseInt(propValue); 1623 if (queueCount != Integer.highestOneBit(queueCount)) { 1624 throw new RuntimeException("Value of " + propName + " must be power of 2"); 1625 } 1626 } else { 1627 int ncpus = Runtime.getRuntime().availableProcessors(); 1628 queueCount = Math.max(Integer.highestOneBit(ncpus / 4), 1); 1629 } 1630 var schedulers = new ScheduledExecutorService[queueCount]; 1631 for (int i = 0; i < queueCount; i++) { 1632 ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) 1633 Executors.newScheduledThreadPool(1, task -> { 1634 Thread t = InnocuousThread.newThread("VirtualThread-unparker", task); 1635 t.setDaemon(true); 1636 return t; 1637 }); 1638 stpe.setRemoveOnCancelPolicy(true); 1639 schedulers[i] = stpe; 1640 } 1641 return schedulers; 1642 } 1643 } 1644 1645 /** 1646 * Schedule virtual threads that are ready to be scheduled after they blocked on 1647 * monitor enter. 1648 */ 1649 private static void unblockVirtualThreads() { 1650 while (true) { 1651 VirtualThread vthread = takeVirtualThreadListToUnblock(); 1652 while (vthread != null) { 1653 assert vthread.onWaitingList; 1654 VirtualThread nextThread = vthread.next; 1655 1656 // remove from list and unblock 1657 vthread.next = null; 1658 boolean changed = vthread.compareAndSetOnWaitingList(true, false); 1659 assert changed; 1660 vthread.unblock(); 1661 1662 vthread = nextThread; 1663 } 1664 } 1665 } 1666 1667 /** 1668 * Retrieves the list of virtual threads that are waiting to be unblocked, waiting 1669 * if necessary until a list of one or more threads becomes available. 1670 */ 1671 private static native VirtualThread takeVirtualThreadListToUnblock(); 1672 1673 static { 1674 var unblocker = InnocuousThread.newThread("VirtualThread-unblocker", 1675 VirtualThread::unblockVirtualThreads); 1676 unblocker.setDaemon(true); 1677 unblocker.start(); 1678 } 1679 } --- EOF ---