Skip to content

node, cli, database, metrics: thread process Io through instead of global_single_threaded - #977

Open
GrapeBaBa wants to merge 3 commits into
mainfrom
io-passthrough-main
Open

node, cli, database, metrics: thread process Io through instead of global_single_threaded#977
GrapeBaBa wants to merge 3 commits into
mainfrom
io-passthrough-main

Conversation

@GrapeBaBa

@GrapeBaBa GrapeBaBa commented Jun 5, 2026

Copy link
Copy Markdown
Member

What

Remove every production reference to std.Io.Threaded.global_single_threaded and route all of it through the process Io.

global_single_threaded is documented debug-only — "In general… library code should accept an Io parameter rather than accessing this declaration. Most code should avoid referencing this declaration entirely… This instance does not support concurrency or cancelation." It should not appear in production code regardless of whether the behavior happens to be equivalent.

Approach (two layers)

1. App/entrypoint components take an explicit io: std.Io (threaded from main's init.io, which already backs the ThreadPool):

  • ApiServer / MetricsServer (io field, used in run() / handleSSEEvents()).
  • database.Db.open / openBackend (+ warnIfOtherBackendPopulated).
  • BeamNode / Node.init (io field; wipeAndReopenDb; the lean node's own ThreadPool.init).
  • Checkpoint-sync HTTP (downloadCheckpointState / downloadAndStoreCheckpointBlock) — real outbound network.
  • metrics.init / api.init.

2. Leaf blocking primitives that can't practically take an io param use a single deliberate process Iopkgs/utils/src/process_io.zig, installed once from init.io as the first statement of mainInner (before any worker thread spawns; written-once-before-reads):

  • utils SyncMutex/RwLock, logger, sleepNs, fs helpers.
  • key-manager, api/event_broadcaster, network/ethlibp2p (debug dump), database rocksdb/lmdb backend open, node/locking connected-peers RNG.

The installed value is the real process Io (a worker-capable Threaded), not a single-threaded instance. A zeam-owned local fallback covers tests / pre-install bootstrap.

Result

grep -rn global_single_threaded pkgs/ shows no production references — only test blocks, setupTestPrimitives, and tools/spectest/stress retain it (their intended debug/test use).

Behavior

Identical. Every converted site does plain blocking I/O; the API/metrics servers remain serial accept-loops; the installed process Io performs the same kernel operations (the Threaded futex/blocking path is instance-independent). No concurrency/cancellation semantics were added.

Test plan

  • zig build green on main.
  • No functional change (blocking I/O semantics preserved).

GrapeBaBa added 3 commits June 5, 2026 16:48
…le_threaded

std.Io.Threaded.global_single_threaded is documented for debugging ("most code
should avoid referencing this declaration") and does not support concurrency or
cancelation. Thread the process Io (main's init.io, already used for the
ThreadPool) through to the entrypoint components instead of hardcoding the
debugging global:

- ApiServer / MetricsServer: take + store an io: std.Io, use it in run() /
  handleSSEEvents().
- database.Db.open / openBackend + warnIfOtherBackendPopulated: take io.
- BeamNode / Node.init: take + store io; use it in wipeAndReopenDb; the lean
  node's ThreadPool.init now uses the threaded io too (consistent with the
  beam-branch pool).

All call sites updated. Behavior is identical: every converted use is plain
blocking I/O and the API/metrics servers remain serial accept-loops on their
own threads. Test/debug code keeps the explicit global, which is its intended use.

NOT yet threaded (tracked follow-up, see commit body): the checkpoint-sync HTTP
client (downloadCheckpointState / checkpoint fetch — real outbound network where
timeouts/cancellation matter, needs new signatures on those free fns), the
DB-backend-internal open (rocksdb/lmdb), and the deeper library packages
(utils/fs, state-transition, network, metrics, key-manager, api/event_broadcaster)
which also reference the global.

Validated: zig build green.
…init

Continue replacing the debugging std.Io.Threaded.global_single_threaded with
the threaded process Io for the remaining HIGH-VALUE production sites:

- node.zig downloadCheckpointState / downloadAndStoreCheckpointBlock: take
  io: std.Io; the std.http.Client now uses the threaded Io (real outbound
  network — timeouts/cancellation belong on a proper Io). Threaded from the
  lean Node's io.
- metrics.init + api.init: take io: std.Io, threaded from main's init.io /
  the node's io. Test-only callers pass the global explicitly (retained).

Deliberately LEFT on a default Io (NOT threaded — pervasive blocking primitives;
threading to every call site is a whole-codebase cascade for ~zero benefit, as
these need neither concurrency nor cancellation):
- utils/sync SyncMutex, utils/log logger, utils/time sleepNs (16 callers),
  key-manager, api/event_broadcaster defaultIo() accessors.
Also left (low value / deep cascade): network/ethlibp2p deserialization-dump
dir (debug-only, would route through the gossip-handler layers) and
utils/fs readFileToEndAlloc -> loadFromYAMLFile (~8 sites + 3 CLI helpers).

Net: every production site where Io behavior actually matters (servers, DB,
node pool, checkpoint HTTP, metrics) now uses the threaded process Io; the
debugging global remains only behind deliberate blocking-primitive defaults and
test code.

Validated: zig build green.
…stalled process Io

`std.Io.Threaded.global_single_threaded` is documented debug-only — "most code
should avoid referencing this declaration entirely." The first two commits
threaded the process Io through the entrypoint components (servers, DB, node,
checkpoint HTTP, metrics). This removes the global from the remaining LEAF
sites that cannot practically take an `io` parameter.

Mechanism: a single deliberate process `Io` (`pkgs/utils/src/process_io.zig`),
installed once from `main`'s `init.io` as the first statement of `mainInner`
(before any worker threads spawn). The leaf primitives' `defaultIo()` helpers
and inline sites now call `process_io.get()`. The installed value is the real
process Io (a worker-capable `Threaded`), NOT a single-threaded instance; a
zeam-owned local fallback covers tests / pre-install bootstrap.

Sites repointed (all blocking primitives / leaf code):
- utils: `SyncMutex`/`RwLock` (`sync.zig` defaultIo), logger (`log.zig`),
  `sleepNs` (`time.zig`), fs helpers (`fs.zig` production paths).
- key-manager `defaultIo`, api/event_broadcaster `defaultIo`.
- network/ethlibp2p deserialization-dump dir.
- database rocksdb/lmdb backend `open`.
- node/locking connected-peers RNG (`std.Random.IoSource`).

After this, NO production code references `global_single_threaded` — only
`test` blocks, `setupTestPrimitives`, and `tools`/`spectest`/`stress` retain it
(their intended debug/test use).

Validated: zig build green.
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