Skip to content

Use time-paused tokio for zero sleeps#309

Open
carlaKC wants to merge 4 commits into
bitcoin-dev-project:mainfrom
carlaKC:des-tokio
Open

Use time-paused tokio for zero sleeps#309
carlaKC wants to merge 4 commits into
bitcoin-dev-project:mainfrom
carlaKC:des-tokio

Conversation

@carlaKC

@carlaKC carlaKC commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Our current solution to waits is to use a sped up wall clock, which has all sorts of limitations.

Using a paused, single-threaded tokio runtime we can get discrete-event-like functionality without doing a large refactor.

carlaKC added a commit to carlaKC/jam-ln that referenced this pull request Jun 23, 2026
Switch from sim-ln's old wall-clock SimulationClock (a fixed u16 speedup that
still slept on real time) to its reimplemented SimulationClock, which advances
virtual time on a paused single-threaded runtime. A run now completes as fast as
the CPU allows instead of sleeping on real time, and is reproducible for a fixed
seed.

Tracks bitcoin-dev-project/sim-ln#309.

- Pin simln-lib/sim-cli to the PR head.
- Drive all three binaries through runtime::block_on_sped_up_clock, which owns
  the paused runtime; main() is therefore synchronous.
- Reimplement the local InstantClock trait on tokio::time::Instant so that
  elapsed time tracks virtual time (no ln-resource-mgr changes required).
- Seed the latency interceptor from a shared SIM_SEED, matching sim-ln's seeded
  RNG.
- Remove the obsolete --clock-speedup multiplier flag; SimulationClock no longer
  takes one.
- Bound the run at one virtual year as a safeguard, since virtual time has no
  wall-clock limit if an attack never triggers shutdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@carlaKC carlaKC force-pushed the des-tokio branch 4 times, most recently from 7e4eb94 to bd2a169 Compare June 25, 2026 20:08
carlaKC and others added 3 commits June 25, 2026 16:09
Replace the wall-time multiplier SimulationClock (a u16 speedup capped at
1000x) and the separate SystemClock with a single SimulationClock that
reports time relative to a SystemTime start anchor. now() is the start time
plus a captured tokio::time::Instant's elapsed(), and sleep() defers to the
tokio timer.

How the speedup works: when this clock is driven on a paused tokio runtime
(start_paused), the runtime freezes the Instant clock and, once no task is
runnable, auto-advances it to the nearest pending timer. So sleep() resolves
with no real time elapsed and now() jumps straight to the next scheduled
event, letting a simulation run as fast as the CPU allows while staying
reproducible. On a regular (non-paused) runtime the exact same clock simply
tracks real time relative to its start, which is how real-node runs use it.
The paused runtime needs tokio's test-util at runtime, so it is gated behind
the new opt-in `virtual-time` feature rather than always compiled in.

Tests construct the clock with SystemTime::now() (live time) rather than a
fixed epoch: LDK's network-graph gossip validation checks channel_update
timestamps against the real wall clock and rejects anything older than two
weeks, so a hardcoded past start time makes populate_network_graph fail.

This commit only lands the clock itself. The runtime helper that drives it on
a paused runtime and the CLI opt-in are removed here for the sake of atomic
commits and reintroduced in later commits; the old --speedup-clock flag and
its validation are dropped accordingly, so for now every run tracks real
time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Running a simulation on virtual time needs a single-threaded, time-paused
runtime: single threaded so scheduling is deterministic, paused so virtual time
auto-advances to the next timer instead of waiting on the wall clock. Leaving
callers to set this up invites silently losing determinism or the virtual-time
advance.

Add runtime::block_on_virtual_time, which owns that runtime, constructs a
SimulationClock on it, and runs a caller-provided build-and-run closure to
completion. It rejects being called from within an existing runtime (returning
the new SimulationError::RuntimeError) rather than panicking on a nested
block_on. The module is gated behind the virtual-time feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nothing in the parse_sim_params call stack actually awaits: it only
does blocking std::fs reads and a blocking dialoguer prompt. Drop the
vestigial async/await from parse_sim_params, read_sim_path and
select_sim_file.

This is preparation for the following commit, which runs the
simulation on a paused virtual-time runtime. There, main() becomes a
plain sync fn that picks a runtime only after parsing, so parsing must
not require an async context of its own.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@carlaKC carlaKC marked this pull request as ready for review June 25, 2026 20:16
carlaKC added a commit to carlaKC/jam-ln that referenced this pull request Jun 25, 2026
Switch from sim-ln's old wall-clock SimulationClock (a fixed u16 speedup that
still slept on real time) to its reimplemented SimulationClock, which advances
virtual time on a paused single-threaded runtime. A run now completes as fast as
the CPU allows instead of sleeping on real time, and is reproducible for a fixed
seed.

Tracks bitcoin-dev-project/sim-ln#309.

- Pin simln-lib/sim-cli to the PR head, enabling simln-lib's virtual-time
  feature (required for runtime::block_on_virtual_time, which pulls in tokio's
  test-util to drive the paused runtime).
- Drive all three binaries through runtime::block_on_virtual_time, which owns
  the paused runtime; main() is therefore synchronous.
- Reimplement the local InstantClock trait on tokio::time::Instant so that
  elapsed time tracks virtual time (no ln-resource-mgr changes required).
- Seed the simulation config and latency interceptor from a shared SIM_SEED so
  that payment generation and sampled delays are reproducible.
- Remove the obsolete --clock-speedup multiplier flag; SimulationClock no longer
  takes one.
- Bound the run at one virtual year as a safeguard, since virtual time has no
  wall-clock limit if an attack never triggers shutdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add --virtual-time, an opt-in flag for simulated networks. When set, the
simulation runs on the library's paused single-threaded runtime, so virtual time
advances instantly to the next event: a multi-day run finishes in seconds and is
reproducible for a given seed. When absent, the simulation runs on a regular
multi-threaded runtime in real time, as before.

main parses and validates the simulation params once up front, then selects the
runtime by the flag: virtual-time runs go through block_on_virtual_time (which
owns the paused runtime and hands back a SimulationClock); regular runs use a
standard runtime. The flag is validated to require a simulated network and
--total-time (an unbounded run would otherwise advance virtual time forever).
sim-cli enables simln-lib's virtual-time feature so the flag is always available.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@carlaKC carlaKC changed the title [WIP]: use time-paused tokio for zero sleeps Use time-paused tokio for zero sleeps Jun 25, 2026
@carlaKC carlaKC requested a review from elnosh June 26, 2026 13:35
carlaKC added a commit to carlaKC/jam-ln that referenced this pull request Jun 26, 2026
Move off sim-ln's old wall-clock SimulationClock (a fixed speedup multiplier
that still slept on real time) onto its reimplemented virtual-time
SimulationClock, which advances time on a paused single-threaded runtime so a
run completes as fast as the CPU allows.

- Pin simln-lib/sim-cli to the virtual-time PR head and enable simln-lib's
  virtual-time feature, which provides runtime::block_on_virtual_time.
- Drive all three binaries through block_on_virtual_time, which owns the paused
  runtime and hands the simulation its clock; main() is therefore synchronous.
- Adopt the new SimulationClock::new(start_time) constructor in place of the
  speedup-multiplier form across the binaries, tests and graph setup.
- Reimplement the local InstantClock trait on tokio::time::Instant so that
  elapsed time tracks virtual time.
- Pass None to the latency interceptor's new seed parameter for now; seeding
  follows in a later commit.

Tracks bitcoin-dev-project/sim-ln#309.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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