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:
+15
-8
@@ -95,10 +95,17 @@ and the full 013/014 history in git.
|
||||
Now unblocked (RFC 013/014 shipped: a timer's `Dest` resolves to a `Pid<A>` /
|
||||
`Name<M>` *on fire*, not a bespoke channel/closure). Two layers:
|
||||
|
||||
**Substrate — `send_after` / `cancel_timer`.** Message-delivery timer on the
|
||||
existing min-heap (`timer.rs`): deliver a value to an address at a deadline,
|
||||
cancellable. Small, and the thing the gen_server idioms below have no clean
|
||||
expression without today.
|
||||
**Substrate — `send_after` / `cancel_timer`.** ✅ SHIPPED — message-delivery
|
||||
timer on the existing `timer.rs` min-heap: deliver a value to an address
|
||||
(`Pid<A>` via `send_to`, `Name<M>` via `send`), resolved *on fire* so a dead
|
||||
target / restarted name is observed at fire time; failed resolve dropped
|
||||
(Erlang `erlang:send_after`). A new `Reason::Send { fire }` thunk carries
|
||||
delivery type-erased; cancellation is an `armed` set keyed on the entry `seq`
|
||||
(only `Send` timers use it — `Sleep`/`WaitTimeout` keep inert-stale), exposed
|
||||
as an opaque `TimerId`. `cancel` is unscoped (any holder of the id) and returns
|
||||
whether it landed before the fire (the race signal). `peek_deadline`'s contract
|
||||
relaxed to "≤ true next deadline" so a future hierarchical timing wheel can back
|
||||
`Timers` without touching `send_after` or the scheduler idle path.
|
||||
|
||||
**The gen_server patterns themselves** (the headline). The OTP time vocabulary,
|
||||
expressed against the v0.8 server loop and its `select` arm priority:
|
||||
@@ -111,10 +118,10 @@ expressed against the v0.8 server loop and its `select` arm priority:
|
||||
exponential re-arm on failure.
|
||||
|
||||
Call deadlines already exist in part (`CallTimeoutError`); fold them in rather
|
||||
than reinvent. Needs an RFC: the timer-arming surface on `ServerCtx`, the
|
||||
timeout-directive shape, and how a fired timer interacts with `handle_info` /
|
||||
arm priority. Land the `send_after` substrate first; `gen_statem` state timeouts
|
||||
(Low priority) then sit on the same mechanism.
|
||||
than reinvent. The `send_after` substrate is now landed (above); `gen_statem`
|
||||
state timeouts (Low priority) sit on the same mechanism. Still needs an RFC: the
|
||||
timer-arming surface on `ServerCtx`, the timeout-directive shape, and how a
|
||||
fired timer interacts with `handle_info` / arm priority.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user