Skip to content

Commit 4732a16

Browse files
committed
perf(testing): share tokio runtime across fixture tests (#8567)
## Summary Each of the ~1,682 fixture tests creates a new `tokio::runtime::Runtime` via `Runtime::new().unwrap().block_on(...)`. This replaces that with a shared static runtime using `OnceLock<Runtime>`, eliminating repeated thread pool creation/teardown overhead. ## Results (`cargo test --workspace --exclude rolldown_binding`) | | Wall clock | User CPU | System CPU | |---|---|---|---| | **Baseline** (per-test runtime) | 33.5s | 133.5s | 65.2s | | **Shared runtime** | 25.3s | 116.1s | 43.0s | | **Improvement** | **-8.2s (24%)** | -17.4s | -22.2s | The shared runtime saves ~8 seconds (24% faster) wall-clock time, with a notable reduction in system CPU time (65s → 43s) from eliminating repeated thread pool creation/teardown. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 290a715 commit 4732a16

File tree

3 files changed

+24
-6
lines changed

3 files changed

+24
-6
lines changed

crates/rolldown_dev/src/dev_engine.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,14 @@ impl DevEngine {
362362
.map_err_to_unhandleable()
363363
.context("DevEngine: failed to send Close message to coordinator - coordinator may have already terminated")?;
364364

365-
// Close the bundler (calls `closeBundle` plugin hook)
366-
let mut bundler = self.bundler.lock().await;
367-
bundler.close().await?;
365+
// Close the bundler (calls `closeBundle` plugin hook).
366+
// The bundler lock MUST be released before waiting for the coordinator below.
367+
// Otherwise we'd deadlock: the coordinator's Close handler waits for any running
368+
// bundling task to finish, and that task may need to acquire the bundler lock.
369+
{
370+
let mut bundler = self.bundler.lock().await;
371+
bundler.close().await?;
372+
}
368373

369374
// Wait for coordinator to close (coordinator handles watcher cleanup)
370375
let coordinator_state = self.coordinator_state.lock().await;

crates/rolldown_testing/src/fixture.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ pub struct Fixture {
1616
fixture_path: PathBuf,
1717
}
1818

19+
static RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
20+
21+
fn shared_runtime() -> &'static tokio::runtime::Runtime {
22+
RUNTIME.get_or_init(|| tokio::runtime::Runtime::new().unwrap())
23+
}
24+
1925
// Using std once lock to store env variable
2026
static NEEDS_EXTENDED_TESTS: OnceLock<bool> = OnceLock::new();
2127
/// A function to get the API key.
@@ -36,11 +42,11 @@ impl Fixture {
3642
}
3743

3844
pub fn run_integration_test(self) {
39-
tokio::runtime::Runtime::new().unwrap().block_on(self.run_inner(vec![]));
45+
shared_runtime().block_on(self.run_inner(vec![]));
4046
}
4147

4248
pub fn run_integration_test_with_plugins(self, plugins: Vec<SharedPluginable>) {
43-
tokio::runtime::Runtime::new().unwrap().block_on(self.run_inner(plugins));
49+
shared_runtime().block_on(self.run_inner(plugins));
4450
}
4551

4652
async fn run_inner(self, plugins: Vec<SharedPluginable>) {

crates/rolldown_testing/src/integration_test.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,14 @@ impl IntegrationTest {
363363

364364
build_snapshot.initial_output = Some(initial_build_output);
365365
artifacts_snapshot.builds.push(build_snapshot);
366-
drop(dev_engine);
366+
// Explicitly close the dev engine to shut down the background coordinator task.
367+
// Without this, the coordinator task would persist across tests under the shared runtime.
368+
if let Err(err) = dev_engine.close().await {
369+
panic!(
370+
"Failed to close dev_engine for integration test in `{}` (title: `{debug_title}`): {err:#?}",
371+
test_folder_path.display()
372+
);
373+
}
367374
}
368375

369376
artifacts_snapshot

0 commit comments

Comments
 (0)