Skip to content

Refactor scheduler discovery and unify JVM argument handling#105

Merged
franz1981 merged 3 commits into
masterfrom
spi-scheduler-discovery
May 11, 2026
Merged

Refactor scheduler discovery and unify JVM argument handling#105
franz1981 merged 3 commits into
masterfrom
spi-scheduler-discovery

Conversation

@franz1981
Copy link
Copy Markdown
Owner

@franz1981 franz1981 commented May 7, 2026

This pull request introduces a new modular bootstrap layer for the Netty VirtualThread Scheduler, separating the system-classpath scheduler entrypoint from the actual Netty logic. It standardizes the JVM property for enabling the custom scheduler, updates documentation and usage throughout the project

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the virtual-thread scheduler integration to improve class-loader compatibility (notably for fat JAR / container scenarios) by introducing a JDK-only bootstrap scheduler that discovers the real implementation via ServiceLoader, and updates build/docs/bench tooling to use the new scheduler entrypoint and unified JVM-args handling.

Changes:

  • Added a new bootstrap module (io.netty.loom.spi) containing the JDK-loaded NettyScheduler and a NettySchedulerSpi interface discovered via ServiceLoader.
  • Moved the Netty-specific scheduling logic behind the SPI (NettySchedulerProviderImpl) and registered it via META-INF/services.
  • Updated documentation, tests, benchmarks, and the benchmark runner script to use io.netty.loom.spi.NettyScheduler and to consolidate JVM argument handling (--jvm-args / SERVER_JVM_ARGS).

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md Updates runtime flag docs to point to the new bootstrap scheduler class.
pom.xml Adds the new bootstrap module and updates Surefire argLine scheduler class.
example-echo/pom.xml Updates the scheduler implClass property to the new bootstrap entrypoint.
core/src/test/java/io/netty/loom/VirtualMultithreadIoEventLoopGroupTest.java Switches tests to use the bootstrap NettyScheduler API.
core/src/test/java/io/netty/loom/NettySchedulerClassInitTest.java Validates bootstrap scheduler availability via the new class.
core/src/test/java/io/netty/loom/ClassLoaderIsolationTest.java Adds a class-loader isolation regression test for SPI discovery.
core/src/main/resources/META-INF/services/io.netty.loom.spi.NettySchedulerSpi Registers the core provider implementation for ServiceLoader.
core/src/main/java/io/netty/loom/VirtualMultithreadIoEventLoopGroup.java Uses bootstrap scheduler for availability checks and updates error text.
core/src/main/java/io/netty/loom/NettySchedulerProviderImpl.java Converts the previous scheduler implementation into an SPI provider.
core/src/main/java/io/netty/loom/FifoEventLoopScheduler.java Uses NettyScheduler.instance() from the bootstrap module.
core/pom.xml Adds a dependency on the new bootstrap module.
bootstrap/src/main/java/io/netty/loom/spi/NettySchedulerSpi.java Introduces SPI contract for provider implementations.
bootstrap/src/main/java/io/netty/loom/spi/NettyScheduler.java Adds the JDK-loaded bootstrap scheduler that discovers the provider via ServiceLoader.
bootstrap/pom.xml Defines the new JDK-only bootstrap module.
benchmarks/src/main/java/io/netty/loom/benchmark/SchedulerHandoffBenchmark.java Updates benchmark forks to use the new scheduler implClass.
benchmarks/src/main/java/io/netty/loom/benchmark/NettySchedulerBenchmark.java Updates benchmark forks to use the new scheduler implClass.
benchmark-runner/scripts/run-benchmark.sh Unifies JVM arg handling and switches scheduler implClass to bootstrap.
benchmark-runner/README.md Updates benchmark runner configuration docs for the unified --jvm-args behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bootstrap/src/main/java/io/netty/loom/spi/NettyScheduler.java
Comment thread core/src/test/java/io/netty/loom/ClassLoaderIsolationTest.java Outdated
The JDK's VirtualThread.loadCustomScheduler() uses
ClassLoader.getSystemClassLoader(), which fails in Spring Boot fat
JARs and app servers where application classes live under a
non-standard class loader (e.g. LaunchedClassLoader).

