Skip to content

Commit 3baae30

Browse files
kixelatedclaude
andcommitted
moq-net: reshape consume_track() to return TrackPending (step 1 of the redesign)
First step of the model API redesign. consume_track() now returns a TrackPending handle (Clone) exposing subscribe()/info()/ok(); a new resolved TrackConsumer (from ok()) carries the producer for immediate subscribe/info. The previous subscribe-future TrackPending is renamed SubscribePending (one moq-mux ref). Every existing caller (consume_track(x).subscribe(None).await / .info().await) keeps the same signature. This step is API-only; the demand-side rewrite (TrackState shared by TrackRequest/TrackProducer, async subscription(), accept upgrading the request into the producer) is the follow-up that removes the current InfoRequest-era plumbing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a109347 commit 3baae30

3 files changed

Lines changed: 77 additions & 30 deletions

File tree

rs/moq-mux/src/container/source.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl VideoTransform {
4949
/// A subscription that resolves on first poll, then the live consumer.
5050
enum SourceState {
5151
/// Waiting for the subscription to resolve (blocks on the publisher's SUBSCRIBE_OK).
52-
Subscribing(moq_net::TrackPending),
52+
Subscribing(moq_net::SubscribePending),
5353
/// The resolved consumer, reading frames.
5454
Active(Consumer<HangContainer>),
5555
}

rs/moq-net/src/model/broadcast.rs

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ impl Drop for TrackRequest {
413413
}
414414
}
415415

416-
/// A pending info lookup returned by [`TrackConsumer::info`].
416+
/// A pending info lookup returned by [`TrackPending::info`].
417417
///
418418
/// Resolves once the track's immutable [`Track`] is known: synchronously if a
419419
/// producer already exists or the info is cached, otherwise once the dynamic
@@ -478,46 +478,46 @@ impl std::future::Future for InfoPending {
478478
}
479479
}
480480

