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