diff --git a/README.md b/README.md index 193590e..a453bd6 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,24 @@ fn main() -> Result<(), Box> { The client library emits structured events for session lifecycle, sent probes, successful replies, loss, duplicates, late replies, warnings, and shutdown. +For shared-socket multi-target integrations, `ManagedClientGroup` publishes +`ManagedGroupEvent::TargetFinished` exactly once for every terminal target +incarnation, including open/runtime failures, removal, cancellation, no-test +completion, and peer closure. Consumers can use `ManagedTargetEndReason` and +`ManagedTargetFailureKind` for outcome and exit decisions without parsing +diagnostic text. + +`ManagedGroupCompletionPolicy::AllTargetsComplete` supports finite static +groups. `ExplicitCancellation` supports long-lived controllers; replacing the +desired set with an empty vector removes current targets but leaves the group +idle and ready for a later `update_targets` call. `join()` reports aggregate +outcome counts and retains only the 256 most recent target outcomes, so +long-running target churn remains bounded. + +Managed event subscriptions remain bounded and nonblocking. When using a +dropping overflow policy, check `EventSubscription::dropped_events()` before +trusting a complete statistical summary. + ## Binaries and features | Build | Binaries | diff --git a/crates/irtt-client/src/managed/group.rs b/crates/irtt-client/src/managed/group.rs index ec33e11..e6d2a0d 100644 --- a/crates/irtt-client/src/managed/group.rs +++ b/crates/irtt-client/src/managed/group.rs @@ -97,6 +97,10 @@ pub struct ManagedTargetConfig { } /// Group send pacing strategy. +/// +/// Both modes keep an absolute cadence. If a scheduler delay spans multiple +/// slots, missed historical slots are skipped instead of sent as a catch-up +/// burst. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum ManagedGroupPacing { /// Send active targets one at a time, spaced approximately interval / N. @@ -362,6 +366,10 @@ fn classify_target_failure(error: &ClientError, opening: bool) -> ManagedTargetF impl ManagedClientGroup { /// Start a managed group without creating an initial event subscription. + /// + /// At least one initial target is required to choose the address family for + /// the shared socket. After startup, [`ManagedClientGroupSession::update_targets`] + /// may replace the desired set with an empty set. pub fn start( config: ManagedClientGroupConfig, targets: Vec, @@ -370,6 +378,9 @@ impl ManagedClientGroup { } /// Start a managed group and subscribe before worker threads run. + /// + /// As with [`start`](Self::start), construction requires at least one + /// initial target. pub fn start_with_subscription( config: ManagedClientGroupConfig, targets: Vec, @@ -514,6 +525,11 @@ impl ManagedClientGroupSession { } /// Wait for the scheduler and receive threads to finish. + /// + /// The returned outcome contains aggregate counts plus a bounded recent + /// target-outcome snapshot. Authoritative outcomes are published through + /// [`ManagedGroupEvent::TargetFinished`] before the subscription is sealed, + /// and queued events remain drainable after this method returns. pub fn join(mut self) -> Result { let scheduler = self.scheduler.take().expect( "ManagedClientGroupSession invariant violated: scheduler handle missing before join", diff --git a/crates/irtt-client/src/managed/runner.rs b/crates/irtt-client/src/managed/runner.rs index 24ad6e8..86b0edb 100644 --- a/crates/irtt-client/src/managed/runner.rs +++ b/crates/irtt-client/src/managed/runner.rs @@ -25,7 +25,8 @@ const IDLE_SLEEP: Duration = Duration::from_millis(1); /// The managed API owns the lower-level [`Client`](crate::Client) loop: it /// opens the session, sends probes at the negotiated interval, receives /// datagrams, publishes [`ClientEvent`] values, and closes the session when the -/// run completes or is cancelled. +/// run completes or is cancelled. Delayed workers skip missed pacing slots +/// instead of sending catch-up bursts. #[derive(Debug)] pub struct ManagedClient;