miner: avoid recursive worker read lock - #2352
Conversation
Use a lock-held gas-limit helper from makeHeader so a queued writer cannot block a recursive RLock while the caller still holds the worker read lock. Add a deterministic queued-writer regression test.
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
|
codegenie review |
🧞 Codegenie Review✅ No credible findings. CoverageReviewed 4/4 hunks. Stats
✅ No FindingsNo credible findings were found. Everything looks good. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a miner deadlock caused by recursively acquiring worker.mu’s read lock during header construction while a writer was queued (Go’s writer-preferring RWMutex can block nested readers once a writer is waiting). The change makes header construction use a lock-free (but lock-required) gas-limit helper, and adds a deterministic regression test to ensure header creation completes even with a queued writer while the read lock is held.
Changes:
- Split dynamic gas-limit calculation into a lock-taking wrapper and a
w.mu-already-held helper, and use the locked helper frommakeHeader. - Document
makeHeader’s locking contract (caller must holdw.mufor reading) to avoid reintroducing recursive lock acquisition. - Add
TestMakeHeaderWithQueuedWriterto reproduce and guard against the deadlock scenario under-race.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| miner/worker.go | Avoids recursive RWMutex read locking by introducing calculateDesiredGasLimitLocked and using it from makeHeader under an existing RLock. |
| miner/worker_test.go | Adds a regression test that queues a writer and verifies makeHeader completes while the read lock remains held. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #2352 +/- ##
===========================================
+ Coverage 54.18% 54.24% +0.05%
===========================================
Files 908 908
Lines 162242 162243 +1
===========================================
+ Hits 87909 88002 +93
+ Misses 68891 68810 -81
+ Partials 5442 5431 -11
... and 26 files with indirect coverage changes
🚀 New features to boost your workflow:
|
Summary
Fixes the miner timeout in nightly race run 31292744499, where
TestPrefetchRaceWithSetExtraran until the package's 15-minute timeout.This was a real Bor block-production concurrency bug exposed by the test, not a flaky assertion, CI instability, or an external dependency failure.
prepareWorkandrunPrefetcherholdworker.mufor reading while callingmakeHeader. The dynamic gas-limit helper attempted to acquire the same read lock again. WhenSetExtraqueued for the write lock, Go's writer-preferringRWMutexblocked the nested reader; the writer could not proceed until the outer reader released, creating a deadlock.The fix keeps the lock-taking gas-limit helper for standalone callers and adds a lock-held helper for
makeHeader. A deterministic regression test queues a writer, confirms it is waiting, and verifies that header construction completes while its caller retains the read lock.Executed tests
TestMakeHeaderWithQueuedWriterfailed with the expected blocked-header assertion.go test -race ./miner -run '^TestMakeHeaderWithQueuedWriter$' -count=20go test -race ./miner -run '^TestPrefetchRaceWithSetExtra$' -shuffle=1786249703675931145 -count=5go test -race ./miner -run '^TestCalculateDesiredGasLimit' -count=10go test -race ./miner -shuffle=1786249703675931145 -count=1 -timeout=15mgo vet ./minermake lint(0 issues)gosec ./miner/...found no issue on changed lines; it exited non-zero for 22 pre-existing findings elsewhere inminer.Rollout notes
Backward-compatible locking fix. It does not change gas-limit calculation, header contents, consensus rules, configuration, or operator behavior.
The current
developdependency set has an unrelatedgovulncheckfailure forGO-2026-6061ingoogle.golang.org/grpc v1.79.3; recent nightly govulncheck runs already fail on the same base SHA. That dependency update remains separate from this PR.Remaining risk is limited to future call paths invoking
makeHeaderwithout holdingworker.mu; all current production and test call sites were audited and hold the read lock. CI unit, integration, and both e2e variants passed.