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 The prototype allows the virtual thread scheduler to be set 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.
33 
34 The `VirtualThreadScheduler` implementation class must be public, with a public no-arg
35 or one-arg constructor, and deployed on the class path or in an exported package of a
36 module on the module path. If the class has a one-arg constructor then the parameter is
37 a `java.lang.Thread.VirtualThreadScheduler` that is a reference to the built-in default
38 scheduler (this allows the custom scheduler to delegate to the built-in default
39 scheduler if required).
40 
41 
42 ## Prototype 2: Use API to select a custom scheduler when creating a virtual thread
43 
44 The `Thread.Builder.OfVirtual.scheduler(Thread.VirtualThreadScheduler)` API can be used
45 to set the scheduler when creating a virtual thread. The following example uses a thread
46 pool with 8 threads as the scheduler.
47 
48 ```
49 ExecutorService pool = Executors.newFixedThreadPool(8);
50 var scheduler = Thread.VirtualThreadScheduler.adapt(pool);
51 
52 Thread thread = Thread.ofVirtual().scheduler(scheduler).start(() -> { });
53 thread.join();
54 ```
55 
56 The prototype API allows different parts of a system to use different schedulers.
57 
58 Custom schedulers are _not inherited_. If a virtual thread assigned to a custom
59 scheduler invokes `Thread.startVirtualThread(Runnable)` then it starts a virtual
60 thread assigned to the default scheduler.
61 
62 Custom schedulers are _not closable_. They are managed by the garbage collector so that
63 a custom scheduler can be collected when all virtual thread assigned to the scheduler
64 have terminated and the scheduler is otherwise unreachable. The lifecycle of carrier
65 threads is managed by the scheduler.
66 
67 ## Poller modes
68 
69 Socket I/O in the context of virtual threads uses a platform specific I/O event notification
70 facility such as `kqueue`, `epoll`, or `io_uring`. The implementation uses of set of internal
71 _poller threads_ that consume events from the I/O event notification facility to unpark
72 virtual threads that are blocked in socket operations. The implementation has a number
73 of _poller modes_. On macOS and Windows there is a set of platform threads that wait for
74 events. On Linux, the poller threads that consume the events from the I/O event notification
75 facility are virtual threads assigned to the default scheduler.
76 
77 When using a custom scheduler it may be useful to use the poller mode that creates an I/O
78 event notification system and a poller virtual thread per carrier thread. The poller
79 thread terminates if the carrier terminates; any outstanding socket I/O operations
80 are moved to a different carrier's I/O event notification facility. This poller mode is
81 currently implemented on Linux and macOS and is used when running with `-Djdk.pollerMode=3`.