feat(serve): registry names + docs (v0.2 chunk 4)

Registration:
- Each listener registers "urus.listener.{i}" from inside its ChildSpec
  factory. On a restart the stale binding points at a dead pid; smarm's
  registry evicts those lazily on register/whereis, so re-registering
  the same name is safe. Result ignored — a registry hiccup must not
  take the listener down.
- "urus.server" -> supervisor pid, registered via the JoinHandle's
  .pid() immediately after spawn rather than inside the closure: the
  binding exists before the supervisor runs an instruction, so an early
  whereis can't observe None.

Test: server_and_listeners_are_registered probes whereis from a route
handler — whereis must run inside the runtime, and the test thread is a
foreign OS thread with no runtime in its TLS.

Docs:
- README: Config documented in full (timeout semantics: keep_alive =
  idle-before-first-byte, request = Instant deadline first-byte ->
  head+body, slowloris-proof), Handle/shutdown_handle/serve_with_shutdown
  section with the 4-step shutdown sequence, named-actors note, test
  coverage list refreshed, Roadmap section now points at ROADMAP.md.
- ROADMAP: v0.2 chunks 1-4 marked landed; design detail stays in the
  chunk commit bodies.

Verified: cargo build --features smarm-trace clean; full suite (32
tests) green 3x; debug-grep clean.

This closes v0.2. Exit criteria all verified across chunks 1-3: crud
drains on stdin-Enter (~100ms), idle keep-alive reaped at
keep_alive_timeout, panicking listener restarts under load.
This commit is contained in:
Claude
2026-06-11 22:24:02 +00:00
parent a60687bfec
commit 8065ea561e
4 changed files with 115 additions and 17 deletions
+14
View File
@@ -312,6 +312,15 @@ pub fn serve_with_shutdown(
let sf = shutdown_flag.clone();
sup = sup.child(ChildSpec::new(Restart::Transient, move || {
println!("urus: listener {} starting", i);
// Named for whereis-style introspection. On a restart the
// old binding points at a dead pid; smarm's registry
// evicts stale bindings lazily, so re-registering the
// same name is fine. Ignore the result — a registry
// hiccup must not take the listener down.
let _ = smarm::register(
format!("urus.listener.{i}"),
smarm::self_pid(),
);
listener_loop(lfd.clone(), p.clone(), limits, r.clone(), sf.clone());
}));
}
@@ -319,6 +328,11 @@ pub fn serve_with_shutdown(
// faster than that trips the cap and tears the pool down — loud
// failure over a zombie server.
let sup_h = smarm::spawn(move || sup.run());
// Register via the JoinHandle's pid rather than inside the
// closure: the binding exists before the supervisor body runs a
// single instruction, so an early `whereis("urus.server")` can't
// race a None. Result ignored for the same reason as listeners.
let _ = smarm::register("urus.server", sup_h.pid());
// Block until told to shut down. We poll `try_recv` + `sleep`
// rather than parking in `recv`: a smarm `Sender::send` from a