roadmap: gen_server time patterns shipped (RFC 015); substrate status recorded
This commit is contained in:
+40
-31
@@ -90,38 +90,39 @@ and the full 013/014 history in git.
|
||||
|
||||
---
|
||||
|
||||
## Up next — gen_server time-related patterns
|
||||
## gen_server time-related patterns ✅ SHIPPED (RFC 015)
|
||||
*(layer 2; seven commits `47d75d1`→`f454a91`, each a reviewable chunk. Builds on
|
||||
the `send_after` substrate below.)*
|
||||
|
||||
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:
|
||||
The OTP time vocabulary against the v0.8 server loop, with **no handler-signature
|
||||
change** — the capability is a handle stashed on `self` (the `Watcher` pattern),
|
||||
not a return-directive or a `&ctx` threaded through handlers. New trait surface:
|
||||
`type Timer` (server's own scheduled payload, `()` if unused, kept distinct from
|
||||
the external `type Info`), `handle_timer`, `handle_idle` (both no-op defaults).
|
||||
|
||||
**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.
|
||||
- **One-shot / debounce / retry-backoff** — `ctx.timer()` hands out a clonable
|
||||
`TimerHandle`; `arm_after(d, msg)` arms, `cancel(id)` carries the substrate's
|
||||
race bool. Debounce/backoff are just arm-and-`cancel` against the latest event
|
||||
(no special mechanism).
|
||||
- **Periodic tick / heartbeat** — `tick_every(d, msg)`: loop-managed sugar over
|
||||
the one-shot substrate (re-arm at `now + d`), one stable id, `cancel` stops the
|
||||
re-arm *and* the pending instance. Requires `Timer: Clone` (method-level bound
|
||||
only); the loop re-delivers via a stored factory, so the bound never leaks onto
|
||||
`type Timer` or the loop.
|
||||
- **Idle / receive timeout** — *not* a channel: it is the timeout on the loop's
|
||||
`select_timeout` / `recv_timeout`. `ctx.idle_after(d)` (set once in `init`)
|
||||
fixes the window; reset on any dispatched message; `None` from the wait ⇒
|
||||
`handle_idle`; re-arms steady. No generation-tag race — there is no token.
|
||||
|
||||
**The gen_server patterns themselves** (the headline). The OTP time vocabulary,
|
||||
expressed against the v0.8 server loop and its `select` arm priority:
|
||||
- **Idle / receive timeout** — a callback directive that arms a one-shot so the
|
||||
server gets a `timeout` if its inbox stays quiet (session expiry, idle
|
||||
shutdown). Folds into the existing arm-priority ordering.
|
||||
- **Periodic tick / heartbeat** — a `ServerCtx` timer arm that re-arms, for
|
||||
liveness probes and metrics flushes.
|
||||
- **Debounce / retry backoff** — arm-and-cancel against the latest event;
|
||||
exponential re-arm on failure.
|
||||
|
||||
Call deadlines already exist in part (`CallTimeoutError`); fold them in rather
|
||||
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.
|
||||
Mechanics: control (monitor intake) + armed timers fold into one loop-internal
|
||||
`Sys` channel selected above the inbox, so **armed timers outrank infos** (a
|
||||
heartbeat can't be starved). One substrate addition — `send_after_to` (a
|
||||
channel-targeting sibling of `send_after`, lands the fire on the loop's own arm).
|
||||
Exit is leak-free: the drop guard (same one that runs `terminate`) drains and
|
||||
cancels every live timer id, then `debug_assert!`s none survive. `call_timeout`
|
||||
is unchanged — it's a *client-side* call deadline, disambiguated in docs from the
|
||||
server-side idle timeout (same word, two axes). `gen_statem` state timeouts
|
||||
(deferred, Low) will reuse the loop-owned idle deadline.
|
||||
|
||||
---
|
||||
|
||||
@@ -151,8 +152,16 @@ profiling spike (where do the cycles actually go per park/unpark round-trip)
|
||||
and then an RFC before it can be scheduled.
|
||||
|
||||
#### send_after / cancel_timer
|
||||
Promoted to **Up next** (gen_server time-related patterns) — it's the substrate
|
||||
that layer lands on, now unblocked by RFC 013/014.
|
||||
✅ SHIPPED (`61520bf`) — message-delivery timer on the `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`). `Reason::Send { fire }`
|
||||
carries delivery type-erased; cancellation is an `armed` set keyed on entry
|
||||
`seq` (only `Send` uses it — `Sleep`/`WaitTimeout` stay inert-stale), exposed as
|
||||
an opaque `TimerId`; `cancel` is unscoped and returns the race signal.
|
||||
`peek_deadline` relaxed to "≤ true next deadline" for a future timing wheel. The
|
||||
gen_server time layer (RFC 015, shipped above) lands on this, adding only the
|
||||
channel-targeting `send_after_to` sibling.
|
||||
|
||||
#### Introspection — process_info / get_state / tree dump
|
||||
`trace.rs` is the seed. What an actor is parked on, queue depth, stack size; a
|
||||
|
||||
Reference in New Issue
Block a user