Days
Architectural Design

Time & Concurrency

Why packets carry time, and how to observe runtime concurrency.

Days is designed around many concurrent actors (sources, sinks, schedulers, switches). Two design choices are especially important for performance and correctness:

  1. Time is carried in messages (packets/frames), and most models maintain a local time: f64.
  2. Concurrency is optionally observable via tracing hooks and a small runtime sampler.

Time propagation via Packet.time

Most message handlers treat Packet.time as “now”:

  • sources create packets with Packet::new(..., creation_time) and set packet.time to the current simulated time,
  • schedulers compute serialization delay and update the packet with packet.departure_update(now + timeout),
  • sinks record statistics at the packet’s arrival time.

This reduces pressure on cx.time(), which can be a contention point when many actors run concurrently.

When built with the test feature, many handlers assert:

  • packet.time matches cx.time() within a small epsilon,
  • local time does not move backwards.

These checks validate that “time-in-message” is consistent with the simulation engine.

Local time fields

Core models keep a local time: f64:

  • PacketSwitch.time (src/switches/switch.rs)
  • Port.time, DRRServer.time, etc. (src/schedulers/)
  • endpoint time in sources/sinks (src/flows/)
  • link-layer time in Link/PFC components (src/l2/)

The local time is updated from the incoming message (or from cx.time() only in limited cases).

Concurrency tracing

Days supports two complementary mechanisms:

  1. A tracing layer (ConcurrencyTrackerLayer, src/utils/tracing.rs) increments/decrements a global ACTIVE_TASKS counter on span enter/exit.
  2. A wall-clock sampler (start_wall_clock_concurrency_sampler, src/utils/tracing.rs) samples ACTIVE_TASKS during Simulation::step_until and logs a wall-clock average concurrency (plus the peak observed by the layer).

Enable it via config:

tracing_active = true
tracing_interval = 1.0

This is intended as a lightweight way to correlate workload shape with runtime behavior.

On this page