From da3f1b09ebf05ed1329018af62b07e4b85f3c6ee Mon Sep 17 00:00:00 2001 From: "pullfrog[bot]" <226033991+pullfrog[bot]@users.noreply.github.com> Date: Thu, 5 Nov 2026 22:46:40 +0000 Subject: [PATCH] test(ffi): hold the route that keeps the panicking origin alive The container-importer supervision test dropped the route it created as a temporary, leaving only broadcast handles. Under the new ownership the route is the origin's owner, so the driver could observe every owner gone and finish cleanly before the injected panic, making the test race the driver and fail under load. Hold the route until the failure is recorded, then drop it before the importer assertions. Also spell out that the route, not the broadcast, keeps the driver running. --- rs/moq-ffi/src/origin.rs | 7 ++++++- rs/moq-net/src/model/origin.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rs/moq-ffi/src/origin.rs b/rs/moq-ffi/src/origin.rs index fbaa891ab2c..8ffe5fc59c8 100644 --- a/rs/moq-ffi/src/origin.rs +++ b/rs/moq-ffi/src/origin.rs @@ -741,7 +741,10 @@ mod supervision_tests { inner, driver: completion.clone(), }); - let broadcast = origin.route("retained".into(), hidden()).unwrap().broadcast().unwrap(); + // The route owns the broadcast, and it is the only handle that keeps the origin + // running once the producer is gone. + let route = origin.route("retained".into(), hidden()).unwrap(); + let broadcast = route.broadcast().unwrap(); // One importer shares a timebase and one doesn't, so both reservation paths carry the driver. let timebase = MoqMediaTimebase::new(broadcast.clone()).unwrap(); let container = MoqMediaContainerProducer::new( @@ -770,6 +773,8 @@ mod supervision_tests { .unwrap_or_else(|_| unreachable!("test keeps its panic trigger open")) = true; assert!(matches!(completion.finished().await, Err(MoqError::Task { .. }))); drop(completion); + // The origin's last owner is gone too, so only the importers still hold the failure. + drop(route); assert!(matches!(container.cut(), Err(MoqError::Task { .. }))); assert!(matches!(independent.write(Vec::new()), Err(MoqError::Task { .. }))); assert!(matches!(container.finish(), Err(MoqError::Task { .. }))); diff --git a/rs/moq-net/src/model/origin.rs b/rs/moq-net/src/model/origin.rs index e3ce501a606..00ef399a985 100644 --- a/rs/moq-net/src/model/origin.rs +++ b/rs/moq-net/src/model/origin.rs @@ -2945,8 +2945,8 @@ struct Serving { /// /// Clones share one route. Dropping the last clone retracts it, and so does closing /// its broadcast. The broadcast lives on for the subscriptions already on it for as -/// long as another of its producer handles does. It keeps the origin's [`Driver`] -/// running, so a publisher can drop every [`Producer`] and keep serving. +/// long as another of its producer handles does. The route keeps the origin's +/// [`Driver`] running, so a publisher can drop every [`Producer`] and keep serving. #[derive(Clone)] #[must_use = "dropping the last clone of a route retracts it"] pub struct Route {