docs+examples: v0.3 endpoint API; crud converted to the app-owns-the-tree shape

- crud is now the demonstrator: app owns smarm runtime + root supervisor,
  store actor and urus::endpoint as ordered siblings (store first, so
  reverse-order shutdown drains HTTP before stopping the store),
  Shutdown::Infinity on the endpoint, shutdown via
  rt.handle().request_shutdown(root_sup) from the stdin thread.
  Deletes two kludges the old shape forced:
    * static OnceLock<Sender> spawn-on-first-use -> a supervised child
      that self-registers a typed Name; handlers use smarm::send per
      request and turn 'between incarnations' into a 503 instead of
      panicking on a dropped store.
    * static SHUTTING_DOWN AtomicBool + 250ms recv_timeout poll in the
      store loop and in the SSE ticker -> a plain park; the tree stops
      both. Smoke-tested live: CRUD round-trips, SSE stream, clean drain
      with the stream open, port closed after.
- Other examples stay short and on serve*, updated for the split config
  (serve_with(cfg, smarm::Config, pipe) / serve_with_shutdown(..., signal)).
  plain_serve's URUS_SCHED_THREADS now builds a smarm::Config.
  ws_chat's doc block explains the OnceLock is a serve*-only workaround
  and points at crud for the clean shape.
- README: new 'Your Own Supervision Tree' section (endpoint as the real
  API), graceful shutdown reframed as the serve*-only path, Config table
  loses scheduler_threads and gains name, PubSub rule 1 notes the
  supervised-sibling alternative.

111 lib + 50 integration + 2 doc tests green; clippy clean.
This commit is contained in:
Claude
2026-08-20 13:20:28 +00:00
parent 099b6bc320
commit b37888ec2c
9 changed files with 181 additions and 95 deletions
+17 -9
View File
@@ -7,14 +7,16 @@
//!
//! - **One `PubSub<String>` for the whole app**, topics are rooms
//! (`room:{name}`). Built lazily on the first connection via a
//! NON-static `Arc<OnceLock<PubSub>>` captured by the route closure:
//! `PubSub::new()` spawns the table actor, which smarm only allows
//! in-runtime — and keeping the cell non-static means the table's
//! last handle drops in-runtime when the drained pipeline drops, so
//! `serve_with_shutdown` actually returns. A `static` cell would pin
//! the table forever and block smarm's all-done. (Same constraint
//! crud's store solves with its OnceLock; the non-static refinement
//! is what makes graceful shutdown compose.)
//! NON-static `Arc<OnceLock<PubSub>>` captured by the route closure,
//! because `PubSub::new()` spawns the table actor and smarm only
//! allows `spawn` in-runtime — while `serve_with_shutdown` owns the
//! runtime, there is no in-runtime moment before the first request.
//! Keep the cell non-static so the table is dropped with the drained
//! pipeline rather than pinned for the life of the process.
//!
//! If your app owns its own runtime and tree (see `crud`), skip this
//! entirely: start the table as a supervised sibling of
//! `urus::endpoint(...)` and address it by name.
//!
//! - **`on_open` subscribes and spawns the relay** — a listen-only
//! client receives the room without ever sending. The subscription is
@@ -123,6 +125,12 @@ fn main() {
handle.shutdown();
});
serve_with_shutdown(Config::new("0.0.0.0:8080".parse().unwrap()), pipeline, signal).unwrap();
serve_with_shutdown(
Config::new("0.0.0.0:8080".parse().unwrap()),
smarm::Config::default(),
pipeline,
signal,
)
.unwrap();
println!("ws_chat: drained, bye");
}