timer: send_after / cancel_timer message-delivery substrate

Add a cancellable message-delivery timer on the existing timer.rs min-heap,
the substrate the gen_server time idioms (idle/receive timeout, periodic tick,
debounce/backoff) need.

- Reason::Send { fire }: a type-erased delivery thunk. send_after (Pid<A>, via
  send_to) and send_after_named (Name<M>, via send) capture dest+msg and
  resolve the address on fire, not at arm time, so a dead target / restarted
  name is observed when it fires; a failed resolve or closed inbox is dropped
  (Erlang erlang:send_after semantics).
- Cancellation via an "armed" set keyed on the entry seq, exposed as an opaque
  TimerId. Only Send timers use it; Sleep/WaitTimeout keep their inert-stale
  behaviour untouched. pop_due fires a Send only while still armed and removes
  it, so cancel returns true iff it landed before the fire (the race signal).
  cancel is unscoped: any holder of the id can cancel (e.g. racing two servers
  and cancelling the slow path).
- peek_deadline contract documented as "<= true next deadline" so a future
  hierarchical timing wheel can back Timers without touching send_after or the
  scheduler idle path. call_timeout left on recv_timeout (blast radius).

Tests: Timers-level (fire/cancel/race/clear/ordering) plus scheduler-level
delivery for both Name<M> and Pid<A>, cancel-prevents-delivery, and silent
drop on unresolved name / dead pid. Green on rq-mutex/rq-mpmc/rq-striped.
This commit is contained in:
smarm-agent
2026-06-18 14:28:54 +00:00
parent 5cb7f6a491
commit 61520bf2cc
6 changed files with 356 additions and 21 deletions
+8
View File
@@ -1232,6 +1232,14 @@ fn schedule_loop(inner: &Arc<RuntimeInner>, slot_idx: usize) {
// The callback may call unpark_at itself.
target.on_timeout(entry.pid, epoch);
}
// A `send_after` deadline: run the captured delivery thunk.
// It resolves the destination through the registry and
// sends now (a send can unpark a receiver) — same as any
// other in-loop unpark. The timers lock is already
// released; lock order Leaf -> Channel is preserved by the
// send itself. `pop_due` only returns still-armed Sends, so
// a cancelled one never reaches here.
crate::timer::Reason::Send { fire } => fire(),
}
}