Follow-up: Replace state_mutex with xev.Async lock-free dispatch for Rust→Zig network callbacks
- Dominant language
- Zig
- Stars
- 97
- Forks
- 39
- PR merge metrics
- No merged PRs in 30d
Description
## Background
PR #695 introduced `EthLibp2p.state_mutex` as a minimal safe patch to fix a race condition (heap corruption) between the Rust networking thread and the main libxev event-loop thread. It works correctly and resolves the immediate crash.
However, the PR description itself acknowledges this is an intermediate fix:
> On "buffer writes and lock only the DB": That is exactly the `xev.Async` approach already mentioned in the PR description as the long-term fix. The Rust callback would enqueue an event into a lock-free channel, and the main libxev thread would dequeue and process it on its own tick, with no mutex at all. That is the right architectural solution. This PR is the minimal safe patch for an active crash; the proper redesign is a follow-up once the immediate regression is resolved.
## Proposed Architecture
Replace the mutex-based synchronisation with an `xev.Async`-based event dispatch model:
- Rust networking callbacks enqueue events into a **lock-free channel** (e.g. `std.atomic.Queue` or a ring buffer) instead of directly calling into shared Zig state
- The main **libxev event-loop thread** dequeues and processes these events on its own tick via `xev.Async`
- No mutex is needed — all shared state is only ever touched from the single libxev thread
## Benefits
- Eliminates lock contention between the Rust thread and the Zig event loop
- Cleaner architectural boundary between networking (Rust) and consensus logic (Zig)
- Better scalability — no blocking on either side
- Aligns with the libxev/async-IO design philosophy
## Acceptance Criteria
- [ ] Rust export fn callbacks enqueue events rather than directly accessing `BeamNode` / chain state
- [ ] `xev.Async` watcher is registered on the main libxev loop to drain the event queue
- [ ] `EthLibp2p.state_mutex` and `BeamNode.state_mutex` are removed
- [ ] Existing behaviour (block processing, fork choice, attestation handling) is preserved
- [ ] Unit tests pass without the mutex
## References
- PR #695 (introduced the mutex as interim fix)
- Issue #699 (original crash that triggered #695)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading PR #695 and the crash context in issue #699, then trace the Rust networking callbacks, EthLibp2p.state_mutex, and BeamNode.state_mutex. Design the xev.Async event-queue flow so callbacks enqueue events and the main libxev thread drains them. Done means both mutexes are removed, block processing, fork choice, and attestation handling remain correct, and unit tests pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, zig
- Domain
- distributed-systems, networking
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 28/100