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