Conversation
How to use the Graphite Merge QueueAdd 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. |
✅ Deploy Preview for rolldown-rs canceled.
|
70fa928 to
d49a834
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
a204c29 to
2c1f4c1
Compare
## [1.0.0-rc.18] - 2026-04-29 ### 💥 BREAKING CHANGES - optimization: default unspecified inlineConst.mode to smart (#9248) by @IWANABETHATGUY ### 🐛 Bug Fixes - rolldown_plugin_vite_import_glob: return error instead of panicking when virtual module uses a relative glob (#9241) by @shulaoda - binding: treat empty inlineConst object as omitted (#9247) by @IWANABETHATGUY - rolldown: keep enum declaration for optional-chain access (#9229) by @Dunqing - link_stage: restore inline let-else in exports-kind filter (#9237) by @IWANABETHATGUY - dev/lazy: avoid module reinitialization in lazy compilation patches (#9179) by @h-a-n-a - dev: visit identifier references for runtime rewrites in HMR finalizer (#9191) by @h-a-n-a - chunk-optimizer: pick dominator for runtime placement to avoid cycles (#9164) by @IWANABETHATGUY - make `this.emitFile` chunk path synchronous to avoid deadlock (#9031) by @lazarv - use sentinel id for `browser: false` ignored modules (#9192) by @shulaoda - prevent chunk optimizer from creating import cycles (#9228) by @IWANABETHATGUY ### 🚜 Refactor - replace tokio::sync::Mutex with std::sync::Mutex for non-IO data (#9176) by @shulaoda - rolldown_plugin_vite_import_glob: do not rewrite import path for absolute base (#9195) by @shulaoda - runtime_helper: wrap DependedRuntimeHelperMap in a struct (#9215) by @IWANABETHATGUY - drop redundant clear() in determine_safely_merge_cjs_ns (#9206) by @IWANABETHATGUY - clean up generate_lazy_export (#9208) by @IWANABETHATGUY - bitset: return bool from set_bit to fuse guard-and-set (#9207) by @IWANABETHATGUY - link_stage: simplify exports-kind filter and clarify safety comments (#9205) by @IWANABETHATGUY ### 📚 Documentation - determine_module_exports_kind (#9252) by @IWANABETHATGUY - fix dead link to esbuild ESM/CJS interop tests (#9230) by @Copilot - remove CSS bundling references (#9234) by @shulaoda - correct IncrementalFullBuild row in BundleMode table (#9214) by @IWANABETHATGUY - design: add bundler data lifecycle design doc (#9212) by @hyf0 - remove minifier alpha status notices (#9202) by @sapphi-red ### ⚙️ Miscellaneous Tasks - upgrade oxc to 0.128.0 (#9260) by @shulaoda - deps: bump rolldown-ariadne to 0.6.0 (#9254) by @IWANABETHATGUY - deps: update github actions (#9259) by @renovate[bot] - deps: update github actions (#9258) by @renovate[bot] - remove renovate overrides (#9257) by @Boshen - use ubuntu-latest for security workflow (#9256) by @Boshen - notify Discord around release publish (#9251) by @Boshen - add release environment to npm publish workflow (#9250) by @Boshen - justfile: drop the `--` separator before forwarded args in `vp run` (#9246) by @shulaoda - deps: update test262 submodule for tests (#9243) by @sapphi-red - add more tracing instrumentations (#9220) by @sapphi-red - rolldown_plugin_vite_import_glob: remove outdated sourcemap doc comment (#9213) by @shulaoda - update security workflow (#9201) by @Boshen ### ❤️ New Contributors * @lazarv made their first contribution in [#9031](#9031) Co-authored-by: shulaoda <165626830+shulaoda@users.noreply.github.com>

Summary
tokio::sync::Mutexwithstd::sync::Mutexfor two places that only guard plain data and never hold the lock across an.awaitpoint:PluginDriver.tx/NativePluginContextImpl.tx—Arc<Mutex<Option<UnboundedSender<ModuleLoaderMsg>>>>BundleCoordinator.watcher—Mutex<DynFsWatcher>PluginDriver::set_context_load_modules_txas a consequence, and drop the redundant.awaitat its two call sites inScanStage.Why
Per tokio's own guidance, the async mutex should be reserved for protecting IO resources where the lock genuinely needs to be held across
.await. For plain data the blockingstd::sync::Mutexis both cheaper and less prone to latent deadlocks when used through sync NAPI bindings. This continues the migration started in #9031 (which collapsed theFileEmitter.txpath to sync for theemit_chunkdeadlock fix).Scope of this change:
PluginDriver.txis only ever acquired to clone anOption<UnboundedSender>and to swap a new sender in at scan boundaries. The existing code inNativePluginContextImpl::loadalready drops the guard before awaitingsender.send(...); the only reason it usedtokio::sync::Mutexwas incidental. Switching tostd::sync::Mutexmakes that invariant syntactically enforced (no.awaitpossible while the guard is live) and letsset_context_load_modules_txshed its unnecessaryasyncmarker.BundleCoordinator.watcheris used exclusively insideupdate_watch_paths, where the guard is held only over synchronouspaths_mut.add(...)/paths_mut.commit()calls. No.awaitexists in that critical section, so async mutex is pure overhead here.No behavioral change for plugin authors or bundler consumers, the JS APIs and message flows are unchanged.
Refs
tokio::sync::Mutexwithstd::sync::Mutexwhere appropriate #9114 (tokio → std mutex migration)this.emitFilechunk path synchronous to avoid deadlock #9031 (sync-NAPI deadlock fix foremit_chunk; established thestd::sync::Mutexpattern for the tx slot onFileEmitter)