diff --git a/rs/moq-net/src/model/group.rs b/rs/moq-net/src/model/group.rs index e199da5f88..db1e74f5a7 100644 --- a/rs/moq-net/src/model/group.rs +++ b/rs/moq-net/src/model/group.rs @@ -1129,7 +1129,10 @@ pub struct Consumer { } /// Subscriber-specific policy for expiring a group after it was handed out. -pub(crate) trait Expiry: Send + Sync { +/// +/// Unwind safe so the [`Consumer`] holding it is, and so the published +/// [`track::Fetching`] that holds a consumer stays unwind safe too. +pub(crate) trait Expiry: Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe { /// Return whether the group is stale, registering `waiter` for anything that /// could change the answer while the group remains live. fn is_expired(&self, waiter: &kio::Waiter) -> bool; diff --git a/rs/moq-net/src/model/track.rs b/rs/moq-net/src/model/track.rs index d526934dff..b9b9765d44 100644 --- a/rs/moq-net/src/model/track.rs +++ b/rs/moq-net/src/model/track.rs @@ -8100,6 +8100,15 @@ mod test { assert_eq!(retry.await.unwrap().sequence, 5); } + /// Dropping an auto trait from a published type is a semver break, so the group + /// consumer a cached fetch holds must not cost `Fetching` its unwind safety. + #[test] + fn fetching_is_unwind_safe() { + fn assert_unwind_safe() {} + assert_unwind_safe::(); + assert_unwind_safe::(); + } + /// A fetch that hits the cache holds the group until polled, so a handler that aborts /// the group once nobody wants it (an abandoned upstream fetch) leaves it alone. #[tokio::test]