1 # Custom Schedulers
 2 
 3 The following is a summary of the experimental support for custom virtual thread schedulers
 4 in the `fibers` branch of the loom repo.
 5 
 6 The purpose of the experimental support is to allow exploration and get feedback to help
 7 inform this project on whether to expose anything.
 8 
 9 The experimental support may change or be removed at any time.
10 
11 ## Prototype 1: Use a custom scheduler as the virtual thread scheduler
12 
13 The JDK's built-in virtual thread scheduler is a `ForkJoinPool` instance that is
14 configured in FIFO mode.
15 
16 This prototype allows the virtual thread scheduler to be set to different scheduler by
17 setting a system property on the command line:
18 
19 ```
20 -Djdk.virtualThreadScheduler.implClass=<scheduler-class>
21 ```
22 
23 where `<scheduler-class>` is fully qualified name of a class that implements
24 `java.lang.Thread.VirtualThreadScheduler`. The interface defines the `onStart` and
25 `onContinue` methods that the custom scheduler must implement. The `onStart` method is
26 invoked when `Thread::start` is used to start a virtual thread. The `onContinue` method
27 is invoked when a virtual thread is scheduled to continue after being parked or blocked.
28 The two methods are called with the task to execute.
29 
30 The custom scheduler may use its own pool of platform threads to execute the tasks,
31 may assign virtual threads to be carried by specific platform threads, or may delegate
32 to the built-in virtual thread scheduler. The prototype method
33 `Thread.Builder.OfVirtual.unstarted(Runnable, Thread, Object)` can be used to specify
34 the preferred carrier when creating an unstarted virtual thread.
35 
36 The `VirtualThreadScheduler` implementation class must be public, with a public no-arg
37 or one-arg constructor, and deployed on the class path or in an exported package of a
38 module on the module path. If the class has a one-arg constructor then the parameter is
39 a `java.lang.Thread.VirtualThreadScheduler` that is a reference to the built-in default
40 scheduler (this allows the custom scheduler to delegate to the built-in default
41 scheduler if required).
42 
43 If the scheduler class implements `jdk.management.VirtualThreadSchedulerMXBean` then
44 the management interface will be registered with the platform `MBeanServer`.
45 
46 ## Prototype 2: Use API to select a custom scheduler when creating a virtual thread
47 
48 The `Thread.Builder.OfVirtual.scheduler(Thread.VirtualThreadScheduler)` API can be used
49 to set the scheduler when creating a virtual thread. The following example uses a thread
50 pool with 8 threads as the scheduler.
51 
52 ```
53 ExecutorService pool = Executors.newFixedThreadPool(8);
54 var scheduler = new VirtualThreadScheduler() {
55     @Override
56     public void onStart(VirtualThreadTask task) {
57         pool.execute(task);
58     }
59     @Override
60     public void onContinue(VirtualThreadTask task) {
61         pool.execute(task);
62     }
63 };
64 Thread thread = Thread.ofVirtual().scheduler(scheduler).start(() -> { });
65 thread.join();
66 ```
67 
68 The prototype API allows different parts of a system to use different schedulers.
69 
70 Custom schedulers are _not inherited_. If a virtual thread assigned to a custom
71 scheduler invokes `Thread.startVirtualThread(Runnable)` then it starts a virtual
72 thread assigned to the default scheduler.
73 
74 Custom schedulers are _not closable_. They are managed by the garbage collector so that
75 a custom scheduler can be collected when all virtual thread assigned to the scheduler
76 have terminated and the scheduler is otherwise unreachable. The lifecycle of carrier
77 threads is managed by the scheduler.
78 
79 ## Time Sharing
80 
81 Early prototype support for <em>forced preemption</em>, based on thread local handshakes,
82 exists in a branch in the loom repo. The prototypes in the `fibers` branch for custom
83 schedulers do not support forced preemption at this time.
84 
85 ## Poller modes
86 
87 Socket I/O in the context of virtual threads uses a platform specific I/O event notification
88 facility such as `kqueue`, `epoll`, or `io_uring`. The implementation uses of set of internal
89 _poller threads_ that consume events from the I/O event notification facility to unpark
90 virtual threads that are blocked in socket operations. The implementation has a number
91 of _poller modes_. On macOS and Windows there is a set of platform threads that wait for
92 events. On Linux, the poller threads that consume the events from the I/O event notification
93 facility are virtual threads assigned to the default scheduler.
94 
95 When using a custom scheduler it may be useful to use the poller mode that creates an I/O
96 event notification system and a poller virtual thread per carrier thread. The poller
97 thread terminates if the carrier terminates; any outstanding socket I/O operations
98 are moved to a different carrier's I/O event notification facility. This poller mode is
99 currently implemented on Linux and macOS and is used when running with `-Djdk.pollerMode=3`.