481-
/// A pending subscription returned by [`TrackConsumer::subscribe`].
481+
/// A pending subscription returned by [`TrackPending::subscribe`].
482482
///
483483
/// The subscription isn't live until the publisher accepts it (for the wire,
484484
/// SUBSCRIBE_OK). It implements [`Future`], so `.await` it to get the
485485
/// [`TrackSubscriber`] (or an error). Poll-based callers can instead drive it
486486
/// with [`Self::poll_ok`] inside a `kio` poll loop.
487-
pub struct TrackPending {
488-
inner: TrackPendingInner,
487+
pub struct SubscribePending {
488+
inner: SubscribePendingInner,
489489
/// Kept alive between `Future::poll` calls so its registration in the
490490
/// resolver channel stays valid until the next poll replaces it.
491491
waiter: Option<kio::Waiter>,
492492
}
493493

494-
enum TrackPendingInner {
494+
enum SubscribePendingInner {
495495
/// Resolved synchronously: the track already existed, or it failed immediately.
496496
Ready(Result<TrackSubscriber, Error>),
497497
/// Waiting for the publisher to accept or deny via the dynamic handler.
498498
Waiting(kio::Consumer<PendingSlot>),
499499
}
500500

501-
impl TrackPending {
501+
impl SubscribePending {
502502
fn ready(result: Result<TrackSubscriber, Error>) -> Self {
503503
Self {
504-
inner: TrackPendingInner::Ready(result),
504+
inner: SubscribePendingInner::Ready(result),
505505
waiter: None,
506506
}
507507
}
508508

509509
fn waiting(consumer: kio::Consumer<PendingSlot>) -> Self {
510510
Self {
511-
inner: TrackPendingInner::Waiting(consumer),
511+
inner: SubscribePendingInner::Waiting(consumer),
512512
waiter: None,
513513
}
514514
}
515515

516516
/// Poll for the resolved [`TrackSubscriber`], without blocking.
517517
pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<TrackSubscriber, Error>> {
518518
match &self.inner {
519-
TrackPendingInner::Ready(result) => Poll::Ready(result.clone()),
520-
TrackPendingInner::Waiting(consumer) => match consumer.poll(waiter, |slot| match &**slot {
519+
SubscribePendingInner::Ready(result) => Poll::Ready(result.clone()),
520+
SubscribePendingInner::Waiting(consumer) => match consumer.poll(waiter, |slot| match &**slot {
521521
Some(result) => Poll::Ready(result.clone()),
522522
None => Poll::Pending,
523523
}) {
@@ -533,7 +533,7 @@ impl TrackPending {
533533
}
534534
}
535535

536-
impl std::future::Future for TrackPending {
536+
impl std::future::Future for SubscribePending {
537537
type Output = Result<TrackSubscriber, Error>;
538538

539539
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
@@ -725,40 +725,40 @@ impl Deref for BroadcastConsumer {
725725
impl BroadcastConsumer {
726726
/// Get a handle to a track on this broadcast.
727727
///
728-
/// This is a cheap, synchronous lookup that returns a [`TrackConsumer`] bound
728+
/// This is a cheap, synchronous lookup that returns a [`TrackPending`] bound
729729
/// to `name`. Nothing is sent to the publisher yet: call
730-
/// [`TrackConsumer::subscribe`] to open a live subscription (blocking on
730+
/// [`TrackPending::subscribe`] to open a live subscription (blocking on
731731
/// SUBSCRIBE_OK), or hold the handle and subscribe later.
732-
pub fn consume_track(&self, name: &str) -> TrackConsumer {
733-
TrackConsumer {
732+
pub fn consume_track(&self, name: &str) -> TrackPending {
733+
TrackPending {
734734
broadcast: self.clone(),
735735
name: name.to_string(),
736736
}
737737
}
738738

739-
/// Register a subscription for `name` and return a [`TrackPending`] that
739+
/// Register a subscription for `name` and return a [`SubscribePending`] that
740740
/// resolves once the publisher accepts it.
741741
///
742742
/// Reuses a live producer if one is already publishing the track (the pending
743743
/// resolves right away), otherwise queues a dynamic request served via
744744
/// [`BroadcastDynamic::requested_track`] and [`TrackRequest::accept`] (for the
745745
/// wire this is SUBSCRIBE_OK). Resolves to [`Error::NotFound`] if no dynamic
746746
/// producer exists to handle the request.
747-
fn request_subscribe(&self, name: &str, subscription: Subscription) -> TrackPending {
747+
fn request_subscribe(&self, name: &str, subscription: Subscription) -> SubscribePending {
748748
// Upgrade to a temporary producer so we can modify the state.
749749
let Some(producer) = self.state.produce() else {
750750
let err = self.state.read().abort.clone().unwrap_or(Error::Dropped);
751-
return TrackPending::ready(Err(err));
751+
return SubscribePending::ready(Err(err));
752752
};
753753
let mut state = match modify(&producer) {
754754
Ok(state) => state,
755-
Err(err) => return TrackPending::ready(Err(err)),
755+
Err(err) => return SubscribePending::ready(Err(err)),
756756
};
757757

758758
// Reuse a live producer if one is already publishing the track.
759759
if let Some(weak) = state.tracks.get(name) {
760760
if !weak.is_closed() {
761-
return TrackPending::ready(Ok(weak.subscribe(subscription)));
761+
return SubscribePending::ready(Ok(weak.subscribe(subscription)));
762762
}
763763
// Drop the stale entry and fall through to a fresh request.
764764
state.tracks.remove(name);
@@ -771,7 +771,7 @@ impl BroadcastConsumer {
771771
// Coalesce onto an in-flight request for the same name.
772772
pending.resolvers.push((subscription, slot));
773773
} else if state.dynamic == 0 {
774-
return TrackPending::ready(Err(Error::NotFound));
774+
return SubscribePending::ready(Err(Error::NotFound));
775775
} else {
776776
state.requests.insert(
777777
name.to_string(),
@@ -783,7 +783,7 @@ impl BroadcastConsumer {
783783
state.request_order.push_back(name.to_string());
784784
}
785785

786-
TrackPending::waiting(consumer)
786+
SubscribePending::waiting(consumer)
787787
}
788788

789789
/// Resolve a track's immutable info, returning an [`InfoPending`].
@@ -841,6 +841,16 @@ impl BroadcastConsumer {
841841
InfoPending::waiting(consumer)
842842
}
843843

844+
/// The live producer's weak handle for `name`, if one is currently publishing.
845+
pub(crate) fn track_weak(&self, name: &str) -> Option<TrackWeak> {
846+
self.state
847+
.read()
848+
.tracks
849+
.get(name)
850+
.filter(|weak| !weak.is_closed())
851+
.cloned()
852+
}
853+
844854
/// Block until the broadcast is closed and return the cause.
845855
///
846856
/// Returns [`Error::Dropped`] if every producer was dropped without an
@@ -878,29 +888,29 @@ impl BroadcastConsumer {
878888
/// to multiple times, and clones are cheap.
879889
// TODO: add `fetch` for one-shot retrieval of a past group range.
880890
#[derive(Clone)]
881-
pub struct TrackConsumer {
891+
pub struct TrackPending {
882892
broadcast: BroadcastConsumer,
883893
name: String,
884894
}
885895

886-
impl TrackConsumer {
896+
impl TrackPending {
887897
/// The track name this handle is bound to.
888898
pub fn name(&self) -> &str {
889899
&self.name
890900
}
891901

892902
/// Open a live subscription.
893903
///
894-
/// Returns a [`TrackPending`] that resolves once the publisher accepts the
904+
/// Returns a [`SubscribePending`] that resolves once the publisher accepts the
895905
/// subscription (SUBSCRIBE_OK on the wire). `.await` it for the
896906
/// [`TrackSubscriber`], which carries the publisher's [`Track`] and reads its
897-
/// groups; or drive it with [`TrackPending::poll_ok`] from a poll loop.
907+
/// groups; or drive it with [`SubscribePending::poll_ok`] from a poll loop.
898908
///
899909
/// `subscription` is this subscriber's preferences and feeds the producer's
900910
/// [`TrackProducer::subscription`] aggregate; pass `None` for
901911
/// [`Subscription::default`]. Concurrent subscribers to the same name coalesce
902912
/// onto one request.
903-
pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> TrackPending {
913+
pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> SubscribePending {
904914
self.broadcast
905915
.request_subscribe(&self.name, subscription.into().unwrap_or_default())
906916
}
@@ -914,6 +924,43 @@ impl TrackConsumer {
914924
pub fn info(&self) -> InfoPending {
915925
self.broadcast.request_info(&self.name)
916926
}
927+
928+
/// Resolve the track, returning a [`TrackConsumer`] once its producer exists.
929+
///
930+
/// Elicits the track (like [`Self::info`]) and waits for the publisher to
931+
/// accept it. `await`ing the [`TrackPending`] itself is equivalent.
932+
pub async fn ok(&self) -> Result<TrackConsumer, Error> {
933+
// Resolving info means the producer was accepted (and its weak inserted).
934+
self.info().await?;
935+
self.broadcast
936+
.track_weak(&self.name)
937+
.map(|weak| TrackConsumer { weak })
938+
.ok_or(Error::Cancel)
939+
}
940+
}
941+
942+
/// A resolved track: its producer exists and its immutable [`Track`] is known, so
943+
/// subscribing and reading info are immediate. Obtained from [`TrackPending::ok`].
944+
#[derive(Clone)]
945+
pub struct TrackConsumer {
946+
weak: TrackWeak,
947+
}
948+
949+
impl TrackConsumer {
950+
/// The track name.
951+
pub fn name(&self) -> &str {
952+
&self.weak.info.name
953+
}
954+
955+
/// The track's immutable publisher properties.
956+
pub fn info(&self) -> Track {
957+
self.weak.info.clone()
958+
}
959+
960+
/// Open a live subscription. Pass `None` for [`Subscription::default`].
961+
pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> TrackSubscriber {
962+
self.weak.subscribe(subscription.into().unwrap_or_default())
963+
}
917964
}
918965

919966
#[cfg(test)]
@@ -932,7 +979,7 @@ mod test {
932979
use super::*;
933980

934981
/// Subscribe and assert the result hasn't resolved yet (it stays pending until
935-
/// a publisher accepts). Returns the [`TrackPending`] to resolve after accepting.
982+
/// a publisher accepts). Returns the [`SubscribePending`] to resolve after accepting.
936983
macro_rules! subscribe_pending {
937984
($consumer:expr, $name:expr) => {{
938985
let pending = $consumer.consume_track($name).subscribe(None);

rs/moq-net/src/model/track.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ impl TrackWeak {
754754

755755
/// A live subscription to a track, used to read its groups.
756756
///
757-
/// Created via [`TrackConsumer::subscribe`](crate::TrackConsumer::subscribe), or
757+
/// Created via [`TrackPending::subscribe`](crate::TrackPending::subscribe), or
758758
/// directly from a [`TrackProducer`] for an in-process track. Carries this
759759
/// subscriber's [`Subscription`] preferences, which feed the producer's aggregate.
760760
#[derive(Clone)]

0 commit comments

Comments
 (0)