diff --git a/crates/orchestrator-core/src/inbox.rs b/crates/orchestrator-core/src/inbox.rs index 15a7ef8..805902a 100644 --- a/crates/orchestrator-core/src/inbox.rs +++ b/crates/orchestrator-core/src/inbox.rs @@ -42,13 +42,21 @@ impl InboxRegistry { /// Allocate a new inbox id and register the sender side. The /// caller awaits the returned `Receiver`; the dispatcher delivers /// the outcome via `deliver(id, …)`. + /// + /// F-Q-006: panic on poison — a poisoned mutex means a prior panic + /// inside the lock-holding code path, so the process is unrecoverable. + /// Silently swallowing it (the old behaviour) leaked a dead id: the + /// sender never got inserted, the later `deliver(id, …)` saw no + /// entry and returned Abandoned, and the caller's `await rx` blocked + /// until the orchestrator's outer timeout. Sister registries + /// (RouteTable, AppDomainTable) already `.expect()` here — settle + /// on the same loud policy. #[must_use] pub fn register(&self) -> (Uuid, oneshot::Receiver) { let id = Uuid::new_v4(); let (tx, rx) = oneshot::channel(); - if let Ok(mut g) = self.inner.lock() { - g.insert(id, tx); - } + let mut g = self.inner.lock().expect("inbox registry poisoned"); + g.insert(id, tx); (id, rx) } @@ -56,10 +64,8 @@ impl InboxRegistry { /// Drops the sender so any future `deliver` returns `Abandoned`. /// Returns `true` if the receiver was still registered. pub fn cancel(&self, id: Uuid) -> bool { - self.inner - .lock() - .map(|mut g| g.remove(&id).is_some()) - .unwrap_or(false) + let mut g = self.inner.lock().expect("inbox registry poisoned"); + g.remove(&id).is_some() } } @@ -72,9 +78,9 @@ impl Default for InboxRegistry { #[async_trait] impl InboxResolver for InboxRegistry { async fn deliver(&self, inbox_id: Uuid, result: InboxResult) -> InboxDeliveryOutcome { - let Ok(mut g) = self.inner.lock() else { - return InboxDeliveryOutcome::Abandoned; - }; + // F-Q-006 + F-Q-010: panic on poison, matching the registry's + // register/cancel policy and the sister registries. + let mut g = self.inner.lock().expect("inbox registry poisoned"); let Some(tx) = g.remove(&inbox_id) else { return InboxDeliveryOutcome::Abandoned; };