Fix DashMap read-write self-deadlock in task_cache causing hangs#92210
Merged
Conversation
The `task_cache.get()` call returns a `dashmap::Ref` that holds a read lock on a DashMap shard. The `Ref` was kept alive until the end of the `if let` block, which includes `ConnectChildOperation::run`. That operation can re-enter `task_cache` with a write lock via `prepare_tasks_with_callback` -> `task_cache.entry()`. When the write targets the same shard that's already read-locked by the current thread, the thread self-deadlocks (dashmap's RwLock is not reentrant). Once one thread deadlocks, all other threads needing that shard block too, causing a full system hang during incremental builds with persistent caching. Fix: use `.map(|r| *r)` to copy the TaskId and drop the Ref (releasing the read lock) before calling ConnectChildOperation::run. Co-Authored-By: Claude <noreply@anthropic.com>
Merging this PR will improve performance by 3.19%
Performance Changes
Comparing Footnotes
|
Contributor
Tests Passed |
Contributor
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URL |
wbinnssmith
pushed a commit
that referenced
this pull request
Apr 2, 2026
lukesandberg
pushed a commit
that referenced
this pull request
Apr 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
Fixes a deadlock in the Turbopack backend's
task_cacheDashMap that causesnextto hang during incremental builds with persistent caching.Why?
In
get_or_create_persistent_taskandget_or_create_transient_task,self.task_cache.get(&task_type)returns adashmap::Refthat holds a read lock on a DashMap shard. Although theTaskIdwas copied out withlet task_id = *task_id, the originalRefwas not dropped until the end of theif letblock — which includes the call toConnectChildOperation::run.ConnectChildOperation::runcan triggerAggregationUpdateQueue::process→for_each_task_all→prepare_tasks_with_callback, which callstask_cache.entry(task_type).or_insert(task_id)— requiring a write lock on atask_cacheshard. When this targets the same shard that the current thread already holds a read lock on, the thread self-deadlocks because dashmap'sRawRwLockis not reentrant.Once one thread self-deadlocks, all other tokio worker threads needing that shard pile up behind it, eventually causing a complete system hang where every worker is stuck in
dashmap::lock::RawRwLock::lock_exclusive_slow.This was observed as a hang during the 7th incremental build with persistent caching of a large application, with a macOS
sampletrace showing all tokio-runtime-worker threads blocked on_pthread_cond_waitinsidelock_exclusive_slowon theDashMap<Arc<CachedTaskType>, TaskId>.How?
Use
.map(|r| *r)to copy theTaskIdout and drop theRef(releasing the read lock) before callingConnectChildOperation::run:Applied to both
get_or_create_persistent_taskandget_or_create_transient_task.