docs(websocket): detached task lifetime semantics (WS-10)
This commit is contained in:
@@ -9,6 +9,32 @@
|
|||||||
//! path). The `install_channel_zero` hook constructs channel 0's
|
//! path). The `install_channel_zero` hook constructs channel 0's
|
||||||
//! `CallConnection` (the identity rides the channels-layer
|
//! `CallConnection` (the identity rides the channels-layer
|
||||||
//! `Connection`) and runs the shared `Dispatcher::run_loop_single_stream`.
|
//! `Connection`) and runs the shared `Dispatcher::run_loop_single_stream`.
|
||||||
|
//!
|
||||||
|
//! ## Detached task lifetime semantics (WS-10)
|
||||||
|
//!
|
||||||
|
//! Two task families spawned here are **detached by design** and
|
||||||
|
//! outlive the session task that spawned them:
|
||||||
|
//!
|
||||||
|
//! - the WS pump tasks (the `WsPumps` pair behind the byte adapter,
|
||||||
|
//! re-exported from `crate::websocket`), and
|
||||||
|
//! - the channel-0 dispatcher task spawned by `install_channel_zero`
|
||||||
|
//! per accepted channels connection.
|
||||||
|
//!
|
||||||
|
//! If the channels session task (`run_channels_session`) dies —
|
||||||
|
//! upgrade-time early return, an adapter fault, or its own task being
|
||||||
|
//! cancelled — these tasks keep running **self-healing**: each ends on
|
||||||
|
//! its own when its stream half closes (peer disconnect, peer close
|
||||||
|
//! frame, read error, or the local teardown arms —
|
||||||
|
//! `WsSessions::abort`, the idle-read timeout, `AsyncWrite::shutdown`).
|
||||||
|
//! They are never leaked unconditionally: the leak window is bounded
|
||||||
|
//! by peer behavior (a peer that holds the socket open keeps the pump
|
||||||
|
//! and dispatcher tasks alive with it) and additionally by the WS-01
|
||||||
|
//! idle-read timeout when configured, and every session's pumps stay
|
||||||
|
//! force-evictable through the WS-08 registry
|
||||||
|
//! ([`WsSessions::abort`]) for the session's whole lifetime. The
|
||||||
|
//! dispatcher task specifically ends when channel 0's `BiStream` read
|
||||||
|
//! side hits EOF (its only exit condition), i.e. when the underlying
|
||||||
|
//! WS connection ends by any of the paths above.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
@@ -35,9 +61,9 @@ use super::byte_adapter::{
|
|||||||
/// halves close and the channels session unwinds). The handle for a
|
/// halves close and the channels session unwinds). The handle for a
|
||||||
/// session is removed when the session task finishes (self-removing
|
/// session is removed when the session task finishes (self-removing
|
||||||
/// guard), so the registry only holds live sessions. Assembly layers
|
/// guard), so the registry only holds live sessions. Assembly layers
|
||||||
/// share one instance via [`crate::server::state::RouterState`] (the
|
/// share one instance via the adapter's `RouterState` (the upgrade
|
||||||
/// upgrade handler registers against it) or per-route request
|
/// handler registers against it) or per-route request extensions (an
|
||||||
/// extensions (an extension clone takes precedence).
|
/// extension clone takes precedence).
|
||||||
///
|
///
|
||||||
/// Un-registered (default) — the upgrade runs fine and simply keeps no
|
/// Un-registered (default) — the upgrade runs fine and simply keeps no
|
||||||
/// eviction lever, matching the pre-WS-08 behavior.
|
/// eviction lever, matching the pre-WS-08 behavior.
|
||||||
@@ -54,11 +80,11 @@ pub const DEFAULT_WS_MAX_SESSIONS: usize = 64;
|
|||||||
|
|
||||||
/// The upgrade handler's state slice: what it needs beyond the request
|
/// The upgrade handler's state slice: what it needs beyond the request
|
||||||
/// itself. Axum lifts it via `FromRef` from either full router state —
|
/// itself. Axum lifts it via `FromRef` from either full router state —
|
||||||
/// the adapter's [`crate::server::state::RouterState`] (carrying the
|
/// the adapter's `RouterState` (carrying the shared [`WsSessions`]
|
||||||
/// shared [`WsSessions`] instance and the configured session cap) or a
|
/// instance and the configured session cap) or a bare
|
||||||
/// bare `Arc<OperationRegistry>` (custom upgrade routes / integration
|
/// `Arc<OperationRegistry>` (custom upgrade routes / integration tests
|
||||||
/// tests get a handler-private registry and the default cap; eviction
|
/// get a handler-private registry and the default cap; eviction still
|
||||||
/// still works in-crate, just not shared).
|
/// works in-crate, just not shared).
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionState {
|
pub struct SessionState {
|
||||||
registry: Arc<OperationRegistry>,
|
registry: Arc<OperationRegistry>,
|
||||||
@@ -122,7 +148,7 @@ impl axum::extract::FromRef<SessionState> for Arc<OperationRegistry> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `FromRef` chain: a bare `Arc<OperationRegistry>` router state lifts
|
/// `FromRef` chain: a bare `Arc<OperationRegistry>` router state lifts
|
||||||
/// into the handler's [`SessionState`]; a full [`RouterState`] carries
|
/// into the handler's [`SessionState`]; a full `RouterState` carries
|
||||||
/// the shared [`WsSessions`] instance and lifts through its own impl.
|
/// the shared [`WsSessions`] instance and lifts through its own impl.
|
||||||
impl axum::extract::FromRef<Arc<OperationRegistry>> for SessionState {
|
impl axum::extract::FromRef<Arc<OperationRegistry>> for SessionState {
|
||||||
fn from_ref(registry: &Arc<OperationRegistry>) -> Self {
|
fn from_ref(registry: &Arc<OperationRegistry>) -> Self {
|
||||||
|
|||||||
Reference in New Issue
Block a user