Split NettyScheduler into a thin bootstrap module (io.netty.loom.spi)
with zero Netty dependencies and a core SPI implementation, following
the JDBC driver discovery pattern:

- bootstrap/ : NettyScheduler (JDK-loaded shim) + NettySchedulerSpi
  interface. Uses ServiceLoader with TCCL to discover the real
  implementation at runtime.
- core/ : NettySchedulerProviderImpl (actual scheduling logic),
  registered via META-INF/services.

The two modules use separate packages (io.netty.loom.spi vs
io.netty.loom) to avoid split-package problems under JPMS.

Includes ClassLoaderIsolationTest that reproduces the original
ClassNotFoundException and proves ServiceLoader-based discovery
works through a child-first class loader.
@franz1981 franz1981 force-pushed the spi-scheduler-discovery branch from 8fbd85a to fa3ea32 Compare May 7, 2026 08:21
…der in test

- validateNettyAvailability() now distinguishes between "bootstrap not
  installed" (missing -D flag) and "bootstrap installed but no SPI
  provider found" (core JAR not on TCCL, missing META-INF/services).
- ClassLoaderIsolationTest: use try-with-resources for URLClassLoader.
- ServiceConfigurationError in the constructor is intentionally not
  caught: silent fallback would mask real misconfigurations. Fail-fast
  matches the JDK's own loadCustomScheduler() behavior.
@franz1981
Copy link
Copy Markdown
Owner Author

@copilot review again?

Copy link
Copy Markdown
Contributor

Copilot AI commented May 7, 2026

@copilot review again?

Re-reviewed on f920455. The earlier review items I raised are addressed, and I don’t have any new code changes to request from this pass. I also rechecked validation: the latest branch CI run for f920455 is in progress, while the previous Loom Build Tests run passed; local Maven verification in this sandbox is blocked because it only has Java 17 but this repo builds against Java 27 preview.

@dreamlike-ocean
Copy link
Copy Markdown
Contributor

There are still some issues when using the custom Loom virtual thread scheduler from a Spring Boot executable jar.I created a branch with both the reproduction and the fix: https://github.com/dreamlike-ocean/Netty-VirtualThread-Scheduler/tree/spring_bug

Reproduction commit: 8356fbe
The generated Spring Boot fat jar places dependencies under BOOT-INF/lib:

example-echo-1.0-SNAPSHOT.jar
└── BOOT-INF/lib/
    ├── netty-virtualthread-core-1.0-SNAPSHOT.jar
    └── netty-virtualthread-bootstrap-1.0-SNAPSHOT.jar

So the scheduler class is physically in the fat jar, but not visible to the loader used by VirtualThread.loadCustomScheduler().

Fix commit: 8ad34ea

This commit places the thin bootstrap scheduler classes in the outer jar as multi-release entries:

example-echo-1.0-SNAPSHOT.jar
├── META-INF/MANIFEST.MF
├── META-INF/versions/27/io/netty/loom/spi/NettyScheduler.class
└── BOOT-INF/lib/
    └── netty-virtualthread-core-1.0-SNAPSHOT.jar

The manifest has:

Multi-Release: true

So Loom can load the bootstrap scheduler successfully, while the real implementation still stays in netty-virtualthread-core and is discovered later via ServiceLoader.

…and application server environments

- Add instructions for configuring classloaders in Spring Boot, OpenLiberty, WildFly, and other servers.
- Document MRJAR-based solution for fat JAR deployments.
- Credit contributor for identifying and fixing related issues.
@franz1981 franz1981 merged commit 9a2428d into master May 11, 2026
4 checks passed
@franz1981
Copy link
Copy Markdown
Owner Author

Thanks @dreamlike-ocean I have added a doc section to explain how to handle it. Now waiting for OpenLiberty maintainers as well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants