Skip to content

fix: pair tokio runtime acquire and release on wasm to prevent premature shutdown#10363

Merged
graphite-app[bot] merged 1 commit into
mainfrom
07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown
Jul 22, 2026
Merged

fix: pair tokio runtime acquire and release on wasm to prevent premature shutdown#10363
graphite-app[bot] merged 1 commit into
mainfrom
07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown

Conversation

@shulaoda

@shulaoda shulaoda commented Jul 20, 2026

Copy link
Copy Markdown
Member

Description

Fixes #8411. Fixes #8747.

On the wasm target the binding keeps a refcount (ACTIVE_TASK_COUNT) over the shared tokio runtime, and the runtime is torn down when the count reaches zero. The count was seeded at 1 and released by RolldownBuild.close(), Watcher.close() and scan() without any matching acquire, while DevEngine never touched it at all. As a result, the first close() in the process (for example Vite bundling vite.config.ts or the dep optimizer) dropped the count to zero and shut the runtime down under every remaining consumer. The next spawn then panicked with Access tokio runtime failed in spawn, which surfaces on StackBlitz as RuntimeError: unreachable.

This PR makes the refcount strictly paired. Every JS object that outlives a single call and spawns onto the runtime now acquires on create and releases on close:

  • ACTIVE_TASK_COUNT is seeded at 0, so the runtime lives exactly as long as its holders.
  • RolldownBuild acquires in the constructor and releases in close(). The asyncRuntimeShutdown static flag and the conditional re-start in #build are removed since they only existed to work around the unbalanced count.
  • Watcher acquires in the constructor. It previously only released.
  • DevEngine acquires in create() and releases in close(). It previously did neither, which is the direct cause of [Panic]: when using bundledDev in stackblitz #8411.
  • scan() acquires before scanning and releases in cleanup(), which is now idempotent because the error path reaches it from both catch and finally.

Double release is also guarded. RolldownBuild.close() and DevEngine.close() claim the release before their first await, so a second or concurrent close() cannot release twice, and the release happens only after BindingBundler.close() settles because that call itself spawns onto the runtime.

All of this is a no-op on native targets, where startAsyncRuntime and shutdownAsyncRuntime compile to empty functions. binding.d.cts is regenerated from the updated doc comments.

shulaoda commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

How to use the Graphite Merge Queue

Add the label graphite: merge-when-ready to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit 77c8ff9
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/6a60674334edaf0008a1252f

@shulaoda
shulaoda force-pushed the 07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown branch from be5c42d to 8c5f077 Compare July 20, 2026 08:58
@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@rolldown/browser

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/browser@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/browser@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/debug

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/debug@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/debug@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

rolldown

pnpm add https://pkg.pr.new/rolldown/rolldown@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown@10363 -D
yarn add https://pkg.pr.new/rolldown/[email protected] -D

@rolldown/binding-android-arm64

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-android-arm64@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-android-arm64@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-darwin-arm64

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-arm64@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-darwin-x64

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-darwin-x64@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-freebsd-x64

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-freebsd-x64@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-arm-gnueabihf

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm-gnueabihf@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-arm64-gnu

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-gnu@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-arm64-musl

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-arm64-musl@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-ppc64-gnu

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-ppc64-gnu@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-ppc64-gnu@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-s390x-gnu

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-s390x-gnu@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-s390x-gnu@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-x64-gnu

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-gnu@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-linux-x64-musl

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-linux-x64-musl@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-openharmony-arm64

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-openharmony-arm64@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-openharmony-arm64@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-wasm32-wasi

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-wasm32-wasi@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-win32-arm64-msvc

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-arm64-msvc@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

@rolldown/binding-win32-x64-msvc

pnpm add https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@10363 -D
npm i https://pkg.pr.new/rolldown/rolldown/@rolldown/binding-win32-x64-msvc@10363 -D
yarn add https://pkg.pr.new/rolldown/rolldown/@rolldown/[email protected] -D

commit: 8c5f077

@shulaoda
shulaoda marked this pull request as ready for review July 20, 2026 09:43
@shulaoda
shulaoda requested review from h-a-n-a and sapphi-red July 20, 2026 09:43
@codspeed-hq

codspeed-hq Bot commented Jul 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 7 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing 07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown (6535e5d) with main (ce804c6)

Open in CodSpeed

Footnotes

  1. 10 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@shulaoda
shulaoda force-pushed the 07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown branch from 1821c51 to 6535e5d Compare July 22, 2026 02:15
@IWANABETHATGUY

Copy link
Copy Markdown
Member

One small thing on the close() methods: the release runs as a plain statement after the awaited close, so if this.#bundler.close() ever rejects, we skip shutdownAsyncRuntime() and leak the acquire. The count stays above zero, the runtime never gets torn down, and on wasm the park threads hang — basically the #8747 symptom in reverse.

It's not a regression (the old code released after the awaits too), but since the whole point here is to make acquire/release strictly paired, it'd be nice to actually guarantee the release fires. A try/finally does it without changing the ordering you documented:

async close(): Promise<void> {
  const shouldRelease = !this.#asyncRuntimeReleased;
  this.#asyncRuntimeReleased = true;
  try {
    await this.#stopWorkers?.();
    await this.#bundler.close();
    this.#stopWorkers = void 0;
  } finally {
    if (shouldRelease) shutdownAsyncRuntime();
  }
}

finally still runs after bundler.close() settles (whether it resolves or rejects), so the "release only after it settles" invariant your comment mentions still holds. Same thing applies to DevEngine.close() and the cleanup() in scan.

@hyfdev hyfdev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The fix correctly pairs every startAsyncRuntime() with a single shutdownAsyncRuntime() on all code paths. With the count now seeded at zero, the runtime lives exactly as long as its holders — no phantom initial owner, no premature shutdown, no lost consumers. Removing the static flag and conditional re-acquire is a clean side benefit.

Each consumer guards against double-release with a synchronous claim before the first await. The scan() cleanup is properly idempotent for the error path that reaches it from both catch and finally. All existing lifecycle tests pass.

@h-a-n-a

h-a-n-a commented Jul 22, 2026

Copy link
Copy Markdown
Member

Blocker: add a test case for FBM with WASI #8411

@shulaoda

Copy link
Copy Markdown
Member Author

Blocker: add a test case for FBM with WASI #8411

I will open a new PR in this stack

shulaoda commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

Merge activity

  • Jul 22, 6:44 AM UTC: The merge label 'graphite: merge-when-ready' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Jul 22, 6:45 AM UTC: shulaoda added this pull request to the Graphite merge queue.
  • Jul 22, 6:54 AM UTC: Merged by the Graphite merge queue.

…ure shutdown (#10363)

### Description

Fixes #8411. Fixes #8747.

On the wasm target the binding keeps a refcount (`ACTIVE_TASK_COUNT`) over the shared tokio runtime, and the runtime is torn down when the count reaches zero. The count was seeded at `1` and released by `RolldownBuild.close()`, `Watcher.close()` and `scan()` without any matching acquire, while `DevEngine` never touched it at all. As a result, the first `close()` in the process (for example Vite bundling `vite.config.ts` or the dep optimizer) dropped the count to zero and shut the runtime down under every remaining consumer. The next spawn then panicked with `Access tokio runtime failed in spawn`, which surfaces on StackBlitz as `RuntimeError: unreachable`.

This PR makes the refcount strictly paired. Every JS object that outlives a single call and spawns onto the runtime now acquires on create and releases on close:

- `ACTIVE_TASK_COUNT` is seeded at `0`, so the runtime lives exactly as long as its holders.
- `RolldownBuild` acquires in the constructor and releases in `close()`. The `asyncRuntimeShutdown` static flag and the conditional re-start in `#build` are removed since they only existed to work around the unbalanced count.
- `Watcher` acquires in the constructor. It previously only released.
- `DevEngine` acquires in `create()` and releases in `close()`. It previously did neither, which is the direct cause of #8411.
- `scan()` acquires before scanning and releases in `cleanup()`, which is now idempotent because the error path reaches it from both `catch` and `finally`.

Double release is also guarded. `RolldownBuild.close()` and `DevEngine.close()` claim the release before their first await, so a second or concurrent `close()` cannot release twice, and the release happens only after `BindingBundler.close()` settles because that call itself spawns onto the runtime.

All of this is a no-op on native targets, where `startAsyncRuntime` and `shutdownAsyncRuntime` compile to empty functions. `binding.d.cts` is regenerated from the updated doc comments.

<!--
- What is this PR solving? Write a clear and concise description.
- Reference the issues it solves (e.g. `fixes #123`).
- What other alternatives have you explored?
- Are there any parts you think require more attention from reviewers?

Also, please make sure you do the following:

- Read the Contributing Guidelines at https://rolldown.rs/contribution-guide/.
- Check that there isn't already a PR that solves the problem the same way. If you find a duplicate, please help us review it.
- Update the corresponding documentation if needed.
- Include relevant tests that fail without this PR but pass with it. If the tests are not included, explain why.

Thank you for contributing to Rolldown!
-->
@graphite-app
graphite-app Bot force-pushed the 07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown branch from 6535e5d to 77c8ff9 Compare July 22, 2026 06:46
@graphite-app
graphite-app Bot merged commit 77c8ff9 into main Jul 22, 2026
33 of 34 checks passed
@graphite-app
graphite-app Bot deleted the 07-20-fix_pair_tokio_runtime_acquire_and_release_on_wasm_to_prevent_premature_shutdown branch July 22, 2026 06:54
graphite-app Bot pushed a commit that referenced this pull request Jul 22, 2026
### Description

Follow-up to #10363, which must land first. Adds regression coverage for the `Access tokio runtime failed in spawn` crash class fixed there.

This bug class was invisible to every existing CI test. The runtime acquire and release functions are no-ops on native targets, and no CI test actually executed the WASI binding: the WASI workflow builds the wasm binding and runs the stability test and the examples, but those load the native binding that the same job builds for tooling. So an unbalanced runtime release could only ever crash on StackBlitz, never in CI.

The new `tests/wasi/runtime-lifecycle.mjs` is a plain node script following the `test:stability` pattern, wired into the WASI workflow right after the build step. It forces the real wasm binding and covers three sequences:

- The #8411 shape: a build that gets closed (Vite bundling `vite.config.ts`), then a `DevEngine` is created and run.
- The #8747 shape: a build that gets closed, then `watch()` runs until `BUNDLE_END`.
- The invariant behind both: closing one consumer while a `DevEngine` is still alive must not tear the runtime down under the engine.

Two details are load-bearing. `NAPI_RS_FORCE_WASI` is set to `error` inside the script before importing rolldown, because `true` silently falls back to the native binding when the wasi files are missing and the test would pass without testing anything. After the import the script also asserts via `require.cache` that `rolldown-binding.wasi.cjs` was really loaded.

### Verification

Red and green were produced under identical conditions (same machine, same stable toolchain, napi 3.11.0 on both sides, wasm binding forced and asserted). On `main` (`aa73a6848`, without #10363) the test exits 1 in the first sequence with the exact issue signature: `Access tokio runtime failed in spawn` in napi's `tokio_runtime.rs`, followed by `RuntimeError: unreachable`. On top of #10363 all three sequences pass.

To run locally: `pnpm build-wasi:debug` in `packages/rolldown` (dist only contains the wasi files when built with `TARGET=rolldown-wasi`), then `pnpm test:wasi-runtime` in `packages/rolldown/tests`.

<!--
- What is this PR solving? Write a clear and concise description.
- Reference the issues it solves (e.g. `fixes #123`).
- What other alternatives have you explored?
- Are there any parts you think require more attention from reviewers?

Also, please make sure you do the following:

- Read the Contributing Guidelines at https://rolldown.rs/contribution-guide/.
- Check that there isn't already a PR that solves the problem the same way. If you find a duplicate, please help us review it.
- Update the corresponding documentation if needed.
- Include relevant tests that fail without this PR but pass with it. If the tests are not included, explain why.

Thank you for contributing to Rolldown!
-->
graphite-app Bot pushed a commit that referenced this pull request Jul 22, 2026
### Description

Follow-up to #10363, addressing [this review comment](#10363 (comment)).

#10363 made the wasm tokio runtime refcount strictly paired: acquire on create, release on close. But every release still ran as a plain statement after the awaited close calls. If any of those awaits rejects, the release is skipped and the acquire leaks. The count then stays above zero, the runtime is never torn down, and on wasm the park threads hang forever, which is the #8747 symptom in reverse. This is not a regression, the old code had the same shape, but it leaves the pairing guaranteed only on the success path.

This PR wraps each release in `try/finally` so it fires no matter how the close settles:

- `RolldownBuild.close()`
- `DevEngine.close()`
- the idempotent `cleanup()` in `scan()`
- `Watcher.close()`, which the comment did not name but has the identical pattern

The documented ordering is unchanged. `finally` runs only after the awaited close settles, whether it resolves or rejects, so the release still happens strictly after `BindingBundler.close()` is done spawning onto the runtime. The claim-before-first-await guard against double release is also untouched, and the whole change remains a no-op on native targets, where `startAsyncRuntime` and `shutdownAsyncRuntime` compile to empty functions.

One pre-existing gap is deliberately left alone: when an early step rejects (for example `stopWorkers()`), the later close calls in the same method are still skipped. This PR only guarantees the runtime release. Making every cleanup step run independently is a separate concern.

<!--
- What is this PR solving? Write a clear and concise description.
- Reference the issues it solves (e.g. `fixes #123`).
- What other alternatives have you explored?
- Are there any parts you think require more attention from reviewers?

Also, please make sure you do the following:

- Read the Contributing Guidelines at https://rolldown.rs/contribution-guide/.
- Check that there isn't already a PR that solves the problem the same way. If you find a duplicate, please help us review it.
- Update the corresponding documentation if needed.
- Include relevant tests that fail without this PR but pass with it. If the tests are not included, explain why.

Thank you for contributing to Rolldown!
-->
Brooooooklyn added a commit that referenced this pull request Jul 22, 2026
Catches the branch up to main so CI can run again (the PR was CONFLICTING
against the advanced base) and pulls in the fixes that target this branch's
red dev-server tests:

  #10380  test(dev): expect preserved hot.data in hmr-whole-chain-dispose
          — Vite's rolldown-canary client now preserves import.meta.hot.data;
            this branch had the stale pre-#10380 assertion. Taken from main.
  #10363/#10381  tokio runtime acquire/release pairing + release-on-reject.

Conflicts resolved to this branch's side (--ours) — the branch's shared
tokio-free runtime supersedes main's tokio-refcount work:
  - dev-engine.ts / experimental.ts / rolldown-build.ts / watch/watcher.ts:
    main wraps shutdownAsyncRuntime() in try/finally (#10381). This branch
    already releases via the RuntimeLease abstraction inside a `finally`
    (release-on-reject guaranteed) plus AggregateError cleanup — a strict
    superset, so #10381 is subsumed. Verified main touched only the
    lease/startAsyncRuntime lines in these files.
  - crates/rolldown_binding/src/lib.rs + generated binding.d.cts: main adds
    the ACTIVE_TASK_COUNT tokio refcount to start/shutdown_async_runtime
    (#10363). This branch removed tokio, so those exports are compatibility
    no-ops (napi env lifecycle owns teardown; the legacy tokio-WASI path uses
    the old binding's own code). Verified main's changes here are entirely
    the refcount + its doc comments.

No lockfile/catalog conflicts: main touched no manifests in aa73a68..c07a55c.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T34fMrYCf2V13Mg8BKmnda
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Panic]: Access tokio runtime failed in spawn crash on StackBlitz when using watch mode [Panic]: when using bundledDev in stackblitz

4 participants