Skip to main content

liminal_server/server/connection/
supervisor.rs

1use std::collections::HashMap;
2#[cfg(test)]
3use std::collections::VecDeque;
4use std::net::{SocketAddr, TcpStream};
5use std::os::fd::RawFd;
6#[cfg(test)]
7use std::sync::Barrier;
8use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
9use std::sync::mpsc::{Receiver, RecvTimeoutError, Sender, TryRecvError, channel};
10use std::sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError, Weak};
11use std::thread;
12use std::time::{Duration, Instant};
13
14use beamr::atom::{Atom, AtomTable};
15use beamr::module::ModuleRegistry;
16use beamr::native::native_process::NativeHandlerFactory;
17use beamr::process::ExitReason;
18use beamr::scheduler::{
19    ExitEvent, ExitEventSubscription, ReadinessToken, Scheduler, SchedulerConfig, SchedulerServices,
20};
21use beamr::timer::TimerRef;
22
23use liminal::protocol::WorkerRegistration;
24use liminal_protocol::wire::ConnectionIncarnation;
25
26use super::incarnation::ConnectionIncarnationAuthority;
27use super::notifier::ConnectionNotifier;
28use super::process::ConnectionProcess;
29use super::services::{
30    ConnectionServices, LiminalConnectionServices, ProductionSubsystems, SubsystemFactory,
31    build_connection_services_via,
32};
33use crate::ServerError;
34use crate::config::types::{LimitsConfig, ServerConfig};
35use crate::server::participant::{
36    ConnectionFateClass, InstalledParticipantService, ParticipantSemanticHandler,
37    ParticipantServiceFatal,
38};
39use crate::server::shutdown::ShutdownHandle;
40
41const CONNECTION_SCHEDULER_THREADS: usize = 4;
42const CONNECTION_SHUTDOWN_CONTROL_ATOM: &str = "liminal_server_connection_shutdown_control";
43/// R6 (§1.2(4)): the single `READY` wake vocabulary for a connection. One atom;
44/// any marker (or N coalesced) triggers one full slice servicing all sources.
45const CONNECTION_READY_ATOM: &str = "liminal_server_connection_ready";
46
47#[cfg(test)]
48#[path = "supervisor_fate_tests.rs"]
49mod fate_tests;
50#[cfg(test)]
51#[path = "supervisor_tests.rs"]
52mod tests;
53
54/// Supervisor that owns the beamr scheduler for per-connection processes.
55#[derive(Clone, Debug)]
56pub struct ConnectionSupervisor {
57    inner: Arc<SupervisorInner>,
58}
59
60impl ConnectionSupervisor {
61    /// Creates a connection supervisor backed by the services the config's
62    /// `[services]` profile selects: the full liminal channel/conversation stack
63    /// (the default) or the capability-scoped worker front door. Profile
64    /// enforcement is [`build_connection_services`](super::services::build_connection_services)'s,
65    /// so this constructor can never build full services for a worker-front-door
66    /// config.
67    ///
68    /// # Errors
69    /// Returns [`ServerError`] when service construction or scheduler startup fails.
70    pub fn from_config(config: &ServerConfig) -> Result<Self, ServerError> {
71        Self::from_config_via(config, &ProductionSubsystems)
72    }
73
74    /// [`Self::from_config`] with the §9 D2 subsystem factory injected.
75    ///
76    /// The factory is the only route to every scheduler-owning subsystem the
77    /// services construction builds, so a recording factory observes exactly what
78    /// was constructed; the connection scheduler itself (built below for BOTH
79    /// profiles) is the census baseline, not a census entry.
80    fn from_config_via(
81        config: &ServerConfig,
82        subsystems: &dyn SubsystemFactory,
83    ) -> Result<Self, ServerError> {
84        let services = build_connection_services_via(config, subsystems)?;
85        // The configured token (if any) is carried opaquely as bytes for a
86        // constant-time comparison against the handshake's `auth_token`. Absent
87        // `[auth]` leaves it `None`, so the connection stays open-access.
88        let auth_token = config
89            .auth
90            .as_ref()
91            .map(|auth| auth.token.clone().into_bytes());
92        SupervisorInner::new(services, None, auth_token, config.limits, None).map(|inner| Self {
93            inner: Arc::new(inner),
94        })
95    }
96
97    /// Creates a connection supervisor with no configured channels.
98    ///
99    /// # Errors
100    /// Returns [`ServerError`] when scheduler startup fails.
101    pub fn new() -> Result<Self, ServerError> {
102        Self::with_services(Arc::new(LiminalConnectionServices::empty()?))
103    }
104
105    /// Creates a connection supervisor using an explicit service adapter.
106    ///
107    /// # Errors
108    /// Returns [`ServerError`] when scheduler startup fails.
109    pub fn with_services(services: Arc<dyn ConnectionServices>) -> Result<Self, ServerError> {
110        SupervisorInner::new(services, None, None, LimitsConfig::default(), None).map(|inner| {
111            Self {
112                inner: Arc::new(inner),
113            }
114        })
115    }
116
117    /// Creates a connection supervisor with an explicit service adapter and the
118    /// configured connection auth token.
119    ///
120    /// This is the production constructor for callers that build services
121    /// themselves (the runtime needs the shared channel cluster before the
122    /// supervisor takes ownership) and therefore cannot use
123    /// [`Self::from_config`]: without it the configured `[auth]` token would be
124    /// silently dropped and the server would run open-access.
125    ///
126    /// # Errors
127    /// Returns [`ServerError`] when scheduler startup fails.
128    pub fn with_services_and_auth(
129        services: Arc<dyn ConnectionServices>,
130        auth_token: Option<Vec<u8>>,
131    ) -> Result<Self, ServerError> {
132        Self::with_services_auth_and_limits(services, auth_token, LimitsConfig::default())
133    }
134
135    /// Creates a connection supervisor with explicit services, authentication,
136    /// and operational limits.
137    ///
138    /// Production runtime construction uses this form so the durable
139    /// incarnation stream's complete-reference bound is the same signed
140    /// `max_connections` bound enforced by connection admission.
141    ///
142    /// # Errors
143    /// Returns [`ServerError`] when incarnation startup or scheduler startup fails.
144    pub fn with_services_auth_and_limits(
145        services: Arc<dyn ConnectionServices>,
146        auth_token: Option<Vec<u8>>,
147        limits: LimitsConfig,
148    ) -> Result<Self, ServerError> {
149        Self::with_services_auth_limits_and_fatal_shutdown(services, auth_token, limits, None)
150    }
151
152    /// Production composition with the process-wide shutdown activation that a
153    /// post-Open participant fatal must join.
154    pub(crate) fn with_fatal_shutdown(
155        services: Arc<dyn ConnectionServices>,
156        auth_token: Option<Vec<u8>>,
157        limits: LimitsConfig,
158        fatal_shutdown: ShutdownHandle,
159    ) -> Result<Self, ServerError> {
160        Self::with_services_auth_limits_and_fatal_shutdown(
161            services,
162            auth_token,
163            limits,
164            Some(fatal_shutdown),
165        )
166    }
167
168    fn with_services_auth_limits_and_fatal_shutdown(
169        services: Arc<dyn ConnectionServices>,
170        auth_token: Option<Vec<u8>>,
171        limits: LimitsConfig,
172        fatal_shutdown: Option<ShutdownHandle>,
173    ) -> Result<Self, ServerError> {
174        SupervisorInner::new(services, None, auth_token, limits, fatal_shutdown).map(|inner| Self {
175            inner: Arc::new(inner),
176        })
177    }
178
179    /// Creates a connection supervisor with an explicit service adapter and a
180    /// connection-keyed worker-registration notifier.
181    ///
182    /// The `notifier` is invoked when a worker registers on a connection and when
183    /// such a connection closes. Supervisors built via [`Self::with_services`],
184    /// [`Self::from_config`], or [`Self::new`] carry no notifier, so liminal still
185    /// runs standalone; a `WorkerRegister` frame is then accepted without any
186    /// application callback.
187    ///
188    /// # Errors
189    /// Returns [`ServerError`] when scheduler startup fails.
190    pub fn with_services_and_notifier(
191        services: Arc<dyn ConnectionServices>,
192        notifier: Arc<dyn ConnectionNotifier>,
193    ) -> Result<Self, ServerError> {
194        SupervisorInner::new(
195            services,
196            Some(notifier),
197            None,
198            LimitsConfig::default(),
199            None,
200        )
201        .map(|inner| Self {
202            inner: Arc::new(inner),
203        })
204    }
205
206    /// Spawns one supervised beamr process that owns `stream`.
207    ///
208    /// # Errors
209    /// Returns [`ServerError`] when stream configuration or beamr spawn fails.
210    pub fn spawn_connection(&self, stream: TcpStream) -> Result<ConnectionHandle, ServerError> {
211        self.inner.spawn_connection(stream)
212    }
213
214    /// Returns the underlying beamr scheduler.
215    #[must_use]
216    pub fn scheduler(&self) -> Arc<Scheduler> {
217        Arc::clone(&self.inner.scheduler)
218    }
219
220    /// Reaps connection processes that have exited outside the normal handler path.
221    #[must_use]
222    pub fn reap_crashed_connections(&self) -> usize {
223        self.inner.runtime.reap_crashed(&self.inner.scheduler)
224    }
225
226    /// Returns true when `pid` is still tracked by the supervisor.
227    #[must_use]
228    pub fn is_tracked(&self, pid: u64) -> bool {
229        self.inner.runtime.contains(pid)
230    }
231
232    /// Returns the number of tracked live connections.
233    #[must_use]
234    pub fn active_connection_count(&self) -> usize {
235        self.inner.runtime.active_count()
236    }
237
238    /// Parks until every tracked connection has been removed or `deadline`
239    /// elapses, returning `true` when the drain completed and `false` when the
240    /// single admitted deadline won.
241    ///
242    /// The TOLD drain-completion replacement (W4 leg 3, §4.3): the waiter is woken
243    /// by the one `remove()` funnel every connection exit reaches — never by a
244    /// reap/count timer. Both the graceful drain and the force-close settle call
245    /// this same waiter, each with its own one-shot deadline; there is no second
246    /// settle poll loop.
247    #[must_use]
248    pub(crate) fn wait_for_connections_drained(&self, deadline: Instant) -> bool {
249        self.inner
250            .runtime
251            .wait_for_active_connections_drained(deadline)
252    }
253
254    /// FIX A-ii shutdown flush barrier: parks until every active connection has
255    /// fanned out its accepted publishes to its socket, or `deadline` elapses.
256    /// Called in `run_shutdown_sequence` BEFORE the shutdown Disconnect broadcast
257    /// so an accepted-but-unfanned-out publish can no longer be overtaken by it.
258    #[must_use]
259    pub(crate) fn wait_for_delivery_quiesced(&self, deadline: Instant) -> bool {
260        self.inner.runtime.wait_for_delivery_quiesced(deadline)
261    }
262
263    /// Returns the first latched post-Open participant fatal, if any.
264    ///
265    /// The production runtime reads this after its existing shutdown handle wakes,
266    /// then returns the typed fatal after the ordinary drain and durable flush.
267    pub(crate) fn participant_service_fatal(
268        &self,
269    ) -> Result<Option<ParticipantServiceFatal>, ServerError> {
270        self.inner.runtime.participant_service_fatal()
271    }
272
273    /// Returns the beamr process ids of the currently tracked live connections.
274    ///
275    /// Useful for addressing a specific connection — e.g. as the `pid` argument to
276    /// [`push_to_connection`](Self::push_to_connection) when the caller knows there
277    /// is a single connected client.
278    #[must_use]
279    pub fn active_connection_pids(&self) -> Vec<u64> {
280        self.inner
281            .runtime
282            .active_connections()
283            .into_iter()
284            .map(|connection| connection.pid)
285            .collect()
286    }
287
288    /// Broadcasts a best-effort shutdown notification to active connections.
289    ///
290    /// Connections with no active subscriptions ignore the notification. Failures
291    /// to enqueue the control message are logged and skipped; they are not retried.
292    pub fn notify_shutdown_subscribers(&self) {
293        self.inner
294            .broadcast_control(&ConnectionControl::NotifyShutdown);
295    }
296
297    /// Sends a force-close control message to every tracked connection process.
298    ///
299    /// Each live process attempts one shutdown notification before closing its
300    /// stream and exiting normally. Enqueue failures are logged and skipped.
301    pub fn force_close_active_connections(&self) {
302        for connection in self.inner.runtime.active_connections() {
303            tracing::warn!(
304                connection_pid = connection.pid,
305                peer_addr = ?connection.peer_addr,
306                "forcefully closing connection after drain timeout"
307            );
308            if !self
309                .inner
310                .enqueue_control(connection.pid, ConnectionControl::ForceClose)
311            {
312                tracing::warn!(
313                    connection_pid = connection.pid,
314                    peer_addr = ?connection.peer_addr,
315                    "failed to request forceful connection close; process is not live"
316                );
317            }
318        }
319    }
320
321    /// Pushes an opaque payload to a specific connected client over that client's
322    /// existing connection and returns an awaiter for the client's correlated reply.
323    ///
324    /// This is the server-initiated leg (server-to-client), the inverse of every
325    /// other request frame. It allocates a correlation id, registers a one-shot
326    /// reply slot keyed by that id, and enqueues a [`ConnectionControl::Push`] for
327    /// the connection process owning `pid`; that process writes a [`Frame::Push`]
328    /// out on its socket. When the client answers with a `PushReply` carrying the
329    /// same correlation id, the connection process resolves the awaiter's slot. The
330    /// returned [`PushReplyAwaiter`] blocks (bounded) for that reply.
331    ///
332    /// The reply's lifetime belongs to the push, not to any one
333    /// [`PushReplyAwaiter::receive`] call: this no-deadline push reserves a slot
334    /// that is reclaimed only by (a) the reply being consumed or (b) the
335    /// connection closing. An elapsed `receive` poll is a benign re-arm, never a
336    /// failure and never a cancellation. The §5
337    /// `max_pending_pushes_per_connection` cap bounds abandonment; use
338    /// [`push_to_connection_with_deadline`](Self::push_to_connection_with_deadline)
339    /// when the reply must have an explicit expiry.
340    ///
341    /// # Errors
342    /// Returns [`ServerError`] when the correlation id cannot be allocated, the
343    /// reply slot cannot be registered, or the control message cannot be enqueued
344    /// for the (possibly already-gone or concurrently-closing) connection
345    /// process. PUBLICATION INVARIANT: an `Err` guarantees no `Push` control was
346    /// published — the client never sees a `Push` frame for a failed call.
347    /// Conversely `Ok` promises ADMISSION, not delivery: the awaiter's outcome
348    /// is the delivery truth (a push admitted just as its connection closes
349    /// resolves to the truthful disconnected outcome, never to a lost reply).
350    pub fn push_to_connection(
351        &self,
352        pid: u64,
353        payload: Vec<u8>,
354    ) -> Result<PushReplyAwaiter, ServerError> {
355        self.push_with_deadline(pid, payload, None)
356    }
357
358    /// Like [`push_to_connection`](Self::push_to_connection) but attaches an
359    /// explicit reply deadline to the reserved slot: `deadline` is a DURATION
360    /// FROM NOW bounding the reply's lifetime — a property of THIS push rather
361    /// than of any [`PushReplyAwaiter::receive`] wait quantum.
362    ///
363    /// Deadline expiry is evaluated HOST-SIDE and LAZILY — at the next `receive`
364    /// touch, and at connection close at the latest. It never wakes the connection
365    /// process, adds no timer thread, and runs no periodic sweeper: a push that is
366    /// abandoned and never polled resolves at the next host-side touch (connection
367    /// close). At expiry the slot resolves to [`ServerError::PushReplyExpired`],
368    /// is removed, and its §5 `max_pending_pushes_per_connection` cap admission is
369    /// released. An elapsed `receive` poll BEFORE the deadline is still a benign
370    /// re-arm. A `receive` call in flight when the deadline falls due returns
371    /// the terminal expiry PROMPTLY — it waits the earlier of its quantum and
372    /// the deadline, so a large quantum can never extend the reply's lifetime
373    /// and the terminal outcome is quantum-independent.
374    ///
375    /// The deadline is evaluated at OBSERVATION POINTS, not enforced against the
376    /// wall clock: a reply that arrives before expiry is observed is delivered
377    /// normally, even if it arrives after the deadline instant. The deadline
378    /// bounds waiting and slot occupancy; it is not a delivery-freshness
379    /// guarantee. (This is deliberate — a reply is checked for at every
380    /// observation point before the deadline is, so an answer in hand always
381    /// beats an expiry.)
382    ///
383    /// # Errors
384    /// Returns [`ServerError`] when `deadline` is not representable on the
385    /// monotonic clock (an extreme duration is refused, never a panic), the
386    /// correlation id cannot be allocated, the reply slot cannot be registered,
387    /// or the control message cannot be enqueued for the (possibly already-gone
388    /// or concurrently-closing) connection process. PUBLICATION INVARIANT: an
389    /// `Err` guarantees no `Push` control was published — the client never sees
390    /// a `Push` frame for a failed call. Conversely `Ok` promises ADMISSION,
391    /// not delivery: the awaiter's outcome is the delivery truth.
392    pub fn push_to_connection_with_deadline(
393        &self,
394        pid: u64,
395        payload: Vec<u8>,
396        deadline: Duration,
397    ) -> Result<PushReplyAwaiter, ServerError> {
398        self.push_with_deadline(pid, payload, Some(deadline))
399    }
400
401    /// Shared body for the no-deadline and explicit-deadline push paths. With
402    /// `deadline == None` this is byte-for-byte the historical
403    /// `push_to_connection` behaviour (no per-slot deadline); with `Some`, the
404    /// slot carries an absolute expiry evaluated lazily at `receive`.
405    fn push_with_deadline(
406        &self,
407        pid: u64,
408        payload: Vec<u8>,
409        deadline: Option<Duration>,
410    ) -> Result<PushReplyAwaiter, ServerError> {
411        // S5: an extreme `Duration` must surface as this fallible API's typed
412        // error, not an `Instant` addition panic. Checked BEFORE any slot is
413        // registered so a refused deadline leaves nothing to roll back.
414        let deadline_at = match deadline {
415            None => None,
416            Some(window) => {
417                Some(
418                    Instant::now()
419                        .checked_add(window)
420                        .ok_or_else(|| ServerError::ListenerAccept {
421                            message: format!(
422                                "cannot push to connection process {pid}: reply deadline of {window:?} overflows the monotonic clock"
423                            ),
424                        })?,
425                )
426            }
427        };
428        let correlation_id = self.inner.runtime.next_push_correlation_id();
429        let receiver = self
430            .inner
431            .runtime
432            .register_push(pid, correlation_id, deadline_at)?;
433        // S3+S7 close-vs-register wall, ordered INSERT -> CONFIRM -> PUBLISH.
434        // The confirmation runs BEFORE the control is enqueued, which yields the
435        // PUBLICATION INVARIANT: an `Err` from this method guarantees no `Push`
436        // control was published — the client never sees a Push for a failed
437        // call. (Confirming after the enqueue was S7's non-linearizable race: a
438        // close could sweep, the published Push could already be answered and
439        // resolved, and the failed confirmation then returned `Err` for a push
440        // the client had received.) A close landing AFTER a successful confirm
441        // linearizes after push admission: the enqueue either fails (process
442        // gone — rollback below, `Err` truthful, nothing delivered) or succeeds
443        // with the slot already swept, and the awaiter then reads the truthful
444        // DISCONNECTED while a late client reply is the pinned harmless no-op.
445        // The exactly-one-side-observes argument lives at
446        // `confirm_push_registration`.
447        if !self
448            .inner
449            .runtime
450            .confirm_push_registration(pid, correlation_id)
451        {
452            return Err(ServerError::ListenerAccept {
453                message: format!(
454                    "cannot push to connection process {pid}: the connection closed during push registration"
455                ),
456            });
457        }
458        let control = ConnectionControl::Push {
459            correlation_id,
460            payload,
461        };
462        if self.inner.enqueue_control(pid, control) {
463            Ok(PushReplyAwaiter {
464                correlation_id,
465                receiver,
466                deadline: deadline_at,
467                runtime: Arc::downgrade(&self.inner.runtime),
468            })
469        } else {
470            // The process is gone AND the control provably never reached a
471            // consumer: `enqueue_control` returns false only when its failed-wake
472            // rollback REMOVED the queued control (S8 — an entry a drain already
473            // consumed counts as published and returns true, with the slot
474            // lifecycle carrying the delivery truth). Dropping the now-unreachable
475            // reply slot here therefore keeps the publication invariant exact on
476            // every `Err` path.
477            self.inner.runtime.cancel_push(correlation_id);
478            Err(ServerError::ListenerAccept {
479                message: format!("cannot push to connection process {pid}: process is not live"),
480            })
481        }
482    }
483
484    /// Flushes durable channel state through the configured liminal services.
485    ///
486    /// # Errors
487    /// Returns [`ServerError::ShutdownFlush`] when the underlying service flush fails.
488    pub fn flush_durable_state(&self) -> Result<(), ServerError> {
489        self.inner.runtime.services().flush_durable_state()
490    }
491
492    /// LP-WS-TRANSPORT R1.3 sibling-transport spawn seam (ADDITIVE ONLY).
493    ///
494    /// Admits, allocates a durable connection incarnation for, spawns, and
495    /// registers a connection process whose handler is built by `build_factory`
496    /// over this supervisor's shared [`ConnectionRuntime`]. The WebSocket
497    /// sibling acceptor uses this so its connections share the ONE §5
498    /// `max_connections` admission bound, the one incarnation authority, the
499    /// one registry (controls, pushes, crash reap, drain, forced close), and the
500    /// one `apply_frame` seam with TCP connections. The TCP accept path above
501    /// (`spawn_connection`) is byte-for-byte untouched and never calls this.
502    ///
503    /// `fd_guard` is a host-held duplicate of the connection's underlying
504    /// socket, exactly like the TCP path's: it keeps the fd alive until the
505    /// single record-removal path has synchronously deregistered readiness.
506    ///
507    /// This method exists because Rust module privacy makes the runtime,
508    /// admission counter, incarnation authority, and registry unreachable from
509    /// the sibling `websocket` module family; it is the narrow additive seam
510    /// that shares them without generalizing any TCP hot path.
511    ///
512    /// # Errors
513    /// Returns [`ServerError`] when admission is refused
514    /// ([`ServerError::ConnectionLimitReached`]), incarnation allocation fails,
515    /// or beamr spawn/registration fails.
516    pub(super) fn spawn_transport_connection(
517        &self,
518        peer_addr: Option<SocketAddr>,
519        fd_guard: TcpStream,
520        build_factory: &dyn Fn(
521            Arc<ConnectionRuntime>,
522            Option<ConnectionIncarnation>,
523        ) -> NativeHandlerFactory,
524    ) -> Result<ConnectionHandle, ServerError> {
525        self.inner
526            .spawn_transport_connection(peer_addr, fd_guard, build_factory)
527    }
528
529    /// Stops the beamr scheduler used by connection processes.
530    pub fn shutdown(&self) {
531        // Remove every host record while the readiness owner is still live. The
532        // removal path ACKs deregistration and only then releases each fd guard;
533        // scheduler shutdown subsequently drops the process-owned handles.
534        for connection in self.inner.runtime.active_connections() {
535            self.inner.runtime.finish(connection.pid);
536        }
537        self.inner.scheduler.shutdown();
538    }
539
540    /// R7 test instrument: slices serviced by connection `pid` since spawn.
541    #[cfg(test)]
542    pub(crate) fn slice_count(&self, pid: u64) -> u64 {
543        self.inner.runtime.slice_count(pid)
544    }
545
546    /// Installs a one-use readiness marker for the next serviced slice of `pid`.
547    #[cfg(test)]
548    pub(crate) fn observe_next_slice(&self, pid: u64) -> Receiver<u64> {
549        self.inner.runtime.observe_next_slice(pid)
550    }
551
552    /// Installs a one-use readiness marker for the next genuine scheduler park of
553    /// `pid`. The delivered value is the process's slice count at the final probe
554    /// that selected `Wait`.
555    #[cfg(test)]
556    pub(crate) fn observe_next_park(&self, pid: u64) -> Receiver<u64> {
557        self.inner.runtime.observe_next_park(pid)
558    }
559
560    /// Returns a marker for the current park when `pid` is already settled, or
561    /// the next park when a coalesced readiness event has started another slice.
562    #[cfg(test)]
563    pub(crate) fn observe_settled_park(&self, pid: u64) -> Receiver<u64> {
564        self.inner.runtime.observe_settled_park(pid)
565    }
566
567    /// Queues an explicit outbound capacity for the next TCP process constructed.
568    #[cfg(test)]
569    pub(crate) fn queue_next_outbound_capacity(&self, capacity: usize) {
570        self.inner.runtime.queue_next_outbound_capacity(capacity);
571    }
572
573    #[cfg(test)]
574    pub(crate) fn install_participant_holdback_pause(&self, pid: u64) -> Receiver<()> {
575        self.inner.runtime.install_participant_holdback_pause(pid)
576    }
577
578    #[cfg(test)]
579    pub(crate) fn resume_test_process(&self, pid: u64) -> bool {
580        self.inner.runtime.ready_waker(pid).is_some_and(|waker| {
581            waker.fire();
582            true
583        })
584    }
585
586    /// Reserved push reply slots outstanding (test observability for the public
587    /// push paths — lets e2e tests assert slot reclamation and cap accounting).
588    #[cfg(test)]
589    pub(super) fn pending_push_count(&self) -> usize {
590        self.inner.runtime.pending_push_count()
591    }
592
593    /// R6 test seam: a [`ReadyWaker`](super::wake::ReadyWaker) for `pid` — the same
594    /// handle a subscription-inbox or reply-availability notifier fires.
595    #[cfg(test)]
596    pub(super) fn ready_waker(&self, pid: u64) -> Option<super::wake::ReadyWaker> {
597        self.inner.runtime.ready_waker(pid)
598    }
599
600    /// Registered readiness tokens held in host records (test observability).
601    #[cfg(test)]
602    pub(super) fn readiness_registration_count(&self) -> usize {
603        self.inner.runtime.readiness_registration_count()
604    }
605
606    /// Kernel fd registered for `pid` (test observability for fd-reuse races).
607    #[cfg(test)]
608    pub(super) fn readiness_fd(&self, pid: u64) -> Option<RawFd> {
609        self.inner.runtime.readiness_fd(pid)
610    }
611
612    /// The readiness token registered for `pid` (test observability). Lets the
613    /// fd-reuse successor oracle capture a stale token before reclamation and
614    /// replay its deregister afterwards, proving it is a keyed no-op.
615    #[cfg(test)]
616    pub(super) fn readiness_token(&self, pid: u64) -> Option<ReadinessToken> {
617        self.inner.runtime.readiness_token(pid)
618    }
619
620    /// Installs the pid-specific reclamation gate (oracle 26) and returns its
621    /// `(reached, release, done)` endpoints.
622    #[cfg(test)]
623    pub(super) fn install_reclaim_barrier(
624        &self,
625        pid: u64,
626    ) -> (Arc<Barrier>, Arc<Barrier>, Arc<Barrier>) {
627        self.inner.runtime.install_reclaim_barrier(pid)
628    }
629
630    /// A weak handle to the connection runtime (test observability): lets a
631    /// lifetime test assert the runtime — and transitively the durable store's
632    /// writer lock — is released synchronously at supervisor drop rather than
633    /// held by the detached reclaim reactor.
634    #[cfg(test)]
635    pub(super) fn runtime_weak(&self) -> Weak<ConnectionRuntime> {
636        Arc::downgrade(&self.inner.runtime)
637    }
638
639    /// Installs a one-use observation for the process-owned stream at `fd` being
640    /// dropped. External scheduler termination removes the process-table entry
641    /// before an executing native handler is destroyed, so table absence is not
642    /// sufficient evidence that the descriptor is reusable.
643    #[cfg(test)]
644    pub(super) fn observe_process_stream_drop(&self, fd: RawFd) -> Receiver<()> {
645        self.inner.runtime.observe_process_stream_drop(fd)
646    }
647
648    /// Installs a one-use arm-to-probe barrier and returns its test endpoints.
649    #[cfg(test)]
650    pub(super) fn install_pre_wait_barrier(&self) -> (Arc<Barrier>, Arc<Barrier>) {
651        self.inner.runtime.install_pre_wait_barrier()
652    }
653
654    /// Barrier-staged final probes that observed newly arrived work.
655    #[cfg(test)]
656    pub(super) fn pre_wait_probe_hits(&self) -> u64 {
657        self.inner.runtime.pre_wait_probe_hits()
658    }
659
660    /// Installs the one-use drain-park gate (oracles 18, 19) and returns its
661    /// `(armed, release)` endpoints.
662    #[cfg(test)]
663    pub(super) fn install_drain_park_barrier(&self) -> (Arc<Barrier>, Arc<Barrier>) {
664        self.inner.runtime.install_drain_park_barrier()
665    }
666
667    /// Drain waiter wakes that observed a real connection removal (oracle 12).
668    #[cfg(test)]
669    pub(super) fn drain_exit_wakes(&self) -> u64 {
670        self.inner.runtime.drain_exit_wakes()
671    }
672
673    /// Drain waiter deadline expirations (oracles 12, 16).
674    #[cfg(test)]
675    pub(super) fn drain_deadline_hits(&self) -> u64 {
676        self.inner.runtime.drain_deadline_hits()
677    }
678}
679
680/// Handle for one supervised connection process.
681#[derive(Clone, Debug)]
682pub struct ConnectionHandle {
683    pid: u64,
684    peer_addr: Option<SocketAddr>,
685    connection_incarnation: Option<ConnectionIncarnation>,
686    supervisor: Arc<SupervisorInner>,
687}
688
689impl ConnectionHandle {
690    /// Returns the beamr process id for this connection.
691    #[must_use]
692    pub const fn pid(&self) -> u64 {
693        self.pid
694    }
695
696    /// Returns the peer address if it was available from the accepted stream.
697    #[must_use]
698    pub const fn peer_addr(&self) -> Option<SocketAddr> {
699        self.peer_addr
700    }
701
702    /// Returns the durable participant connection incarnation, when this
703    /// supervisor has a complete participant service installed.
704    ///
705    /// `None` identifies a services adapter that does not advertise participant
706    /// lifecycle semantics.
707    #[must_use]
708    pub const fn connection_incarnation(&self) -> Option<ConnectionIncarnation> {
709        self.connection_incarnation
710    }
711
712    /// Returns whether the beamr process is still live.
713    #[must_use]
714    pub fn is_live(&self) -> bool {
715        self.supervisor
716            .scheduler
717            .process_table()
718            .get(self.pid)
719            .is_some()
720    }
721
722    /// Requests an error exit for tests and supervisor control paths.
723    ///
724    /// # Errors
725    /// Returns [`ServerError`] when the process is no longer live.
726    pub fn request_crash(&self) -> Result<(), ServerError> {
727        if self
728            .supervisor
729            .scheduler
730            .enqueue_atom_message(self.pid, Atom::ERROR)
731        {
732            Ok(())
733        } else {
734            Err(ServerError::ListenerAccept {
735                message: format!("connection process {} is not live", self.pid),
736            })
737        }
738    }
739}
740
741/// Awaits the correlated reply to a single server-initiated push.
742///
743/// Returned by [`ConnectionSupervisor::push_to_connection`]. The reply slot is
744/// resolved when the originating connection process receives a `PushReply` frame
745/// carrying the same correlation id, so [`PushReplyAwaiter::receive`] blocks
746/// (bounded) for that one correlated answer.
747#[derive(Debug)]
748pub struct PushReplyAwaiter {
749    correlation_id: u64,
750    receiver: Receiver<Vec<u8>>,
751    /// This push's absolute reply deadline, mirrored from its slot. `None` (the
752    /// default push) selects the no-deadline receive path, which NEVER touches
753    /// the runtime — byte-compatible with 0.2.3, no shared-lock exposure.
754    /// `Some` lets `receive` wait `min(caller quantum, time until deadline)` and
755    /// resolve expiry promptly, so the caller's quantum can never select a
756    /// deadlined push's terminal outcome.
757    deadline: Option<Instant>,
758    /// Weak handle to the owning runtime, used ONLY by the explicit-deadline
759    /// path to resolve expiry host-side at [`receive`](Self::receive). A
760    /// no-deadline push never upgrades it. `Weak` so the awaiter never keeps the
761    /// runtime alive; if it is already gone, the slot (and its sender) is gone
762    /// with it — the connection side is torn down.
763    runtime: Weak<ConnectionRuntime>,
764}
765
766impl PushReplyAwaiter {
767    /// Returns the correlation id this awaiter is matched on.
768    #[must_use]
769    pub const fn correlation_id(&self) -> u64 {
770        self.correlation_id
771    }
772
773    /// Blocks up to `timeout` for the client's correlated reply payload.
774    ///
775    /// `timeout` is a WAIT QUANTUM ONLY — a MAXIMUM wait, not a promise to
776    /// block: an elapsed poll is a benign re-arm, never a failure; the reply's
777    /// lifetime belongs to the push. A caller may re-invoke `receive`
778    /// indefinitely after a [`ServerError::PushReplyTimeout`]: the reserved slot
779    /// is untouched and a later reply is still delivered byte-exact. The poll
780    /// quantum never changes the protocol outcome — for a deadlined push the
781    /// call waits no longer than the EARLIER of the caller's quantum and the
782    /// push's deadline, so the terminal expiry is returned promptly once due,
783    /// never held until the quantum ends and never deferred past it.
784    ///
785    /// A push with no explicit deadline never touches shared supervisor state
786    /// here: the elapsed quantum returns straight from the channel wait
787    /// (behaviour-compatible with 0.2.3 — no registry lock, no contention, no
788    /// poison exposure on the unchanged API).
789    ///
790    /// # Errors
791    /// Returns [`ServerError::PushReplyTimeout`] when no reply arrived within this
792    /// `timeout` quantum and the push's deadline (if any) is not yet due (a
793    /// benign re-arm — call again to keep waiting);
794    /// [`ServerError::PushReplyExpired`] when the push carried an explicit reply
795    /// deadline (via
796    /// [`push_to_connection_with_deadline`](ConnectionSupervisor::push_to_connection_with_deadline))
797    /// and that deadline is due (terminal: the slot is removed and its §5 cap
798    /// admission released; returned as soon as the deadline passes, even
799    /// mid-quantum — but evaluated at observation points, not against the wall
800    /// clock: a reply already delivered when this call observes the slot wins
801    /// over expiry, even if it arrived after the deadline instant); or
802    /// [`ServerError::PushReplyDisconnected`] when the connection process
803    /// dropped the reply slot (the connection closed — the prompt worker-death
804    /// signal). The variants are distinct so callers classify by type, not
805    /// message.
806    pub fn receive(&self, timeout: Duration) -> Result<Vec<u8>, ServerError> {
807        self.deadline.map_or_else(
808            || self.receive_no_deadline(timeout),
809            |deadline| self.receive_deadlined(timeout, deadline),
810        )
811    }
812
813    /// The default-push receive: exactly the 0.2.3 shape. One bounded channel
814    /// wait; an elapsed quantum is a benign timeout straight from the channel —
815    /// no runtime upgrade, no registry lock, EVER (unrelated registry work can
816    /// never stretch this call past its quantum, and registry poison cannot
817    /// reach it).
818    fn receive_no_deadline(&self, timeout: Duration) -> Result<Vec<u8>, ServerError> {
819        match self.receiver.recv_timeout(timeout) {
820            Ok(payload) => Ok(payload),
821            Err(RecvTimeoutError::Timeout) => Err(ServerError::PushReplyTimeout {
822                correlation_id: self.correlation_id,
823            }),
824            Err(RecvTimeoutError::Disconnected) => Err(ServerError::PushReplyDisconnected {
825                correlation_id: self.correlation_id,
826            }),
827        }
828    }
829
830    /// The deadlined receive: waits `min(caller quantum, time until deadline)`
831    /// and re-evaluates reply-first-then-expiry on every wake, so the caller's
832    /// quantum can never select the terminal outcome (S1). Order per iteration:
833    ///
834    /// 1. Deliver a reply already in hand — an answer that is here must never be
835    ///    reported as a timeout OR an expiry (the observation-point rule).
836    /// 2. If the deadline is due, resolve expiry atomically against the registry
837    ///    (`expire_slot`) and return the terminal outcome promptly — even when
838    ///    the caller's quantum has time left (the quantum is a max wait).
839    /// 3. Otherwise wait for the earlier of quantum-remaining and deadline; a
840    ///    wake re-runs 1-2, and an exhausted quantum before the deadline is the
841    ///    benign `PushReplyTimeout` re-arm with the slot untouched.
842    fn receive_deadlined(
843        &self,
844        timeout: Duration,
845        deadline: Instant,
846    ) -> Result<Vec<u8>, ServerError> {
847        let started = Instant::now();
848        loop {
849            if let Some(result) = self.try_take_reply() {
850                return result;
851            }
852            let now = Instant::now();
853            if now >= deadline {
854                return self.expire_slot();
855            }
856            let quantum_left = timeout.saturating_sub(now.duration_since(started));
857            if quantum_left.is_zero() {
858                return Err(ServerError::PushReplyTimeout {
859                    correlation_id: self.correlation_id,
860                });
861            }
862            match self
863                .receiver
864                .recv_timeout(quantum_left.min(deadline.duration_since(now)))
865            {
866                Ok(payload) => return Ok(payload),
867                Err(RecvTimeoutError::Disconnected) => {
868                    return Err(ServerError::PushReplyDisconnected {
869                        correlation_id: self.correlation_id,
870                    });
871                }
872                // Re-loop: deliver a reply that raced the wake, expire a
873                // now-due deadline, or report the exhausted quantum benignly.
874                Err(RecvTimeoutError::Timeout) => {}
875            }
876        }
877    }
878
879    /// Resolves a due deadline against the registry's atomic removal transition.
880    fn expire_slot(&self) -> Result<Vec<u8>, ServerError> {
881        let timeout_error = || ServerError::PushReplyTimeout {
882            correlation_id: self.correlation_id,
883        };
884        let Some(runtime) = self.runtime.upgrade() else {
885            // The runtime is gone, and the slot map (with every sender) with it:
886            // the connection side is torn down. Re-check the channel so the
887            // dropped sender reads as the established DISCONNECTED outcome — a
888            // dead runtime must not be misreported as a benign healthy-but-slow
889            // timeout (S4).
890            return self
891                .try_take_reply()
892                .unwrap_or(Err(ServerError::PushReplyDisconnected {
893                    correlation_id: self.correlation_id,
894                }));
895        };
896        match runtime.expire_push_if_due(self.correlation_id) {
897            PushSlotDisposition::Expired => Err(ServerError::PushReplyExpired {
898                correlation_id: self.correlation_id,
899            }),
900            // Unreachable by construction (this is only called with the deadline
901            // due, and the registry re-reads a monotonic clock); honest benign
902            // fallback rather than a panic.
903            PushSlotDisposition::Live => Err(timeout_error()),
904            // Another path (a concurrent `resolve_push`, or connection close)
905            // removed the slot under the registry lock while we waited on it. Its
906            // send, if any, happens under that same lock, so re-check the channel:
907            // a delivered reply is present now; a dropped sender is disconnected.
908            PushSlotDisposition::Absent => self
909                .try_take_reply()
910                .unwrap_or_else(|| Err(timeout_error())),
911        }
912    }
913
914    /// Non-blocking check for a reply already sitting in the channel. `Some` with
915    /// the payload or a disconnected error; `None` when the channel is still empty
916    /// (no reply yet — the caller re-arms).
917    fn try_take_reply(&self) -> Option<Result<Vec<u8>, ServerError>> {
918        match self.receiver.try_recv() {
919            Ok(payload) => Some(Ok(payload)),
920            Err(TryRecvError::Disconnected) => Some(Err(ServerError::PushReplyDisconnected {
921                correlation_id: self.correlation_id,
922            })),
923            Err(TryRecvError::Empty) => None,
924        }
925    }
926}
927
928/// The kernel-parked exit-event reactor (W4 leg 1 reclamation carve-out, §4.1).
929/// It is the single TOLD source that reclaims connection host records for
930/// processes that exit WITHOUT a final handler slice, replacing the retired
931/// per-accept `reap_crashed` scan for that class.
932///
933/// It blocks on beamr's sole exit-event subscription — never polling, never
934/// timed — and on each delivered [`ExitEvent::Exited`] it (1) drains the
935/// retained additive outcome so beamr's exactly-once outcome store stays bounded
936/// (we are the sole subscriber and therefore the sole drainer) and (2) reclaims
937/// the pid through [`ConnectionRuntime::reclaim_terminated`], which funnels into
938/// `remove()`. On the bounded queue's [`ExitEvent::Lagged`] overflow marker it
939/// runs exactly one reconciliation pass over the tracked records (beamr's
940/// documented recovery), driven by that one TELL — not a timer.
941///
942/// It holds WEAK handles to both the scheduler and the runtime and upgrades them
943/// per event, so it never keeps either alive past supervisor drop. It returns
944/// when the subscription disconnects (scheduler and publisher dropped) OR when a
945/// per-event upgrade fails — both observed at an event delivery, never sampled,
946/// so there is no stop flag (LAW-1). The runtime and its durable store therefore
947/// release synchronously at supervisor drop rather than after the reactor exits.
948fn run_reclaim_reactor(
949    subscription: &ExitEventSubscription,
950    scheduler: &Weak<Scheduler>,
951    runtime: &Weak<ConnectionRuntime>,
952) {
953    loop {
954        match subscription.recv() {
955            Ok(ExitEvent::Exited { pid, reason }) => {
956                let Some(runtime) = runtime.upgrade() else {
957                    return;
958                };
959                runtime.deliver_reclamation(scheduler, pid, reason);
960            }
961            Ok(ExitEvent::Lagged) => {
962                let (Some(runtime), Some(scheduler)) = (runtime.upgrade(), scheduler.upgrade())
963                else {
964                    return;
965                };
966                runtime.reap_crashed(&scheduler);
967            }
968            Err(_) => return,
969        }
970    }
971}
972
973pub(super) struct SupervisorInner {
974    scheduler: Arc<Scheduler>,
975    runtime: Arc<ConnectionRuntime>,
976    incarnations: Option<Arc<ConnectionIncarnationAuthority>>,
977}
978
979impl std::fmt::Debug for SupervisorInner {
980    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
981        formatter
982            .debug_struct("SupervisorInner")
983            .field("runtime", &self.runtime)
984            .finish_non_exhaustive()
985    }
986}
987
988impl SupervisorInner {
989    fn new(
990        services: Arc<dyn ConnectionServices>,
991        notifier: Option<Arc<dyn ConnectionNotifier>>,
992        auth_token: Option<Vec<u8>>,
993        limits: LimitsConfig,
994        fatal_shutdown: Option<ShutdownHandle>,
995    ) -> Result<Self, ServerError> {
996        let installed_services = ConnectionServiceInstallation::capture(services);
997        let incarnations = installed_services
998            .participant_service
999            .as_ref()
1000            .map(
1001                |service| -> Result<Arc<ConnectionIncarnationAuthority>, ServerError> {
1002                    ConnectionIncarnationAuthority::startup(
1003                        service.durable_store(),
1004                        limits.max_connections,
1005                        service.publication_conversation_limit(),
1006                        service,
1007                    )
1008                    .map(Arc::new)
1009                },
1010            )
1011            .transpose()?;
1012        let atoms = AtomTable::with_common_atoms();
1013        let control_atom = atoms.intern(CONNECTION_SHUTDOWN_CONTROL_ATOM);
1014        let registry = Arc::new(ModuleRegistry::new());
1015
1016        let scheduler = Scheduler::with_services(
1017            SchedulerConfig {
1018                thread_count: Some(CONNECTION_SCHEDULER_THREADS),
1019                ..SchedulerConfig::default()
1020            },
1021            SchedulerServices::from_config().owned_readiness(),
1022            registry,
1023        )
1024        .map_err(|message| ServerError::ListenerAccept {
1025            message: format!("failed to start connection scheduler: {message}"),
1026        })?;
1027        let ready_atom = atoms.intern(CONNECTION_READY_ATOM);
1028        let scheduler = Arc::new(scheduler);
1029        // The runtime captures a WEAK handle to the connection scheduler so
1030        // notifier wakes (R3/R1(vi)) can be fired from another actor's slice
1031        // without a strong scheduler↔process↔runtime cycle that would leak the
1032        // whole connection scheduler.
1033        let runtime = Arc::new(ConnectionRuntime::new(
1034            ConnectionRuntimeInstallation {
1035                services: installed_services,
1036                incarnations: incarnations.clone(),
1037                fatal_shutdown,
1038            },
1039            control_atom,
1040            ready_atom,
1041            Arc::downgrade(&scheduler),
1042            notifier,
1043            auth_token,
1044            limits,
1045        ));
1046        // W4 leg 1 reclamation carve-out (§4.1): the kernel-parked exit-event
1047        // reactor is the TOLD source that reclaims a connection host record whose
1048        // process exited WITHOUT a final handler slice (external/panic
1049        // termination). It blocks on beamr's single exit-event subscription —
1050        // never a poll — and routes every reclamation through the same `remove()`
1051        // funnel as an ordinary exit. Detached on purpose: it exits when the
1052        // scheduler (and so its event publisher) drops, so there is no stop flag
1053        // to sample (LAW-1).
1054        match scheduler.subscribe_exit_events() {
1055            Some(subscription) => {
1056                let reactor_scheduler = Arc::downgrade(&scheduler);
1057                // WEAK, symmetric with the scheduler handle: the reactor must not
1058                // keep the runtime (and its durable store's writer lock) alive past
1059                // supervisor drop. It upgrades per event and exits on a failed
1060                // upgrade, so the runtime is released synchronously at drop.
1061                let reactor_runtime = Arc::downgrade(&runtime);
1062                thread::Builder::new()
1063                    .name("liminal-connection-reclaim".to_owned())
1064                    .spawn(move || {
1065                        run_reclaim_reactor(&subscription, &reactor_scheduler, &reactor_runtime);
1066                    })
1067                    .map_err(|error| ServerError::ListenerAccept {
1068                        message: format!("failed to start connection reclamation reactor: {error}"),
1069                    })?;
1070            }
1071            None => {
1072                tracing::error!(
1073                    "connection scheduler exit-event subscription unavailable; \
1074                     external-termination reclamation has no TOLD exit source (the \
1075                     shutdown-drain scan that once backstopped it was retired by W4 leg 3)"
1076                );
1077            }
1078        }
1079        Ok(Self {
1080            scheduler,
1081            runtime,
1082            incarnations,
1083        })
1084    }
1085
1086    fn spawn_connection(
1087        self: &Arc<Self>,
1088        stream: TcpStream,
1089    ) -> Result<ConnectionHandle, ServerError> {
1090        // §5 `max_connections`: ATOMIC admission reservation acquired BEFORE any
1091        // process construction (review round 1 item 7 — a signed bound must not
1092        // be exceedable by concurrent callers; check-then-spawn across an
1093        // unlocked window was). The CAS reservation is released on every failure
1094        // path below and converts into the connection record at `register`;
1095        // thereafter the single record-removal path (`remove`) releases it. An
1096        // over-cap accept therefore costs nothing and the bound holds under any
1097        // concurrency.
1098        self.runtime.try_reserve_admission()?;
1099        let reservation = AdmissionReservation {
1100            runtime: &self.runtime,
1101            armed: true,
1102        };
1103        stream
1104            .set_nonblocking(true)
1105            .map_err(|error| ServerError::ListenerAccept {
1106                message: format!("failed to configure connection stream: {error}"),
1107            })?;
1108        let peer_addr = stream.peer_addr().ok();
1109        // The host-held duplicate keeps the fd alive until the single record-removal
1110        // path has synchronously deregistered readiness. External process death can
1111        // therefore never let fd reuse overtake host-side deregistration.
1112        let fd_guard = stream
1113            .try_clone()
1114            .map_err(|error| ServerError::ListenerAccept {
1115                message: format!("failed to retain connection fd for teardown: {error}"),
1116            })?;
1117        let connection_incarnation = self.allocate_connection_incarnation()?;
1118        let holder = Arc::new(Mutex::new(Some(stream)));
1119        let runtime = Arc::clone(&self.runtime);
1120        let process_holder = Arc::clone(&holder);
1121        let factory: NativeHandlerFactory = Box::new(move || {
1122            Box::new(ConnectionProcess::from_holder(
1123                Arc::clone(&runtime),
1124                peer_addr,
1125                &process_holder,
1126                connection_incarnation,
1127            ))
1128        });
1129        let pid =
1130            self.scheduler
1131                .spawn_native(factory)
1132                .map_err(|error| ServerError::ListenerAccept {
1133                    message: format!("failed to spawn connection process: {error}"),
1134                })?;
1135        if let Err(error) =
1136            self.runtime
1137                .register_with_fd(pid, peer_addr, connection_incarnation, fd_guard)
1138        {
1139            // Registration failure leaves no host record to reap. Terminate the
1140            // just-spawned process explicitly so neither its stream nor admission
1141            // reservation can escape this failed spawn.
1142            self.scheduler.terminate_process(pid, ExitReason::Error);
1143            return Err(error);
1144        }
1145        // The reservation is now owned by the registered record: `remove` (the
1146        // single record-removal path — finish/mark_crashed/reap all funnel
1147        // through it) releases the admission when the record goes away.
1148        reservation.convert();
1149        Ok(ConnectionHandle {
1150            pid,
1151            peer_addr,
1152            connection_incarnation,
1153            supervisor: Arc::clone(self),
1154        })
1155    }
1156
1157    /// LP-WS-TRANSPORT R1.3: the sibling-transport spawn body. Mirrors
1158    /// [`Self::spawn_connection`]'s admission → incarnation → spawn → register →
1159    /// convert sequence exactly (same reservation guard, same failure rollback,
1160    /// same single record-removal ownership), differing only in that the caller
1161    /// supplies the native handler factory and the host-held fd guard instead of
1162    /// a raw `TcpStream`. Purely additive; the TCP path never calls this.
1163    fn spawn_transport_connection(
1164        self: &Arc<Self>,
1165        peer_addr: Option<SocketAddr>,
1166        fd_guard: TcpStream,
1167        build_factory: &dyn Fn(
1168            Arc<ConnectionRuntime>,
1169            Option<ConnectionIncarnation>,
1170        ) -> NativeHandlerFactory,
1171    ) -> Result<ConnectionHandle, ServerError> {
1172        self.runtime.try_reserve_admission()?;
1173        let reservation = AdmissionReservation {
1174            runtime: &self.runtime,
1175            armed: true,
1176        };
1177        let connection_incarnation = self.allocate_connection_incarnation()?;
1178        let factory = build_factory(Arc::clone(&self.runtime), connection_incarnation);
1179        let pid =
1180            self.scheduler
1181                .spawn_native(factory)
1182                .map_err(|error| ServerError::ListenerAccept {
1183                    message: format!("failed to spawn connection process: {error}"),
1184                })?;
1185        if let Err(error) =
1186            self.runtime
1187                .register_with_fd(pid, peer_addr, connection_incarnation, fd_guard)
1188        {
1189            self.scheduler.terminate_process(pid, ExitReason::Error);
1190            return Err(error);
1191        }
1192        reservation.convert();
1193        Ok(ConnectionHandle {
1194            pid,
1195            peer_addr,
1196            connection_incarnation,
1197            supervisor: Arc::clone(self),
1198        })
1199    }
1200
1201    fn allocate_connection_incarnation(
1202        &self,
1203    ) -> Result<Option<ConnectionIncarnation>, ServerError> {
1204        let Some(authority) = self.incarnations.as_ref() else {
1205            return Ok(None);
1206        };
1207        // Production-era uniqueness invariant: every published incarnation is
1208        // unique against ALL durable references — binding epochs committed
1209        // into conversation logs included — by allocator-log monotonicity
1210        // alone, not by the completeness of the reference set below.
1211        //
1212        //   1. Startup replays the durable allocator stream and STRICTLY
1213        //      increments the server incarnation, fsyncing the Startup event
1214        //      before any listener becomes ready
1215        //      (`IncarnationStream::startup`); a server value is never wrapped
1216        //      or reused, so no two process lifetimes share one.
1217        //   2. Within a lifetime, allocations are serialized under this
1218        //      authority's mutex, candidates start strictly above the durable
1219        //      `last_examined_connection_ordinal`
1220        //      (`allocate_connection_incarnation`), and every allocation's
1221        //      event is appended and flushed BEFORE its pair is published
1222        //      (`StartedIncarnationStream::allocate`), so ordinals never
1223        //      repeat within a lifetime and replay restores a head at or
1224        //      above every published ordinal.
1225        //   3. A durable reference can only name a pair this allocator
1226        //      previously PUBLISHED (binding epochs are committed only after
1227        //      their connection was admitted), and the same store's flush
1228        //      barrier orders the allocator event before any conversation-log
1229        //      entry that references it.
1230        //
1231        // The live-connection reference set below is therefore defense in
1232        // depth — a bounded collision skip against a rolled-back or divergent
1233        // allocator stream — never the uniqueness foundation, and never a raw
1234        // caller-supplied matrix.
1235        let references = self.runtime.complete_active_incarnation_references()?;
1236        authority.allocate(&references).map(Some)
1237    }
1238
1239    fn broadcast_control(&self, control: &ConnectionControl) {
1240        for connection in self.runtime.active_connections() {
1241            if !self.enqueue_control(connection.pid, control.clone()) {
1242                tracing::debug!(
1243                    connection_pid = connection.pid,
1244                    peer_addr = ?connection.peer_addr,
1245                    ?control,
1246                    "connection control message skipped because process is not live"
1247                );
1248            }
1249        }
1250    }
1251
1252    /// Queues `control` for `pid` and wakes the process. Returns whether the
1253    /// control was PUBLISHED (left in the queue with a successful wake, or
1254    /// already consumed by a drain) — `false` guarantees no consumer ever saw
1255    /// it.
1256    ///
1257    /// S8: a failed wake does NOT prove the queued control was never consumed.
1258    /// The insert releases the queue lock before the wake attempt, and a
1259    /// process already executing a control drain (each control atom drains ALL
1260    /// queued controls for the pid) can pop the just-inserted entry in that
1261    /// window, then exit before the wake check. Publication is therefore
1262    /// disambiguated BY OBSERVATION on the failed-wake path: `remove_control`
1263    /// finding and removing the entry proves no consumer saw it (truly
1264    /// unpublished — `false`); finding nothing proves a drain consumed it
1265    /// (`pop_control` is the only other remover of queue entries, and the
1266    /// removal key embeds the push's runtime-unique correlation id, so it can
1267    /// never match a different entry) — the control was published and the
1268    /// caller's slot lifecycle carries the delivery truth (`true`).
1269    fn enqueue_control(&self, pid: u64, control: ConnectionControl) -> bool {
1270        // Keep a key for the failure-path removal before the control is moved into
1271        // the queue, so a non-`Copy` (push) control can still be located and pulled
1272        // back out if the scheduler wakeup fails.
1273        let removal_key = control.clone();
1274        if self.runtime.push_control(pid, control).is_err() {
1275            return false;
1276        }
1277        // Deterministic test seam in the insert->wake window (S8 staging).
1278        #[cfg(test)]
1279        self.runtime.run_pre_wake_barrier();
1280        if self
1281            .scheduler
1282            .enqueue_atom_message(pid, self.runtime.control_atom())
1283        {
1284            true
1285        } else {
1286            // Failed wake: the entry's fate is the publication verdict. Removed
1287            // here => nobody consumed it => unpublished. Already gone => a
1288            // drain consumed it before the wake check => published. (A poisoned
1289            // queue lock reads as not-removed => published — the safe
1290            // direction: the slot lifecycle then reports the truthful outcome,
1291            // whereas claiming "unpublished" could be a lie.)
1292            !self.runtime.remove_control(pid, &removal_key)
1293        }
1294    }
1295}
1296
1297/// RAII guard for one §5 `max_connections` admission reservation.
1298///
1299/// Acquired (via [`ConnectionRuntime::try_reserve_admission`]) before any process
1300/// construction in `spawn_connection`; every early-return failure path releases
1301/// it through `Drop`, and a successful `register` converts it into the
1302/// connection record (whose removal releases the admission instead). RAII means
1303/// no failure path — present or future — can leak a reservation.
1304struct AdmissionReservation<'a> {
1305    runtime: &'a ConnectionRuntime,
1306    armed: bool,
1307}
1308
1309impl AdmissionReservation<'_> {
1310    /// Converts the reservation into record ownership: `Drop` no longer releases
1311    /// it, because the registered record's removal will.
1312    fn convert(mut self) {
1313        self.armed = false;
1314    }
1315}
1316
1317impl Drop for AdmissionReservation<'_> {
1318    fn drop(&mut self) {
1319        if self.armed {
1320            self.runtime.release_admission();
1321        }
1322    }
1323}
1324
1325#[derive(Debug, Clone, PartialEq, Eq)]
1326pub(super) enum ConnectionControl {
1327    NotifyShutdown,
1328    ForceClose,
1329    /// Server-initiated push of an opaque payload, correlated by `correlation_id`,
1330    /// to be written out as a [`Frame::Push`] by the receiving connection process.
1331    Push {
1332        correlation_id: u64,
1333        payload: Vec<u8>,
1334    },
1335}
1336
1337#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1338pub struct ActiveConnection {
1339    pid: u64,
1340    peer_addr: Option<SocketAddr>,
1341}
1342
1343#[cfg(test)]
1344#[derive(Debug, Clone)]
1345struct PreWaitBarrier {
1346    armed: Arc<Barrier>,
1347    release: Arc<Barrier>,
1348}
1349
1350/// Pid-specific, one-use deterministic gate on the reclamation delivery path
1351/// (oracle 26). The exit-event reactor stages it only for the targeted pid, so
1352/// unrelated exits still reclaim immediately; for the target it rendezvouses
1353/// `reached` (proving the pid is dead but its record still tracked — the S8
1354/// reclamation window), then `release` (the harness lets the reclaim proceed),
1355/// then `done` (the `remove()` funnel completed). It changes no production
1356/// semantics — the whole type and its call sites are `#[cfg(test)]`.
1357#[cfg(test)]
1358#[derive(Debug, Clone)]
1359struct ReclaimBarrier {
1360    pid: u64,
1361    reached: Arc<Barrier>,
1362    release: Arc<Barrier>,
1363    done: Arc<Barrier>,
1364}
1365
1366#[derive(Debug)]
1367struct ConnectionServiceInstallation {
1368    services: Arc<dyn ConnectionServices>,
1369    participant_service: Option<InstalledParticipantService>,
1370}
1371
1372impl ConnectionServiceInstallation {
1373    /// Captures the service adapter's capability posture exactly once, before
1374    /// participant incarnation startup or connection process construction.
1375    fn capture(services: Arc<dyn ConnectionServices>) -> Self {
1376        let participant_service = services.participant_service();
1377        Self {
1378            services,
1379            participant_service,
1380        }
1381    }
1382}
1383
1384#[derive(Debug)]
1385struct ConnectionRuntimeInstallation {
1386    services: ConnectionServiceInstallation,
1387    incarnations: Option<Arc<ConnectionIncarnationAuthority>>,
1388    fatal_shutdown: Option<ShutdownHandle>,
1389}
1390
1391#[derive(Debug)]
1392pub(super) struct ConnectionRuntime {
1393    services: Arc<dyn ConnectionServices>,
1394    /// Complete participant handler/store bundle captured at supervisor startup.
1395    /// `Some` is paired with an incarnation authority on `SupervisorInner`.
1396    participant_service: Option<InstalledParticipantService>,
1397    /// Same started authority used for allocation, shared for terminal Open/Complete.
1398    incarnations: Option<Arc<ConnectionIncarnationAuthority>>,
1399    /// Existing runtime shutdown activation notified by the first post-Open fatal.
1400    /// Test-only/runtime-less constructors intentionally carry `None`.
1401    fatal_shutdown: Option<ShutdownHandle>,
1402    records: Mutex<HashMap<u64, ConnectionRecord>>,
1403    controls: Mutex<Vec<QueuedConnectionControl>>,
1404    control_atom: Atom,
1405    /// R6 single `READY` wake atom for this connection scheduler. Fired by every
1406    /// wake source's notifier (R3/R1(vi)); coalescing and duplicates are harmless.
1407    ready_atom: Atom,
1408    /// Weak handle to the connection scheduler, used to build [`ReadyWaker`]s a
1409    /// notifier fires from another actor's slice. Weak so it never keeps the
1410    /// scheduler alive (the scheduler owns the processes that own this runtime).
1411    scheduler: Weak<Scheduler>,
1412    /// W4 leg 3 (§4.3) TOLD drain-completion primitive. Reuses the
1413    /// [`ShutdownHandle`] `Condvar` shape (`shutdown.rs` reuse candidate (c)): a
1414    /// monotonic connection-removal generation guarded by [`Self::drain_removed`]'s
1415    /// mutex, bumped once whenever [`Self::remove`] actually drops a record — the
1416    /// single removal funnel every exit route (in-slice `mark_crashed`/`finish`,
1417    /// the reclaim reactor, and the reconciliation scan) reaches. The
1418    /// shutdown-sequence drain/settle waiter parks on the `Condvar` and wakes only
1419    /// on a delivered exit (a generation bump + `notify_all`) or the one admitted
1420    /// deadline it passes to `wait_timeout`. No periodic reap or count scan.
1421    drain_generation: Mutex<u64>,
1422    /// Woken on every connection-record removal; the drain/settle waiter parks
1423    /// here. Paired with [`Self::drain_generation`] under the same mutex so an
1424    /// exit delivered between the waiter's arm-before-observe snapshot and its
1425    /// park cannot be lost (oracle 18).
1426    drain_removed: Condvar,
1427    /// FIX A-ii shutdown flush barrier: a delivery-quiescence generation of the
1428    /// exact TOLD `drain_generation` shape. Bumped whenever a connection parks
1429    /// with every accepted publish already fanned out to its socket — but only
1430    /// while [`Self::settle_armed`] is set (the flush barrier is waiting) — so
1431    /// normal operation pays nothing. Guards [`Self::settle_changed`]'s mutex.
1432    settle_generation: Mutex<u64>,
1433    /// Woken when a connection reaches delivery quiescence during shutdown; the
1434    /// flush barrier parks here, paired with [`Self::settle_generation`] for the
1435    /// same arm-before-observe safety as the drain waiter.
1436    settle_changed: Condvar,
1437    /// Set only while the flush barrier is actively waiting, so a park bumps the
1438    /// settle generation and wakes the barrier ONLY when someone is listening.
1439    settle_armed: AtomicBool,
1440    /// Test-only count of drain waiter wakes that observed a real removal
1441    /// (generation advanced across the park). A quiet drain records zero — it
1442    /// wakes only for the single deadline (oracle 12).
1443    #[cfg(test)]
1444    drain_exit_wakes: AtomicU64,
1445    /// Test-only count of drain waiter deadline expirations. A quiet drain that
1446    /// times out records exactly one — one arming, one delivery, no helper tick
1447    /// (oracles 12, 16).
1448    #[cfg(test)]
1449    drain_deadline_hits: AtomicU64,
1450    /// Test-only one-use gate in the drain waiter's observe->park window, so a
1451    /// harness can deliver an exit strictly after the completion observation and
1452    /// before the park to pin the arm-before-observe barrier (oracles 18, 19).
1453    #[cfg(test)]
1454    drain_park_barrier: Mutex<Option<PreWaitBarrier>>,
1455    /// R7 (§1.2(6)) test-only per-connection slice counter, keyed by pid. Bumped
1456    /// once at the head of every serviced slice. The park-flip's permanent rule-1
1457    /// assertion (a parked connection's counter must not advance without an event)
1458    /// reads this; the instrument lands now with a test proving it counts slices.
1459    #[cfg(test)]
1460    slice_counts: Mutex<HashMap<u64, u64>>,
1461    /// One-use readiness markers for the next serviced slice of a process.
1462    #[cfg(test)]
1463    slice_observers: Mutex<HashMap<u64, Sender<u64>>>,
1464    /// One-use readiness markers emitted only after the real final probe selects
1465    /// `Wait`, immediately before the native process returns to the scheduler.
1466    #[cfg(test)]
1467    park_observers: Mutex<HashMap<u64, Sender<u64>>>,
1468    /// Most recent slice count whose real final probe selected `Wait`.
1469    #[cfg(test)]
1470    park_counts: Mutex<HashMap<u64, u64>>,
1471    /// Explicit capacities consumed in TCP process construction order.
1472    #[cfg(test)]
1473    next_outbound_capacities: Mutex<VecDeque<usize>>,
1474    #[cfg(test)]
1475    participant_holdback_pauses: Mutex<HashMap<u64, Sender<()>>>,
1476    /// Deterministic test gate placed after arm and before the final probe.
1477    #[cfg(test)]
1478    pre_wait_barrier: Mutex<Option<PreWaitBarrier>>,
1479    /// Deterministic test gate in `enqueue_control`'s insert->wake window (S8).
1480    #[cfg(test)]
1481    pre_wake_barrier: Mutex<Option<PreWaitBarrier>>,
1482    /// Pid-specific one-use gate held on the reclamation delivery path so a test
1483    /// can pin the dead-but-tracked S8 window deterministically (oracle 26).
1484    #[cfg(test)]
1485    reclaim_barrier: Mutex<Option<ReclaimBarrier>>,
1486    /// Barrier-staged slices where the final probe found newly arrived work.
1487    #[cfg(test)]
1488    pre_wait_probe_hits: AtomicU64,
1489    /// One-use observers for process-owned streams reaching their actual drop
1490    /// boundary after external scheduler termination.
1491    #[cfg(test)]
1492    process_stream_drop_observers: Mutex<HashMap<RawFd, Sender<()>>>,
1493    /// One-shot reply slots for in-flight server pushes, keyed by correlation id.
1494    /// The supervisor registers a slot in `push_to_connection`; the connection
1495    /// process resolves it when the matching `PushReply` frame arrives. Each slot
1496    /// records the owning connection pid so the close path can drop a connection's
1497    /// outstanding slots and wake their awaiters with a prompt disconnected error.
1498    push_replies: Mutex<HashMap<u64, PendingPush>>,
1499    /// Monotonic source of push correlation ids. Server-allocated, so it never
1500    /// collides with a client-chosen id on this connection.
1501    next_push_id: AtomicU64,
1502    /// §5 `max_connections` admission counter. Incremented atomically (CAS
1503    /// against the limit) BEFORE a connection process is constructed and
1504    /// decremented on every spawn-failure path and on final record removal, so
1505    /// the signed bound holds under concurrent spawns — admission is never
1506    /// derived from the records-map length across an unlocked window.
1507    admissions: AtomicU64,
1508    /// Optional application hook invoked on worker registration and on the close
1509    /// of a connection that had registered. `None` keeps liminal standalone: a
1510    /// `WorkerRegister` is accepted with no callback.
1511    notifier: Option<Arc<dyn ConnectionNotifier>>,
1512    /// Configured connection auth token (the `[auth]` section's token as opaque
1513    /// bytes). `Some` gates the `Connect` handshake — the frame's `auth_token` must
1514    /// match under a constant-time comparison; `None` leaves the server open-access,
1515    /// byte-identical to the pre-auth behaviour.
1516    auth_token: Option<Vec<u8>>,
1517    /// Operational caps (§5). Enforced with typed refusals at admission:
1518    /// per-connection subscription, conversation, push, and pending-reply counts,
1519    /// plus the shared inbox byte budget. Non-config constructors carry the signed
1520    /// defaults ([`LimitsConfig::default`]).
1521    limits: LimitsConfig,
1522}
1523
1524impl ConnectionRuntime {
1525    fn new(
1526        installation: ConnectionRuntimeInstallation,
1527        control_atom: Atom,
1528        ready_atom: Atom,
1529        scheduler: Weak<Scheduler>,
1530        notifier: Option<Arc<dyn ConnectionNotifier>>,
1531        auth_token: Option<Vec<u8>>,
1532        limits: LimitsConfig,
1533    ) -> Self {
1534        let ConnectionRuntimeInstallation {
1535            services:
1536                ConnectionServiceInstallation {
1537                    services,
1538                    participant_service,
1539                },
1540            incarnations,
1541            fatal_shutdown,
1542        } = installation;
1543        Self {
1544            services,
1545            participant_service,
1546            incarnations,
1547            fatal_shutdown,
1548            records: Mutex::new(HashMap::new()),
1549            controls: Mutex::new(Vec::new()),
1550            control_atom,
1551            ready_atom,
1552            scheduler,
1553            drain_generation: Mutex::new(0),
1554            drain_removed: Condvar::new(),
1555            settle_generation: Mutex::new(0),
1556            settle_changed: Condvar::new(),
1557            settle_armed: AtomicBool::new(false),
1558            #[cfg(test)]
1559            drain_exit_wakes: AtomicU64::new(0),
1560            #[cfg(test)]
1561            drain_deadline_hits: AtomicU64::new(0),
1562            #[cfg(test)]
1563            drain_park_barrier: Mutex::new(None),
1564            #[cfg(test)]
1565            slice_counts: Mutex::new(HashMap::new()),
1566            #[cfg(test)]
1567            slice_observers: Mutex::new(HashMap::new()),
1568            #[cfg(test)]
1569            park_observers: Mutex::new(HashMap::new()),
1570            #[cfg(test)]
1571            park_counts: Mutex::new(HashMap::new()),
1572            #[cfg(test)]
1573            next_outbound_capacities: Mutex::new(VecDeque::new()),
1574            #[cfg(test)]
1575            participant_holdback_pauses: Mutex::new(HashMap::new()),
1576            #[cfg(test)]
1577            pre_wait_barrier: Mutex::new(None),
1578            #[cfg(test)]
1579            pre_wake_barrier: Mutex::new(None),
1580            #[cfg(test)]
1581            reclaim_barrier: Mutex::new(None),
1582            #[cfg(test)]
1583            pre_wait_probe_hits: AtomicU64::new(0),
1584            #[cfg(test)]
1585            process_stream_drop_observers: Mutex::new(HashMap::new()),
1586            push_replies: Mutex::new(HashMap::new()),
1587            next_push_id: AtomicU64::new(1),
1588            admissions: AtomicU64::new(0),
1589            notifier,
1590            auth_token,
1591            limits,
1592        }
1593    }
1594
1595    /// Atomically reserves one §5 `max_connections` admission slot: a CAS loop
1596    /// against the configured limit, so N concurrent callers racing for the last
1597    /// slot admit EXACTLY one — the bound cannot be transiently exceeded.
1598    ///
1599    /// # Errors
1600    /// Returns [`ServerError::ConnectionLimitReached`] when every slot is taken.
1601    fn try_reserve_admission(&self) -> Result<(), ServerError> {
1602        self.ensure_participant_service_live()?;
1603        let limit = self.limits.max_connections as u64;
1604        let mut current = self.admissions.load(Ordering::Acquire);
1605        loop {
1606            if current >= limit {
1607                return Err(ServerError::ConnectionLimitReached {
1608                    limit: self.limits.max_connections,
1609                });
1610            }
1611            match self.admissions.compare_exchange_weak(
1612                current,
1613                current + 1,
1614                Ordering::AcqRel,
1615                Ordering::Acquire,
1616            ) {
1617                Ok(_) => return Ok(()),
1618                Err(observed) => current = observed,
1619            }
1620        }
1621    }
1622
1623    /// Releases one admission slot. Called by the spawn failure paths (via the
1624    /// [`AdmissionReservation`] guard) and by [`Self::remove`] when a registered
1625    /// record is removed — exactly one release per reservation. Saturating so a
1626    /// spurious release can never wrap the counter.
1627    fn release_admission(&self) {
1628        let mut current = self.admissions.load(Ordering::Acquire);
1629        loop {
1630            let next = current.saturating_sub(1);
1631            match self.admissions.compare_exchange_weak(
1632                current,
1633                next,
1634                Ordering::AcqRel,
1635                Ordering::Acquire,
1636            ) {
1637                Ok(_) => return,
1638                Err(observed) => current = observed,
1639            }
1640        }
1641    }
1642
1643    /// The operational caps (§5) this runtime enforces.
1644    pub(super) const fn limits(&self) -> &LimitsConfig {
1645        &self.limits
1646    }
1647
1648    /// The connection's single R6 `READY` wake atom.
1649    pub(super) const fn ready_atom(&self) -> Atom {
1650        self.ready_atom
1651    }
1652
1653    /// Builds a [`ReadyWaker`] targeting `pid` on the connection scheduler, if the
1654    /// scheduler is still live. `None` when the scheduler is gone (teardown) or in
1655    /// scheduler-free unit tests — a notifier with no waker simply never wakes,
1656    /// which under the busy loop is redundant anyway (the every-slice pump still
1657    /// services the source). This is the seam every wake source installs its
1658    /// notifier through (R3/R1(vi)).
1659    pub(super) fn ready_waker(&self, pid: u64) -> Option<super::wake::ReadyWaker> {
1660        let scheduler = self.scheduler.upgrade()?;
1661        let ready_pending = self
1662            .records
1663            .lock()
1664            .ok()?
1665            .get(&pid)
1666            .map(|record| Arc::clone(&record.ready_pending))?;
1667        Some(super::wake::ReadyWaker::new(
1668            &scheduler,
1669            pid,
1670            self.ready_atom,
1671            ready_pending,
1672        ))
1673    }
1674
1675    /// Acknowledges READY edges whose mailbox atoms were drained before this slice.
1676    pub(super) fn acknowledge_ready(&self, pid: u64) {
1677        if let Ok(records) = self.records.lock()
1678            && let Some(record) = records.get(&pid)
1679        {
1680            record.ready_pending.store(false, Ordering::Release);
1681        }
1682    }
1683
1684    /// Reports a READY edge queued while the current process snapshot is executing.
1685    pub(super) fn ready_pending(&self, pid: u64) -> bool {
1686        self.records
1687            .lock()
1688            .ok()
1689            .and_then(|records| {
1690                records
1691                    .get(&pid)
1692                    .map(|record| record.ready_pending.load(Ordering::Acquire))
1693            })
1694            .unwrap_or(false)
1695    }
1696
1697    /// FIX A-ii: marks `pid` as executing a slice — not parked, so not yet
1698    /// delivery-quiescent. Reuses the registry lock the slice already takes for
1699    /// `is_registered`; it never touches the barrier condvar.
1700    pub(super) fn mark_running(&self, pid: u64) {
1701        if let Ok(records) = self.records.lock()
1702            && let Some(record) = records.get(&pid)
1703        {
1704            record.parked.store(false, Ordering::Release);
1705        }
1706    }
1707
1708    /// FIX A-ii: marks `pid` as parked with every accepted publish already fanned
1709    /// out to its socket, and — only while the shutdown flush barrier is armed —
1710    /// bumps the settle generation and wakes it. The bump/notify is skipped
1711    /// entirely in normal operation, so a park off the shutdown path is just one
1712    /// flag store.
1713    pub(super) fn mark_parked(&self, pid: u64) {
1714        if let Ok(records) = self.records.lock()
1715            && let Some(record) = records.get(&pid)
1716        {
1717            record.parked.store(true, Ordering::Release);
1718        }
1719        if self.settle_armed.load(Ordering::Acquire) {
1720            self.signal_settle_changed();
1721        }
1722    }
1723
1724    /// Bumps the delivery-quiescence generation under its mutex, then wakes the
1725    /// flush barrier — the same lock-then-notify discipline as
1726    /// [`Self::signal_connection_removed`], so a park published before the notify
1727    /// can never be missed by a waiter holding the mutex across its re-check.
1728    fn signal_settle_changed(&self) {
1729        {
1730            let mut generation = recover_lock(&self.settle_generation);
1731            *generation = generation.wrapping_add(1);
1732        }
1733        self.settle_changed.notify_all();
1734    }
1735
1736    /// Reads the current delivery-quiescence generation under its mutex.
1737    fn settle_generation_snapshot(&self) -> u64 {
1738        *recover_lock(&self.settle_generation)
1739    }
1740
1741    /// True when every tracked connection is parked with no pending READY edge —
1742    /// i.e. every accepted publish has been pumped to its subscriber's outbound
1743    /// and no fan-out wake is still in flight. An empty registry is trivially
1744    /// quiescent.
1745    fn all_connections_delivery_quiesced(&self) -> bool {
1746        let Ok(records) = self.records.lock() else {
1747            return false;
1748        };
1749        records.values().all(|record| {
1750            record.parked.load(Ordering::Acquire) && !record.ready_pending.load(Ordering::Acquire)
1751        })
1752    }
1753
1754    /// FIX A-ii: wakes every tracked connection once so it drains its socket and
1755    /// pumps its subscriptions. This is what makes the flush barrier robust to the
1756    /// readiness gap: a publisher whose fire-and-forget publish bytes have arrived
1757    /// but whose readiness wake has not yet rescheduled it still looks "parked",
1758    /// so without this it could be sampled as quiescent before it admits and fans
1759    /// out those publishes. Firing sets each connection's `ready_pending` edge, so
1760    /// the quiescence check below cannot pass until every woken connection has run
1761    /// its slice (reading and admitting any buffered publish, whose admission then
1762    /// fires its subscribers in turn) and re-parked.
1763    fn wake_all_connections_for_flush(&self) {
1764        let pids: Vec<u64> = self
1765            .records
1766            .lock()
1767            .map(|records| records.keys().copied().collect())
1768            .unwrap_or_default();
1769        for pid in pids {
1770            if let Some(waker) = self.ready_waker(pid) {
1771                waker.fire();
1772            }
1773        }
1774    }
1775
1776    /// FIX A-ii: parks until every tracked connection has fanned out its accepted
1777    /// publishes (delivery quiescence) or `deadline` elapses, returning `true` on
1778    /// quiescence and `false` when the single admitted deadline won. The TOLD
1779    /// shape mirrors [`Self::wait_for_active_connections_drained`]: arm the
1780    /// barrier, then snapshot-before-observe so a park delivered between the
1781    /// observation and the wait bumps a generation the wait detects. It samples
1782    /// nothing on a timer — it wakes only on a delivered park (generation bump) or
1783    /// the one deadline.
1784    pub(super) fn wait_for_delivery_quiesced(&self, deadline: Instant) -> bool {
1785        self.settle_armed.store(true, Ordering::Release);
1786        // Force every connection to run once so a publisher whose buffered publish
1787        // bytes have not yet triggered a readiness wake still drains and admits
1788        // them (and fires its subscribers) before the quiescence check can pass.
1789        self.wake_all_connections_for_flush();
1790        let quiesced = loop {
1791            let snapshot = self.settle_generation_snapshot();
1792            if self.all_connections_delivery_quiesced() {
1793                break true;
1794            }
1795            let Some(remaining) = deadline.checked_duration_since(Instant::now()) else {
1796                break false;
1797            };
1798            let outcome = self
1799                .settle_changed
1800                .wait_timeout_while(
1801                    recover_lock(&self.settle_generation),
1802                    remaining,
1803                    |current| *current == snapshot,
1804                )
1805                .unwrap_or_else(PoisonError::into_inner);
1806            drop(outcome);
1807        };
1808        self.settle_armed.store(false, Ordering::Release);
1809        quiesced
1810    }
1811
1812    /// R7: records one serviced slice for `pid`. Bumped at the head of every
1813    /// slice; the park-flip's quiescence assertion reads [`Self::slice_count`].
1814    #[cfg(test)]
1815    pub(super) fn record_slice(&self, pid: u64) {
1816        let count = if let Ok(mut counts) = self.slice_counts.lock() {
1817            let count = counts.entry(pid).or_insert(0);
1818            *count += 1;
1819            *count
1820        } else {
1821            return;
1822        };
1823        if let Ok(mut observers) = self.slice_observers.lock()
1824            && let Some(observer) = observers.remove(&pid)
1825        {
1826            let _ = observer.send(count);
1827        }
1828    }
1829
1830    #[cfg(test)]
1831    fn observe_next_slice(&self, pid: u64) -> Receiver<u64> {
1832        let (sender, receiver) = channel();
1833        if let Ok(mut observers) = self.slice_observers.lock() {
1834            observers.insert(pid, sender);
1835        }
1836        receiver
1837    }
1838
1839    #[cfg(test)]
1840    fn observe_next_park(&self, pid: u64) -> Receiver<u64> {
1841        let (sender, receiver) = channel();
1842        if let Ok(mut observers) = self.park_observers.lock() {
1843            observers.insert(pid, sender);
1844        }
1845        receiver
1846    }
1847
1848    #[cfg(test)]
1849    fn observe_settled_park(&self, pid: u64) -> Receiver<u64> {
1850        let (sender, receiver) = channel();
1851        let Ok(counts) = self.slice_counts.lock() else {
1852            return receiver;
1853        };
1854        let current = counts.get(&pid).copied().unwrap_or(0);
1855        let Ok(parks) = self.park_counts.lock() else {
1856            return receiver;
1857        };
1858        if current > 0 && parks.get(&pid).copied() == Some(current) {
1859            let _ = sender.send(current);
1860        } else if let Ok(mut observers) = self.park_observers.lock() {
1861            observers.insert(pid, sender);
1862        }
1863        drop(parks);
1864        drop(counts);
1865        receiver
1866    }
1867
1868    #[cfg(test)]
1869    pub(super) fn record_park(&self, pid: u64) {
1870        let count = self.slice_count(pid);
1871        if let Ok(mut parks) = self.park_counts.lock() {
1872            parks.insert(pid, count);
1873        }
1874        if let Ok(mut observers) = self.park_observers.lock()
1875            && let Some(observer) = observers.remove(&pid)
1876        {
1877            let _ = observer.send(count);
1878        }
1879    }
1880
1881    #[cfg(test)]
1882    fn queue_next_outbound_capacity(&self, capacity: usize) {
1883        if let Ok(mut capacities) = self.next_outbound_capacities.lock() {
1884            capacities.push_back(capacity);
1885        }
1886    }
1887
1888    #[cfg(test)]
1889    pub(super) fn take_next_outbound_capacity(&self) -> Option<usize> {
1890        self.next_outbound_capacities
1891            .lock()
1892            .ok()
1893            .and_then(|mut capacities| capacities.pop_front())
1894    }
1895
1896    #[cfg(test)]
1897    fn install_participant_holdback_pause(&self, pid: u64) -> Receiver<()> {
1898        let (sender, receiver) = channel();
1899        if let Ok(mut pauses) = self.participant_holdback_pauses.lock() {
1900            pauses.insert(pid, sender);
1901        }
1902        receiver
1903    }
1904
1905    #[cfg(test)]
1906    pub(super) fn pause_participant_holdback(&self, pid: u64) -> bool {
1907        self.participant_holdback_pauses
1908            .lock()
1909            .ok()
1910            .and_then(|mut pauses| pauses.remove(&pid))
1911            .is_some_and(|sender| {
1912                let _ = sender.send(());
1913                true
1914            })
1915    }
1916
1917    /// R7: slices serviced by connection `pid` since spawn (test instrument).
1918    #[cfg(test)]
1919    pub(super) fn slice_count(&self, pid: u64) -> u64 {
1920        self.slice_counts
1921            .lock()
1922            .map_or(0, |counts| counts.get(&pid).copied().unwrap_or(0))
1923    }
1924
1925    #[cfg(test)]
1926    fn install_pre_wait_barrier(&self) -> (Arc<Barrier>, Arc<Barrier>) {
1927        let armed = Arc::new(Barrier::new(2));
1928        let release = Arc::new(Barrier::new(2));
1929        if let Ok(mut slot) = self.pre_wait_barrier.lock() {
1930            *slot = Some(PreWaitBarrier {
1931                armed: Arc::clone(&armed),
1932                release: Arc::clone(&release),
1933            });
1934        }
1935        (armed, release)
1936    }
1937
1938    /// Installs the pid-specific reclamation gate (oracle 26) and returns its
1939    /// `(reached, release, done)` endpoints. The harness rendezvouses `reached`
1940    /// to pin the dead-but-tracked window, `release` to let the reclaim proceed,
1941    /// and `done` to observe the `remove()` funnel completing.
1942    #[cfg(test)]
1943    pub(super) fn install_reclaim_barrier(
1944        &self,
1945        pid: u64,
1946    ) -> (Arc<Barrier>, Arc<Barrier>, Arc<Barrier>) {
1947        let reached = Arc::new(Barrier::new(2));
1948        let release = Arc::new(Barrier::new(2));
1949        let done = Arc::new(Barrier::new(2));
1950        if let Ok(mut slot) = self.reclaim_barrier.lock() {
1951            *slot = Some(ReclaimBarrier {
1952                pid,
1953                reached: Arc::clone(&reached),
1954                release: Arc::clone(&release),
1955                done: Arc::clone(&done),
1956            });
1957        }
1958        (reached, release, done)
1959    }
1960
1961    /// Takes the installed reclamation gate iff it targets `pid` (one-use). Any
1962    /// other pid's delivery is ungated, so unrelated exits reclaim immediately.
1963    #[cfg(test)]
1964    fn stage_reclaim_barrier(&self, pid: u64) -> Option<ReclaimBarrier> {
1965        let mut slot = self.reclaim_barrier.lock().ok()?;
1966        if slot.as_ref().is_some_and(|barrier| barrier.pid == pid) {
1967            slot.take()
1968        } else {
1969            None
1970        }
1971    }
1972
1973    /// Installs the one-use drain-park gate (oracles 18, 19) and returns its
1974    /// `(armed, release)` endpoints. Staged in the drain waiter's observe->park
1975    /// window so a harness can deliver an exit strictly between the completion
1976    /// observation and the park. Entirely `#[cfg(test)]`; changes no production
1977    /// wait semantics.
1978    #[cfg(test)]
1979    pub(super) fn install_drain_park_barrier(&self) -> (Arc<Barrier>, Arc<Barrier>) {
1980        let armed = Arc::new(Barrier::new(2));
1981        let release = Arc::new(Barrier::new(2));
1982        if let Ok(mut slot) = self.drain_park_barrier.lock() {
1983            *slot = Some(PreWaitBarrier {
1984                armed: Arc::clone(&armed),
1985                release: Arc::clone(&release),
1986            });
1987        }
1988        (armed, release)
1989    }
1990
1991    /// Runs the one-use drain-park gate, if installed. One-use so only the staged
1992    /// park rendezvouses; every later park in the same waiter runs ungated.
1993    #[cfg(test)]
1994    fn run_drain_park_barrier(&self) {
1995        let barrier = self
1996            .drain_park_barrier
1997            .lock()
1998            .ok()
1999            .and_then(|mut slot| slot.take());
2000        let Some(barrier) = barrier else {
2001            return;
2002        };
2003        barrier.armed.wait();
2004        barrier.release.wait();
2005    }
2006
2007    /// Test-only count of drain waiter wakes that observed a real removal.
2008    #[cfg(test)]
2009    pub(super) fn drain_exit_wakes(&self) -> u64 {
2010        self.drain_exit_wakes.load(Ordering::SeqCst)
2011    }
2012
2013    /// Test-only count of drain waiter deadline expirations.
2014    #[cfg(test)]
2015    pub(super) fn drain_deadline_hits(&self) -> u64 {
2016        self.drain_deadline_hits.load(Ordering::SeqCst)
2017    }
2018
2019    /// Runs a one-use deterministic test gate after arm. Returns whether the gate
2020    /// was installed so only that staged probe contributes to observability.
2021    #[cfg(test)]
2022    pub(super) fn run_pre_wait_barrier(&self) -> bool {
2023        let barrier = self
2024            .pre_wait_barrier
2025            .lock()
2026            .ok()
2027            .and_then(|mut slot| slot.take());
2028        let Some(barrier) = barrier else {
2029            return false;
2030        };
2031        barrier.armed.wait();
2032        barrier.release.wait();
2033        true
2034    }
2035
2036    /// Installs a one-use barrier in `enqueue_control`'s insert->wake window
2037    /// (S8 staging: lets a test act as the control-drain consumer between the
2038    /// queue insertion and the wake attempt) and returns its test endpoints.
2039    #[cfg(test)]
2040    pub(super) fn install_pre_wake_barrier(&self) -> (Arc<Barrier>, Arc<Barrier>) {
2041        let armed = Arc::new(Barrier::new(2));
2042        let release = Arc::new(Barrier::new(2));
2043        if let Ok(mut slot) = self.pre_wake_barrier.lock() {
2044            *slot = Some(PreWaitBarrier {
2045                armed: Arc::clone(&armed),
2046                release: Arc::clone(&release),
2047            });
2048        }
2049        (armed, release)
2050    }
2051
2052    /// Runs the one-use insert->wake test gate, if installed.
2053    #[cfg(test)]
2054    pub(super) fn run_pre_wake_barrier(&self) {
2055        let barrier = self
2056            .pre_wake_barrier
2057            .lock()
2058            .ok()
2059            .and_then(|mut slot| slot.take());
2060        if let Some(barrier) = barrier {
2061            barrier.armed.wait();
2062            barrier.release.wait();
2063        }
2064    }
2065
2066    #[cfg(test)]
2067    pub(super) fn record_pre_wait_probe_hit(&self) {
2068        self.pre_wait_probe_hits.fetch_add(1, Ordering::AcqRel);
2069    }
2070
2071    #[cfg(test)]
2072    fn pre_wait_probe_hits(&self) -> u64 {
2073        self.pre_wait_probe_hits.load(Ordering::Acquire)
2074    }
2075
2076    #[cfg(test)]
2077    fn observe_process_stream_drop(&self, fd: RawFd) -> Receiver<()> {
2078        let (sender, receiver) = channel();
2079        if let Ok(mut observers) = self.process_stream_drop_observers.lock() {
2080            observers.insert(fd, sender);
2081        }
2082        receiver
2083    }
2084
2085    /// Publishes the process-owned stream's real drop boundary to a waiting test.
2086    #[cfg(test)]
2087    pub(super) fn record_process_stream_drop(&self, fd: RawFd) {
2088        let observer = self
2089            .process_stream_drop_observers
2090            .lock()
2091            .ok()
2092            .and_then(|mut observers| observers.remove(&fd));
2093        if let Some(observer) = observer {
2094            let _ = observer.send(());
2095        }
2096    }
2097
2098    /// Builds a runtime wrapping `services` for unit tests that exercise
2099    /// `apply_frame` without a live scheduler. Uses a fresh interned control atom
2100    /// and no notifier.
2101    #[cfg(test)]
2102    pub(super) fn for_tests(services: Arc<dyn ConnectionServices>) -> Self {
2103        let atoms = AtomTable::with_common_atoms();
2104        let control_atom = atoms.intern(CONNECTION_SHUTDOWN_CONTROL_ATOM);
2105        let ready_atom = atoms.intern(CONNECTION_READY_ATOM);
2106        Self::new(
2107            ConnectionRuntimeInstallation {
2108                services: ConnectionServiceInstallation::capture(services),
2109                incarnations: None,
2110                fatal_shutdown: None,
2111            },
2112            control_atom,
2113            ready_atom,
2114            Weak::new(),
2115            None,
2116            None,
2117            LimitsConfig::default(),
2118        )
2119    }
2120
2121    /// Builds a runtime wrapping `services` with explicit `limits` for unit tests
2122    /// that exercise the §5 admission caps without a live scheduler.
2123    #[cfg(test)]
2124    pub(super) fn for_tests_with_limits(
2125        services: Arc<dyn ConnectionServices>,
2126        limits: LimitsConfig,
2127    ) -> Self {
2128        let atoms = AtomTable::with_common_atoms();
2129        let control_atom = atoms.intern(CONNECTION_SHUTDOWN_CONTROL_ATOM);
2130        let ready_atom = atoms.intern(CONNECTION_READY_ATOM);
2131        Self::new(
2132            ConnectionRuntimeInstallation {
2133                services: ConnectionServiceInstallation::capture(services),
2134                incarnations: None,
2135                fatal_shutdown: None,
2136            },
2137            control_atom,
2138            ready_atom,
2139            Weak::new(),
2140            None,
2141            None,
2142            limits,
2143        )
2144    }
2145
2146    /// Builds a runtime wrapping `services` with a configured auth `token` for unit
2147    /// tests that exercise the `Connect` handshake enforcement without a live
2148    /// scheduler. Uses a fresh interned control atom and no notifier.
2149    #[cfg(test)]
2150    pub(super) fn for_tests_with_auth_token(
2151        services: Arc<dyn ConnectionServices>,
2152        token: Vec<u8>,
2153    ) -> Self {
2154        let atoms = AtomTable::with_common_atoms();
2155        let control_atom = atoms.intern(CONNECTION_SHUTDOWN_CONTROL_ATOM);
2156        let ready_atom = atoms.intern(CONNECTION_READY_ATOM);
2157        Self::new(
2158            ConnectionRuntimeInstallation {
2159                services: ConnectionServiceInstallation::capture(services),
2160                incarnations: None,
2161                fatal_shutdown: None,
2162            },
2163            control_atom,
2164            ready_atom,
2165            Weak::new(),
2166            None,
2167            Some(token),
2168            LimitsConfig::default(),
2169        )
2170    }
2171
2172    /// Builds a runtime wrapping `services` with a `notifier` for unit tests that
2173    /// exercise `apply_frame` and the close path without a live scheduler.
2174    #[cfg(test)]
2175    pub(super) fn for_tests_with_notifier(
2176        services: Arc<dyn ConnectionServices>,
2177        notifier: Arc<dyn ConnectionNotifier>,
2178    ) -> Self {
2179        let atoms = AtomTable::with_common_atoms();
2180        let control_atom = atoms.intern(CONNECTION_SHUTDOWN_CONTROL_ATOM);
2181        let ready_atom = atoms.intern(CONNECTION_READY_ATOM);
2182        Self::new(
2183            ConnectionRuntimeInstallation {
2184                services: ConnectionServiceInstallation::capture(services),
2185                incarnations: None,
2186                fatal_shutdown: None,
2187            },
2188            control_atom,
2189            ready_atom,
2190            Weak::new(),
2191            Some(notifier),
2192            None,
2193            LimitsConfig::default(),
2194        )
2195    }
2196
2197    pub(super) fn services(&self) -> &dyn ConnectionServices {
2198        self.services.as_ref()
2199    }
2200
2201    /// Returns the complete participant service captured at supervisor startup.
2202    pub(super) const fn participant_service(&self) -> Option<&InstalledParticipantService> {
2203        self.participant_service.as_ref()
2204    }
2205
2206    fn participant_service_fatal(&self) -> Result<Option<ParticipantServiceFatal>, ServerError> {
2207        let Some(service) = self.participant_service() else {
2208            return Ok(None);
2209        };
2210        service
2211            .service_fatal()
2212            .map_err(|error| ServerError::ParticipantIncarnation {
2213                phase: "participant fatal latch inspection",
2214                message: error.to_string(),
2215            })
2216    }
2217
2218    fn activate_fatal_shutdown(&self) {
2219        if let Some(shutdown) = self.fatal_shutdown.as_ref() {
2220            shutdown.initiate();
2221        }
2222    }
2223
2224    fn ensure_participant_service_live(&self) -> Result<(), ServerError> {
2225        let Some(fatal) = self.participant_service_fatal()? else {
2226            return Ok(());
2227        };
2228        self.activate_fatal_shutdown();
2229        Err(ServerError::ParticipantServiceFatal { fatal })
2230    }
2231
2232    fn latch_connection_fate_intent_incomplete(
2233        &self,
2234        open_sequence: u64,
2235        conversation_id: u64,
2236    ) -> Result<ParticipantServiceFatal, ServerError> {
2237        let Some(service) = self.participant_service() else {
2238            return Err(ServerError::ParticipantIncarnation {
2239                phase: "connection-fate fatal latch",
2240                message: "a durable Open lacks its installed participant service".to_owned(),
2241            });
2242        };
2243        let fatal = service
2244            .latch_connection_fate_intent_incomplete(open_sequence, conversation_id)
2245            .map_err(|error| ServerError::ParticipantIncarnation {
2246                phase: "connection-fate fatal latch",
2247                message: error.to_string(),
2248            })?;
2249        self.activate_fatal_shutdown();
2250        Ok(fatal)
2251    }
2252
2253    fn complete_connection_fate_fatal(
2254        &self,
2255        open_sequence: u64,
2256        conversations: &[u64],
2257        phase: &'static str,
2258        error: &impl std::fmt::Display,
2259    ) -> ServerError {
2260        tracing::error!(open_sequence, phase, %error, "durable connection-fate intent is incomplete");
2261        let Some(&conversation_id) = conversations.first() else {
2262            return ServerError::ParticipantIncarnation {
2263                phase: "connection-fate fatal target",
2264                message: "a durable Open has no tracked conversation target".to_owned(),
2265            };
2266        };
2267        match self.latch_connection_fate_intent_incomplete(open_sequence, conversation_id) {
2268            Ok(fatal) => ServerError::ParticipantServiceFatal { fatal },
2269            Err(latch_error) => latch_error,
2270        }
2271    }
2272
2273    /// Runs one typed terminal fold after classification and before teardown.
2274    pub(super) fn complete_connection_fate(
2275        &self,
2276        connection_incarnation: Option<ConnectionIncarnation>,
2277        class: ConnectionFateClass,
2278        conversations: &[u64],
2279    ) -> Result<(), ServerError> {
2280        if conversations.is_empty() {
2281            return Ok(());
2282        }
2283        self.ensure_participant_service_live()?;
2284        let (Some(connection_incarnation), Some(service), Some(authority)) = (
2285            connection_incarnation,
2286            self.participant_service(),
2287            self.incarnations.as_ref(),
2288        ) else {
2289            return Err(ServerError::ParticipantIncarnation {
2290                phase: "connection-fate authority composition",
2291                message: "tracked participant conversations lack a complete service/incarnation authority"
2292                    .to_owned(),
2293            });
2294        };
2295        let intent =
2296            authority.open_connection_fate(connection_incarnation, class, conversations)?;
2297        if let Err(error) = service.handle_connection_fate(intent.work_item()) {
2298            return Err(self.complete_connection_fate_fatal(
2299                intent.open_sequence,
2300                conversations,
2301                "handler",
2302                &error,
2303            ));
2304        }
2305        if let Err(error) = authority.complete_connection_fate(intent.open_sequence) {
2306            return Err(self.complete_connection_fate_fatal(
2307                intent.open_sequence,
2308                conversations,
2309                "Complete",
2310                &error,
2311            ));
2312        }
2313        Ok(())
2314    }
2315
2316    /// Resolves the bound-only protocol-error gate from participant authority.
2317    pub(super) fn connection_has_bound_participant(
2318        &self,
2319        connection_incarnation: Option<ConnectionIncarnation>,
2320        conversations: &[u64],
2321    ) -> Result<bool, ServerError> {
2322        if conversations.is_empty() {
2323            return Ok(false);
2324        }
2325        self.ensure_participant_service_live()?;
2326        let (Some(connection_incarnation), Some(service)) =
2327            (connection_incarnation, self.participant_service())
2328        else {
2329            return Err(ServerError::ParticipantIncarnation {
2330                phase: "bound participant classification",
2331                message:
2332                    "tracked participant conversations lack a complete service/incarnation pair"
2333                        .to_owned(),
2334            });
2335        };
2336        service
2337            .connection_has_bound_participant(connection_incarnation, conversations)
2338            .map_err(|error| ServerError::ParticipantIncarnation {
2339                phase: "bound participant classification",
2340                message: error.to_string(),
2341            })
2342    }
2343
2344    /// Returns the configured connection auth token as opaque bytes, or `None` when
2345    /// no `[auth]` section was configured (open access).
2346    pub(super) fn auth_token(&self) -> Option<&[u8]> {
2347        self.auth_token.as_deref()
2348    }
2349
2350    /// Returns the configured connection-keyed notifier, if any.
2351    pub(super) fn notifier(&self) -> Option<&Arc<dyn ConnectionNotifier>> {
2352        self.notifier.as_ref()
2353    }
2354
2355    /// Offers a channel publish to the notifier's observability-drain tap, returning
2356    /// `true` when the application consumed it (so the connection process skips the
2357    /// normal fan-out). `false` when no notifier is installed (liminal standalone) or
2358    /// the notifier did not recognise the channel, so the caller can invoke it
2359    /// unconditionally and fall through to the normal publish path.
2360    pub(super) fn notifier_channel_publish(&self, pid: u64, channel: &str, payload: &[u8]) -> bool {
2361        self.notifier
2362            .as_ref()
2363            .is_some_and(|notifier| notifier.on_channel_publish(pid, channel, payload))
2364    }
2365
2366    /// Stores `registration` on the connection record for `pid`, so the close
2367    /// path can later fire `on_worker_unregistered` for exactly the connections
2368    /// that registered. A missing record (the connection already closed) is a
2369    /// no-op.
2370    ///
2371    /// # Errors
2372    /// Returns [`ServerError`] when the connection registry mutex is poisoned.
2373    pub(super) fn set_registration(
2374        &self,
2375        pid: u64,
2376        registration: WorkerRegistration,
2377    ) -> Result<(), ServerError> {
2378        if let Some(record) = lock(&self.records, "connection registry")?.get_mut(&pid) {
2379            record.registration = Some(registration);
2380        }
2381        Ok(())
2382    }
2383
2384    /// Allocates the next monotonic push correlation id.
2385    fn next_push_correlation_id(&self) -> u64 {
2386        self.next_push_id.fetch_add(1, Ordering::Relaxed)
2387    }
2388
2389    /// Registers a one-shot reply slot for `correlation_id`, owned by connection
2390    /// `pid`, and returns its receiver. `deadline` is the slot's optional absolute
2391    /// reply expiry (`None` = the default no-deadline shape). The connection
2392    /// process resolves the slot via [`resolve_push`]; the close path drops the
2393    /// connection's outstanding slots via [`cancel_pushes_for_connection`]; an
2394    /// explicit deadline resolves it via [`expire_push_if_due`].
2395    ///
2396    /// # Errors
2397    /// Returns [`ServerError`] when the correlation registry mutex is poisoned.
2398    fn register_push(
2399        &self,
2400        pid: u64,
2401        correlation_id: u64,
2402        deadline: Option<Instant>,
2403    ) -> Result<Receiver<Vec<u8>>, ServerError> {
2404        let (sender, receiver) = channel();
2405        let limit = self.limits.max_pending_pushes_per_connection;
2406        {
2407            let mut slots = lock(&self.push_replies, "push correlation registry")?;
2408            // §5 `max_pending_pushes_per_connection`: refuse a new in-flight push
2409            // once this connection already holds the cap. Counted per owning pid so
2410            // one connection cannot exhaust the shared registry; slots free on
2411            // reply, deadline expiry, or connection close. The count-and-insert
2412            // stays under the one lock so the cap is enforced atomically.
2413            let outstanding = slots.values().filter(|pending| pending.pid == pid).count();
2414            if outstanding >= limit {
2415                return Err(ServerError::ConnectionCapReached {
2416                    operation: "server push".to_owned(),
2417                    cap: "max_pending_pushes_per_connection",
2418                    limit,
2419                });
2420            }
2421            slots.insert(
2422                correlation_id,
2423                PendingPush {
2424                    pid,
2425                    sender,
2426                    deadline,
2427                },
2428            );
2429        }
2430        Ok(receiver)
2431    }
2432
2433    /// Host-side, lazy evaluation of a push's reply deadline, called from an
2434    /// elapsed [`PushReplyAwaiter::receive`] quantum. This NEVER wakes the
2435    /// connection process and runs no timer — it inspects supervisor-owned state
2436    /// under the registry lock only.
2437    ///
2438    /// A slot with an explicit deadline that has passed is removed here (dropping
2439    /// its `Sender` and releasing its §5 `max_pending_pushes_per_connection` cap
2440    /// admission, since the cap is the per-pid slot count) and reported
2441    /// [`PushSlotDisposition::Expired`]. A slot with no deadline, or a deadline
2442    /// still in the future, is left UNTOUCHED and reported
2443    /// [`PushSlotDisposition::Live`] — the elapsed quantum is a benign re-arm. A
2444    /// missing slot is [`PushSlotDisposition::Absent`].
2445    fn expire_push_if_due(&self, correlation_id: u64) -> PushSlotDisposition {
2446        // S4: a poisoned registry must NOT read as slot absence — the slot (and
2447        // its cap admission) may still be in the map. Reclamation recovers the
2448        // guard: removal-only operations are sound on a recovered map (a panic
2449        // in another critical section cannot leave the HashMap itself in a
2450        // partial state; only our bookkeeping invariants could be stale, and
2451        // removal restores them). Admission (`register_push`) stays fail-closed.
2452        let mut slots = recover_lock(&self.push_replies);
2453        let Some(pending) = slots.get(&correlation_id) else {
2454            return PushSlotDisposition::Absent;
2455        };
2456        // Copy the deadline out so the immutable borrow of `slots` ends before the
2457        // conditional `remove` below takes a mutable one.
2458        let deadline = pending.deadline;
2459        match deadline {
2460            Some(at) if Instant::now() >= at => {
2461                slots.remove(&correlation_id);
2462                PushSlotDisposition::Expired
2463            }
2464            _ => PushSlotDisposition::Live,
2465        }
2466    }
2467
2468    /// Drops a registered reply slot without resolving it, used on the
2469    /// push-enqueue failure path (the control could not be delivered to a
2470    /// now-gone process, so the just-reserved slot is unreachable). Dropping the
2471    /// slot's `Sender` wakes a still-waiting awaiter with a disconnected error.
2472    ///
2473    /// Returns whether THIS call removed the slot. Removal under the registry
2474    /// mutex is the atomic resolved-vs-cancelled transition: `false` means
2475    /// another path won — [`resolve_push`](Self::resolve_push) already sent the
2476    /// reply (its send happens under the same lock, so the payload is already in
2477    /// the channel when this returns), or the connection's close path dropped
2478    /// the slot (sender gone, channel disconnected).
2479    pub(super) fn cancel_push(&self, correlation_id: u64) -> bool {
2480        // S4: reclamation recovers a poisoned guard — a rollback that silently
2481        // skipped its removal would strand the slot and its cap admission.
2482        recover_lock(&self.push_replies)
2483            .remove(&correlation_id)
2484            .is_some()
2485    }
2486
2487    /// Drops every reply slot owned by connection `pid`, waking each awaiter with a
2488    /// disconnected error (the dropped `Sender` disconnects the awaiter's
2489    /// `Receiver`). Called from the close path so a connection that exits with
2490    /// in-flight pushes signals worker death immediately instead of leaving each
2491    /// awaiter to block the full push-reply timeout. A slot that [`resolve_push`]
2492    /// already removed is gone, so it is untouched here; an unknown pid is a no-op.
2493    fn cancel_pushes_for_connection(&self, pid: u64) {
2494        // S4: the close sweep is the reclamation of last resort ("connection
2495        // close at the latest") — it must complete on a poisoned map too.
2496        recover_lock(&self.push_replies).retain(|_correlation_id, pending| pending.pid != pid);
2497    }
2498
2499    /// S3 second half (shape (b), check-after-insert): pre-publication
2500    /// confirmation that the connection record for `pid` still exists, run in
2501    /// the INSERT -> CONFIRM -> PUBLISH order (S7 — confirming after the
2502    /// enqueue let a close-swept-then-answered push report `Err` for a Push the
2503    /// client had received). `true` leaves the slot in place and the caller may
2504    /// publish; `false` means a concurrent close already removed the record —
2505    /// this call then removes the caller's own just-inserted slot (rolling back
2506    /// its cap admission) so nothing is stranded, and the caller returns
2507    /// WITHOUT publishing: an `Err` from the push methods guarantees no `Push`
2508    /// control was published.
2509    ///
2510    /// Why exactly one side always observes the slot: `remove` (the single
2511    /// record-removal path) removes the host record BEFORE sweeping the pid's
2512    /// push slots, and this check reads the record AFTER inserting the slot and
2513    /// BEFORE the control is published. Both records accesses are serialized by
2514    /// the `records` mutex, so either (i) this read precedes the record removal
2515    /// — then the slot insert precedes the sweep (insert < read < removal <
2516    /// sweep in the happens-before order) and the SWEEP observes and removes
2517    /// the slot: if the control was published in the meantime the awaiter reads
2518    /// the truthful disconnected outcome and a late client reply is a harmless
2519    /// no-op; or (ii) this read follows the record removal — then THIS call
2520    /// observes the absence, rolls the slot back itself, and nothing was
2521    /// published. When both observe (a sweep and a rollback can both run in
2522    /// case (ii) if the insert also preceded the sweep), removal is idempotent
2523    /// and the cap is derived from map membership, so nothing double-releases.
2524    ///
2525    /// Lock discipline: `records` and `push_replies` are NEVER held together —
2526    /// here (`records` read, released, then `push_replies` on rollback), in
2527    /// `remove` (`records` removal, released, then the sweep), and everywhere
2528    /// else in this file the two mutexes are taken strictly sequentially, so no
2529    /// lock-order inversion is possible. This adds ZERO work to the connection
2530    /// slice path: the re-check runs on the push caller's thread only.
2531    pub(super) fn confirm_push_registration(&self, pid: u64, correlation_id: u64) -> bool {
2532        if self.is_registered(pid) {
2533            return true;
2534        }
2535        self.cancel_push(correlation_id);
2536        false
2537    }
2538
2539    /// Number of reserved push reply slots outstanding. A benign wait-quantum
2540    /// timeout must NOT change this (the slot survives); an explicit-deadline
2541    /// expiry, a consumed reply, and connection close each release exactly one.
2542    #[cfg(test)]
2543    pub(super) fn pending_push_count(&self) -> usize {
2544        recover_lock(&self.push_replies).len()
2545    }
2546
2547    /// Reserved push reply slots owned by connection `pid` — the exact quantity
2548    /// the §5 `max_pending_pushes_per_connection` cap counts (test instrument).
2549    #[cfg(test)]
2550    pub(super) fn pending_push_count_for(&self, pid: u64) -> usize {
2551        recover_lock(&self.push_replies)
2552            .values()
2553            .filter(|pending| pending.pid == pid)
2554            .count()
2555    }
2556
2557    /// Resolves the reply slot for `correlation_id` with the client's reply
2558    /// payload, waking the [`PushReplyAwaiter`]. Called by the connection process
2559    /// when a correlated `PushReply` frame arrives. A missing slot — already
2560    /// resolved, expired at its explicit deadline, dropped by connection close, or
2561    /// an unknown id — is a harmless no-op: a late `PushReply` for a slot that is
2562    /// gone is discarded here, never delivered and never a panic or desync.
2563    pub(super) fn resolve_push(&self, correlation_id: u64, payload: Vec<u8>) {
2564        // S4: delivery-plus-removal recovers a poisoned guard — dropping a real
2565        // reply (and stranding its slot) because an unrelated critical section
2566        // panicked would kill reclamation and exact cap accounting.
2567        let mut slots = recover_lock(&self.push_replies);
2568        if let Some(pending) = slots.remove(&correlation_id) {
2569            // The send stays under the registry lock so removal and delivery are
2570            // one atomic step: a timed-out awaiter that observes the slot gone
2571            // (its `cancel_push` returned false) is then GUARANTEED to find the
2572            // payload already in the channel — without this ordering the awaiter
2573            // could see the removal, find the channel still empty, and report a
2574            // timeout for a reply that was about to land. The send itself never
2575            // blocks (unbounded channel), and a receiver dropped after an
2576            // abandoned wait makes it a benign discard.
2577            pending.sender.send(payload).ok();
2578        }
2579    }
2580
2581    pub(super) const fn control_atom(&self) -> Atom {
2582        self.control_atom
2583    }
2584
2585    /// Sole registration path for a connection: the spawn thread inserts the
2586    /// record synchronously, before `spawn_connection` returns the handle, so
2587    /// `is_tracked`/`active_connection_count` reflect the connection
2588    /// immediately. The connection handler never writes the registry (it only
2589    /// reads via `mark_crashed`/`finish`), so there is a single writer here and
2590    /// no register/ensure-register race.
2591    ///
2592    /// Ordering note: `spawn_native` only enqueues the process, so its first
2593    /// slice may run on another worker thread before this insert lands. If that
2594    /// first slice exits immediately (e.g. a missing-stream crash) its
2595    /// `mark_crashed`/`finish` removes nothing and this insert then leaves a
2596    /// record for an already-dead pid. W4 leg 1 retires the per-accept
2597    /// `reap_crashed` scan that used to self-heal that orphan continuously;
2598    /// instead [`Self::reconcile_register_orphan`] closes the race with a SINGLE
2599    /// point check on the registration event itself — never a loop.
2600    fn register_with_fd(
2601        &self,
2602        pid: u64,
2603        peer_addr: Option<SocketAddr>,
2604        connection_incarnation: Option<ConnectionIncarnation>,
2605        fd_guard: TcpStream,
2606    ) -> Result<(), ServerError> {
2607        self.register_record(pid, peer_addr, connection_incarnation, Some(fd_guard))?;
2608        self.reconcile_register_orphan(pid);
2609        Ok(())
2610    }
2611
2612    /// Closes the register-orphan race (see [`Self::register_with_fd`]) with one
2613    /// point check driven by the registration event — not a periodic scan. If
2614    /// the just-registered pid is already absent from the scheduler process
2615    /// table, its first slice has run and exited, so the record this
2616    /// registration inserted is an orphan the retiring reap scan used to sweep;
2617    /// reclaim it immediately through the ordinary `remove()` funnel. A pid still
2618    /// present is live and needs nothing here: a later external termination rides
2619    /// the exit-event reactor and an ordinary exit its own final slice.
2620    fn reconcile_register_orphan(&self, pid: u64) {
2621        let Some(scheduler) = self.scheduler.upgrade() else {
2622            return;
2623        };
2624        // The process-table lookup returns a sharded guard; bind only the
2625        // presence bool so the guard is released before `reclaim_terminated`
2626        // takes the connection registry lock (no cross-lock hold).
2627        let already_exited = scheduler.process_table().get(pid).is_none();
2628        if !already_exited {
2629            return;
2630        }
2631        let reason = scheduler
2632            .peek_exit_reason(pid)
2633            .unwrap_or(ExitReason::Normal);
2634        self.reclaim_terminated(pid, reason);
2635    }
2636
2637    /// TOLD reclamation of a connection whose process exited WITHOUT running a
2638    /// final handler slice — external/panic termination, where
2639    /// [`ConnectionProcess::Drop`] runs but no `mark_crashed`/`finish` does, and
2640    /// the register-orphan race above. Delivered the instant beamr publishes the
2641    /// process's [`ExitEvent`] (via [`run_reclaim_reactor`]) or at the
2642    /// registration point check, and routed through the SAME [`Self::remove`]
2643    /// funnel as every other teardown — no third funnel, no periodic scan.
2644    /// Idempotent: a record already removed in-slice, by the orphan reconcile, or
2645    /// by a duplicate delivery is a no-op here (remove returns `None`), so the §5
2646    /// admission gauge is released exactly once.
2647    fn reclaim_terminated(&self, pid: u64, reason: ExitReason) {
2648        let Some(record) = self.remove(pid) else {
2649            return;
2650        };
2651        self.fire_unregistered(pid, &record);
2652        tracing::warn!(
2653            connection_pid = pid,
2654            peer_addr = ?record.peer_addr,
2655            reason = ?reason,
2656            "connection process exited without a final slice; host record reclaimed by delivery"
2657        );
2658    }
2659
2660    /// One exit-event delivery: drain beamr's retained outcome (sole drainer,
2661    /// bounding its store) then reclaim through [`Self::reclaim_terminated`]. The
2662    /// only non-production element is the `#[cfg(test)]` reclamation gate, which
2663    /// is staged for at most one targeted pid and compiled out entirely in
2664    /// production — the delivery semantics are identical with or without it.
2665    fn deliver_reclamation(&self, scheduler: &Weak<Scheduler>, pid: u64, reason: ExitReason) {
2666        if let Some(scheduler) = scheduler.upgrade() {
2667            // The reason is already in-hand from the event; the drained outcome
2668            // is discarded, its purpose being only to bound beamr's store.
2669            drop(scheduler.take_exit_outcome(pid));
2670        }
2671        #[cfg(test)]
2672        let staged = self.stage_reclaim_barrier(pid);
2673        #[cfg(test)]
2674        if let Some(barrier) = staged.as_ref() {
2675            // Rendezvous: the pid is now dead but its record is still tracked —
2676            // the S8 reclamation window (oracle 26). Then wait for the harness to
2677            // release the reclaim.
2678            barrier.reached.wait();
2679            barrier.release.wait();
2680        }
2681        self.reclaim_terminated(pid, reason);
2682        #[cfg(test)]
2683        if let Some(barrier) = staged.as_ref() {
2684            // Signal the funnel completed so the harness can observe the record
2685            // gone without sampling.
2686            barrier.done.wait();
2687        }
2688    }
2689
2690    #[cfg(test)]
2691    fn register(&self, pid: u64, peer_addr: Option<SocketAddr>) -> Result<(), ServerError> {
2692        self.register_record(pid, peer_addr, None, None)
2693    }
2694
2695    fn register_record(
2696        &self,
2697        pid: u64,
2698        peer_addr: Option<SocketAddr>,
2699        connection_incarnation: Option<ConnectionIncarnation>,
2700        fd_guard: Option<TcpStream>,
2701    ) -> Result<(), ServerError> {
2702        lock(&self.records, "connection registry")?.insert(
2703            pid,
2704            ConnectionRecord {
2705                peer_addr,
2706                connection_incarnation,
2707                registration: None,
2708                readiness: None,
2709                ready_pending: Arc::new(AtomicBool::new(false)),
2710                parked: AtomicBool::new(false),
2711                fd_guard,
2712            },
2713        );
2714        // Single-writer insert (see doc above) pairs one gauge increment with the
2715        // decrement in `remove`, keeping `liminal_connections_active` equal to the
2716        // live record count on every teardown route.
2717        crate::metrics::connection_spawned();
2718        Ok(())
2719    }
2720
2721    pub(super) fn mark_crashed(&self, pid: u64, reason: ExitReason, peer_addr: Option<SocketAddr>) {
2722        let removed = self.remove(pid);
2723        if let Some(record) = removed.as_ref() {
2724            self.fire_unregistered(pid, record);
2725        }
2726        let removed_peer_addr = removed
2727            .as_ref()
2728            .and_then(|record| record.peer_addr)
2729            .or(peer_addr);
2730        tracing::warn!(
2731            connection_pid = pid,
2732            peer_addr = ?removed_peer_addr,
2733            reason = ?reason,
2734            "connection process crashed"
2735        );
2736    }
2737
2738    /// Whether the spawn thread has installed the host record. A first native
2739    /// slice can win the enqueue-vs-record race and must remain runnable until it
2740    /// has somewhere host-reachable to publish its readiness token.
2741    pub(super) fn is_registered(&self, pid: u64) -> bool {
2742        self.contains(pid)
2743    }
2744
2745    /// Removes a token minted in-slice when publishing it to the host record fails.
2746    pub(super) fn deregister_unpublished_readiness(&self, token: ReadinessToken) {
2747        if let Some(scheduler) = self.scheduler.upgrade() {
2748            scheduler.readiness_deregister(token);
2749        }
2750    }
2751
2752    /// Cancels deadline timers detached by reply completion or connection close.
2753    pub(super) fn cancel_deadline_timers(&self, timers: Vec<TimerRef>) {
2754        let Some(scheduler) = self.scheduler.upgrade() else {
2755            return;
2756        };
2757        if let Ok(mut wheel) = scheduler.timers().lock() {
2758            for timer in timers {
2759                wheel.cancel(timer);
2760            }
2761        }
2762    }
2763
2764    pub(super) fn finish(&self, pid: u64) {
2765        if let Some(removed) = self.remove(pid) {
2766            self.fire_unregistered(pid, &removed);
2767        }
2768    }
2769
2770    /// Records the one readiness token minted for this connection. A live
2771    /// connection never re-registers: later parked slices rearm this identity.
2772    pub(super) fn set_readiness_token_once(
2773        &self,
2774        pid: u64,
2775        token: ReadinessToken,
2776        fd: RawFd,
2777    ) -> Result<(), ServerError> {
2778        let mut records = lock(&self.records, "connection registry")?;
2779        let record = records
2780            .get_mut(&pid)
2781            .ok_or_else(|| ServerError::ListenerAccept {
2782                message: format!("connection {pid} has no host record for readiness registration"),
2783            })?;
2784        if record.readiness.is_some() {
2785            return Err(ServerError::ListenerAccept {
2786                message: format!("connection {pid} attempted to replace its readiness token"),
2787            });
2788        }
2789        record.readiness = Some(ReadinessRegistration { token, fd });
2790        drop(records);
2791        Ok(())
2792    }
2793
2794    /// Invokes `on_worker_unregistered` for a removed connection record that
2795    /// carried a worker registration. A record with no registration (a plain
2796    /// connection, or a worker connection that never registered) is a no-op, so
2797    /// only connections that actually registered deregister.
2798    fn fire_unregistered(&self, pid: u64, record: &ConnectionRecord) {
2799        if record.registration.is_some() {
2800            if let Some(notifier) = self.notifier.as_ref() {
2801                notifier.on_worker_unregistered(pid);
2802            }
2803        }
2804    }
2805
2806    fn reap_crashed(&self, scheduler: &Scheduler) -> usize {
2807        let pids = match self.records.lock() {
2808            Ok(records) => records.keys().copied().collect::<Vec<_>>(),
2809            Err(error) => {
2810                tracing::warn!(%error, "connection registry unavailable during crash reap");
2811                return 0;
2812            }
2813        };
2814        let mut reaped = 0;
2815        for pid in pids {
2816            if scheduler.process_table().get(pid).is_none() {
2817                let removed = self.remove(pid);
2818                if let Some(record) = removed.as_ref() {
2819                    self.fire_unregistered(pid, record);
2820                }
2821                let peer_addr = removed.and_then(|record| record.peer_addr);
2822                // This process exited without ever reaching `mark_crashed`/`finish`
2823                // (e.g. the beamr scheduler terminated it externally). W4 leg 1
2824                // retired this scan from the per-accept listener loop, and W4 leg 3
2825                // retired the shutdown-drain reconciliation that also drove it: the
2826                // reclaimer of these exits is now the TOLD exit-event reactor
2827                // ([`run_reclaim_reactor`]), which also composes drain completion
2828                // through the one `remove()` funnel. This scan now survives only as
2829                // that reactor's exit-event overflow (`Lagged`) recovery, driven a
2830                // bounded number of times, never periodically. beamr 0.15.4 exposes
2831                // a public, non-blocking
2832                // `peek_exit_reason` (and `take_exit_outcome`), so the real reason
2833                // IS recoverable here rather than logged as an opaque literal.
2834                let reason = scheduler.peek_exit_reason(pid);
2835                tracing::warn!(
2836                    connection_pid = pid,
2837                    ?peer_addr,
2838                    ?reason,
2839                    "connection process exited without a final slice; reclaimed by reconciliation"
2840                );
2841                reaped += 1;
2842            }
2843        }
2844        reaped
2845    }
2846
2847    fn contains(&self, pid: u64) -> bool {
2848        self.records
2849            .lock()
2850            .is_ok_and(|records| records.contains_key(&pid))
2851    }
2852
2853    #[cfg(test)]
2854    fn readiness_registration_count(&self) -> usize {
2855        self.records.lock().map_or(0, |records| {
2856            records
2857                .values()
2858                .filter(|record| record.readiness.is_some())
2859                .count()
2860        })
2861    }
2862
2863    #[cfg(test)]
2864    fn readiness_fd(&self, pid: u64) -> Option<RawFd> {
2865        self.records
2866            .lock()
2867            .ok()?
2868            .get(&pid)
2869            .and_then(|record| record.readiness.map(|registration| registration.fd))
2870    }
2871
2872    #[cfg(test)]
2873    fn readiness_token(&self, pid: u64) -> Option<ReadinessToken> {
2874        self.records
2875            .lock()
2876            .ok()?
2877            .get(&pid)
2878            .and_then(|record| record.readiness.map(|registration| registration.token))
2879    }
2880
2881    fn active_connections(&self) -> Vec<ActiveConnection> {
2882        self.records.lock().map_or_else(
2883            |_| Vec::new(),
2884            |records| {
2885                records
2886                    .iter()
2887                    .map(|(&pid, record)| ActiveConnection {
2888                        pid,
2889                        peer_addr: record.peer_addr,
2890                    })
2891                    .collect()
2892            },
2893        )
2894    }
2895
2896    /// Reads the complete active-connection incarnation set under the
2897    /// registry lock — the bounded defense-in-depth collision-skip input to
2898    /// incarnation allocation (uniqueness itself comes from allocator-log
2899    /// monotonicity; see `allocate_connection_incarnation`). Poisoning fails
2900    /// admission closed: treating an unreadable registry as empty would
2901    /// silently drop the defense layer.
2902    fn complete_active_incarnation_references(
2903        &self,
2904    ) -> Result<Vec<ConnectionIncarnation>, ServerError> {
2905        Ok(
2906            lock(&self.records, "connection incarnation reference registry")?
2907                .values()
2908                .filter_map(|record| record.connection_incarnation)
2909                .collect(),
2910        )
2911    }
2912
2913    fn push_control(&self, pid: u64, control: ConnectionControl) -> Result<(), ServerError> {
2914        lock(&self.controls, "connection control queue")?
2915            .push(QueuedConnectionControl { pid, control });
2916        Ok(())
2917    }
2918
2919    pub(super) fn pop_control(&self, pid: u64) -> Option<ConnectionControl> {
2920        let mut controls = self.controls.lock().ok()?;
2921        let index = controls.iter().position(|queued| queued.pid == pid)?;
2922        Some(controls.remove(index).control)
2923    }
2924
2925    /// Non-consuming final-probe query for controls enqueued after mailbox drain.
2926    pub(super) fn has_control(&self, pid: u64) -> bool {
2927        self.controls
2928            .lock()
2929            .is_ok_and(|controls| controls.iter().any(|queued| queued.pid == pid))
2930    }
2931
2932    /// Pulls a queued-but-unconsumed control back out of the queue. Returns
2933    /// whether THIS call removed it — `false` means the entry already left the
2934    /// queue, and since [`Self::pop_control`] is the only other remover, a
2935    /// consumer drain took it (S8's publication disambiguator). Matching is
2936    /// `pid` + full control equality; a `Push` control embeds its
2937    /// runtime-unique correlation id, so this can never remove a different
2938    /// push's entry and misreport.
2939    fn remove_control(&self, pid: u64, control: &ConnectionControl) -> bool {
2940        let Ok(mut controls) = self.controls.lock() else {
2941            return false;
2942        };
2943        let Some(index) = controls
2944            .iter()
2945            .position(|queued| queued.pid == pid && &queued.control == control)
2946        else {
2947            return false;
2948        };
2949        controls.remove(index);
2950        true
2951    }
2952
2953    fn active_count(&self) -> usize {
2954        self.records.lock().map_or(0, |records| records.len())
2955    }
2956
2957    /// Removes the connection record for `pid` and, in the same close step, drops
2958    /// every push reply slot that connection still owns so each waiting
2959    /// [`PushReplyAwaiter`] wakes immediately with a disconnected error. This runs
2960    /// on every close route — `finish`, `mark_crashed`, and `reap_crashed` all
2961    /// remove through here — and fires regardless of whether the connection ever
2962    /// registered a worker, so a plain push target is covered too.
2963    ///
2964    /// ORDER MATTERS (S3/S7): the record is removed BEFORE the push sweep. A
2965    /// push registering concurrently runs INSERT -> CONFIRM -> PUBLISH
2966    /// (`confirm_push_registration` reads the record after inserting its slot
2967    /// and before publishing its control), so with this ordering exactly one
2968    /// side always observes a racing slot: a confirm that ran before this
2969    /// removal implies the slot was inserted before the sweep below (which then
2970    /// reaps it — a control published after that confirm is answered into a
2971    /// swept slot, read as the truthful disconnected outcome); a confirm after
2972    /// this removal sees the absence, rolls the slot back itself, and never
2973    /// publishes. Sweeping first (the original order) left a window — sweep,
2974    /// then insert+confirm, then record removal — where NEITHER side observed
2975    /// the slot and it leaked past connection close. The two locks are taken
2976    /// strictly sequentially (never nested), so no lock-order inversion.
2977    fn remove(&self, pid: u64) -> Option<ConnectionRecord> {
2978        let mut removed = self
2979            .records
2980            .lock()
2981            .ok()
2982            .and_then(|mut records| records.remove(&pid));
2983        self.cancel_pushes_for_connection(pid);
2984        if let Some(registration) = removed.as_mut().and_then(|record| record.readiness.take()) {
2985            if let Some(scheduler) = self.scheduler.upgrade() {
2986                // This call is ACK'd: it returns only after the poll owner has
2987                // removed the registration. `fd_guard` is still live here.
2988                scheduler.readiness_deregister(registration.token);
2989                tracing::debug!(
2990                    registered_fd = registration.fd,
2991                    "connection readiness deregistration acknowledged"
2992                );
2993            }
2994        }
2995        // Decrement only when a record was actually present so a double-remove
2996        // (e.g. `finish` after `reap_crashed`) cannot drive the gauge negative.
2997        // The §5 admission slot is released on the same guard: the reservation
2998        // acquired in `spawn_connection` converted into this record at
2999        // `register`, so its removal is exactly one release per reservation.
3000        if removed.is_some() {
3001            crate::metrics::connection_closed();
3002            self.release_admission();
3003        }
3004        if let Some(record) = removed.as_mut() {
3005            // Explicit after-deregister drop documents and enforces the fd wall.
3006            drop(record.fd_guard.take());
3007        }
3008        // TOLD drain-completion tell (W4 leg 3, §4.3): the record map above already
3009        // reflects this removal, so bump the removal generation and wake the
3010        // drain/settle waiter AFTER the observed state is updated. Ordering the
3011        // state update before the generation bump — paired with the waiter arming
3012        // its snapshot before observing `active_count` — is the arm-before-observe
3013        // barrier that makes a concurrently delivered exit un-losable (oracle 18).
3014        // Only a real removal tells, so a double-remove drives no spurious wake.
3015        if removed.is_some() {
3016            self.signal_connection_removed();
3017        }
3018        removed
3019    }
3020
3021    /// Bumps the drain-completion generation and wakes the drain/settle waiter.
3022    /// Called from [`Self::remove`] on every route that actually drops a record.
3023    fn signal_connection_removed(&self) {
3024        // Bump the generation under the lock, release it, THEN notify. A waiter
3025        // holds this lock continuously from its generation re-check through the
3026        // atomic release inside `wait_timeout`, so it can never miss a bump
3027        // published before the notify — the Condvar lost-wakeup contract holds
3028        // without notifying under the guard.
3029        {
3030            let mut generation = recover_lock(&self.drain_generation);
3031            *generation = generation.wrapping_add(1);
3032        }
3033        self.drain_removed.notify_all();
3034    }
3035
3036    /// Parks the calling thread until every tracked connection has been removed
3037    /// or `deadline` elapses, returning `true` when the drain completed and
3038    /// `false` when the single admitted deadline won. This is the TOLD
3039    /// replacement (W4 leg 3, §4.3) for the retired reap/count/sleep drain loop:
3040    /// it never samples completion on a timer. Completion is observed only on a
3041    /// delivered connection-removal wake (composed from the one `remove()` funnel,
3042    /// so a Died/Detached/crash exit and an orderly close decrement through the
3043    /// same path — oracle 15) or the one deadline. Force-close settle reuses this
3044    /// same waiter with its own deadline rather than a second poll loop (oracle
3045    /// 14).
3046    pub(super) fn wait_for_active_connections_drained(&self, deadline: Instant) -> bool {
3047        loop {
3048            // ARM before OBSERVE: snapshot the removal generation first, so an exit
3049            // delivered after the completion observation below and before the park
3050            // bumps a generation the park detects — it is never lost (oracle 18).
3051            let snapshot = self.drain_generation_snapshot();
3052            // OBSERVE completion first: a last exit that reaches zero wins a tie
3053            // with a simultaneously elapsed deadline (oracle 19).
3054            if self.active_count() == 0 {
3055                return true;
3056            }
3057            let Some(remaining) = deadline.checked_duration_since(Instant::now()) else {
3058                #[cfg(test)]
3059                self.drain_deadline_hits.fetch_add(1, Ordering::SeqCst);
3060                return false;
3061            };
3062            #[cfg(test)]
3063            self.run_drain_park_barrier();
3064            self.park_until_removed_or(snapshot, remaining);
3065        }
3066    }
3067
3068    /// Reads the current removal generation under its mutex.
3069    fn drain_generation_snapshot(&self) -> u64 {
3070        *recover_lock(&self.drain_generation)
3071    }
3072
3073    /// Parks on the removal `Condvar` for at most `timeout`, but only while no
3074    /// removal has bumped the generation since `snapshot` — the arm-before-observe
3075    /// barrier. `wait_timeout_while` evaluates the predicate under the lock BEFORE
3076    /// waiting, so a generation already advanced (an exit landed between the
3077    /// observation and here) returns immediately without sleeping; spurious wakes
3078    /// re-wait inside the call and never return early.
3079    fn park_until_removed_or(&self, snapshot: u64, timeout: Duration) {
3080        let outcome = self
3081            .drain_removed
3082            .wait_timeout_while(recover_lock(&self.drain_generation), timeout, |current| {
3083                *current == snapshot
3084            })
3085            .unwrap_or_else(PoisonError::into_inner);
3086        // A non-timed-out return means the predicate went false — a real removal
3087        // bumped the generation and woke this park (as opposed to the deadline).
3088        #[cfg(test)]
3089        if !outcome.1.timed_out() {
3090            self.drain_exit_wakes.fetch_add(1, Ordering::SeqCst);
3091        }
3092        // Release the guard promptly; the caller re-loops unlocked.
3093        drop(outcome);
3094    }
3095}
3096
3097/// One in-flight server-push reply slot, associating the awaiter's reply `sender`
3098/// with the `pid` of the connection that owns the push. The pid lets the close
3099/// path drop exactly that connection's slots; the correlation id (the map key)
3100/// still drives [`ConnectionRuntime::resolve_push`] and
3101/// [`ConnectionRuntime::cancel_push`].
3102#[derive(Debug)]
3103struct PendingPush {
3104    pid: u64,
3105    sender: Sender<Vec<u8>>,
3106    /// Absolute reply deadline for this push, when one was requested via
3107    /// [`ConnectionSupervisor::push_to_connection_with_deadline`]. `None` is the
3108    /// default 0.2.3 shape: the slot has no per-slot deadline and is reclaimed
3109    /// only by reply-consumed or connection-close. `Some` is evaluated host-side
3110    /// and lazily in [`ConnectionRuntime::expire_push_if_due`].
3111    deadline: Option<Instant>,
3112}
3113
3114/// Host-side disposition of a reply slot at an elapsed `receive` quantum.
3115enum PushSlotDisposition {
3116    /// The slot carried an explicit deadline that has passed; this call removed
3117    /// it (releasing its §5 cap admission).
3118    Expired,
3119    /// The slot is present with no deadline, or a deadline still in the future:
3120    /// the elapsed quantum is a benign re-arm and the slot is untouched.
3121    Live,
3122    /// No slot for this correlation id — a concurrent resolve or connection close
3123    /// already removed it.
3124    Absent,
3125}
3126
3127#[derive(Debug)]
3128struct ConnectionRecord {
3129    peer_addr: Option<SocketAddr>,
3130    /// Durable pair allocated and flushed before the process was spawned.
3131    connection_incarnation: Option<ConnectionIncarnation>,
3132    /// Worker registration declared on this connection, set by `set_registration`
3133    /// when a `WorkerRegister` frame is accepted. `Some` marks a connection whose
3134    /// close must fire `on_worker_unregistered`.
3135    registration: Option<WorkerRegistration>,
3136    /// Host-reachable identity for ACK'd deregistration after external death.
3137    readiness: Option<ReadinessRegistration>,
3138    /// Shared edge set before READY enters beamr's process-table pending queue;
3139    /// the executing process reads it at its final probe.
3140    ready_pending: Arc<AtomicBool>,
3141    /// FIX A-ii: `true` while this connection is parked (its last slice returned
3142    /// `Wait`). A parked connection with no `ready_pending` edge has fanned out
3143    /// every accepted publish to its socket, so the shutdown flush barrier reads
3144    /// this with `ready_pending` to know delivery has quiesced before it lets the
3145    /// shutdown Disconnect be broadcast.
3146    parked: AtomicBool,
3147    /// Keeps the fd alive until deregistration has been acknowledged, preventing
3148    /// stale registration delivery to a subsequently reused descriptor number.
3149    fd_guard: Option<TcpStream>,
3150}
3151
3152#[derive(Debug, Clone, Copy)]
3153struct ReadinessRegistration {
3154    token: ReadinessToken,
3155    fd: RawFd,
3156}
3157
3158#[derive(Debug, Clone, PartialEq, Eq)]
3159struct QueuedConnectionControl {
3160    pid: u64,
3161    control: ConnectionControl,
3162}
3163
3164fn lock<'a, T>(mutex: &'a Mutex<T>, context: &str) -> Result<MutexGuard<'a, T>, ServerError> {
3165    mutex.lock().map_err(|error| ServerError::ListenerAccept {
3166        message: format!("{context} unavailable: {error}"),
3167    })
3168}
3169
3170/// Locks `mutex`, RECOVERING a poisoned guard instead of failing (S4). For
3171/// lifecycle-cleanup paths only (reply delivery, expiry, cancellation, the
3172/// close sweep): removal-style operations are sound on a recovered map, and a
3173/// cleanup that silently skipped its removal would strand slots and their §5
3174/// cap admissions forever. Admission paths keep the fail-closed [`lock`].
3175fn recover_lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
3176    mutex
3177        .lock()
3178        .unwrap_or_else(std::sync::PoisonError::into_inner)
3179}