Skip to content

p2p: read dial scheduler bookkeeping under the lock - #1026

Open
hyunsooda wants to merge 1 commit into
kaiachain:devfrom
hyunsooda:fix/dialsched-candidate-snapshot
Open

p2p: read dial scheduler bookkeeping under the lock#1026
hyunsooda wants to merge 1 commit into
kaiachain:devfrom
hyunsooda:fix/dialsched-candidate-snapshot

Conversation

@hyunsooda

@hyunsooda hyunsooda commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Problem

  • getCandidates releases ds.mu after copying the static nodes, then keeps reading connectedAll, static, connectedOutbound and dialing, which are written from the handshake, peer and dial goroutines.

Fix

  • Take those counts while the lock is still held. The lock is not extended over the rest of the function, because the dynamic candidate path acquires it again.

Types of changes

  • 🐛 Bug fix
  • ✨ Non-hardfork changes (node upgrade not required)
  • 💥 Hardfork / consensus-breaking changes
  • 🧪 Test improvements
  • 🧰 CI / build tool
  • ♻️ Chore / Refactor / Non-functional changes

Checklist

  • 📖 I have read the CONTRIBUTING GUIDELINES doc
  • 📝 I have signed in the PR comment I have read the CLA Document and I hereby sign the CLA in first time contribute after having read CLA
  • 🟢 Lint and unit tests pass locally with my changes ($ make test)

Related issues

Further comments

getCandidates releases ds.mu after copying the static nodes, then keeps reading
connectedAll, static, connectedOutbound and dialing. Those sets have no internal
synchronisation and are written from the handshake, peer and dial goroutines, so
the reads race with ordinary peer churn. The logging reads happen at any log
level, because the arguments are evaluated before the call.

Take the four counts while the lock is still held and use the local values
afterwards. The lock cannot simply be extended over the rest of the function:
the dynamic candidate path acquires it again, and sync.RWMutex is not reentrant.

Constraint: the lock must be released before the dynamic candidate lookup
Rejected: hold ds.mu for the whole function | dynamicCandidateAllowed re-acquires
  it, which deadlocks once a writer is queued
Rejected: give typedNodeSet its own mutex | doubles the locking and breaks
  reading several sets as one consistent snapshot
Confidence: high
Scope-risk: narrow
Not-tested: no dedicated regression test; TestServerDial catches a
  reintroduction under -race in roughly half of its runs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hyunsooda hyunsooda self-assigned this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant