@@ -224,12 +224,43 @@ class MoqxRelay::LocalSubscribeFilter final : public PublisherCrossExecFilter {
224224 folly::coro::Task<FetchResult>
225225 fetch (moxygen::Fetch fetch, std::shared_ptr<moxygen::FetchConsumer> consumer) override {
226226 auto session = moxygen::MoQSession::getRequestSession ();
227- auto resolved = relay_->fetchOnSubscriberExec (std::move (fetch), session);
228- // Joining resolved/deferred on subscriberExec; base filter wraps + hops to relayExec_.
229- return PublisherCrossExecFilter::fetch (std::move (resolved), std::move (consumer));
227+ auto setup = relay_->resolveJoiningFetchOnSubscriberExec (fetch, *session);
228+ if (setup.hasError ()) {
229+ return folly::coro::makeTask<FetchResult>(folly::makeUnexpected (std::move (setup.error ())));
230+ }
231+ if (!setup->isReady ()) {
232+ return fetchAfterSetup (
233+ std::move (*setup),
234+ std::move (fetch),
235+ std::move (session),
236+ std::move (consumer)
237+ );
238+ }
239+ // Base filter wraps + hops to relayExec_.
240+ return PublisherCrossExecFilter::fetch (std::move (fetch), std::move (consumer));
230241 }
231242
232243private:
244+ folly::coro::Task<FetchResult> fetchAfterSetup (
245+ folly::SemiFuture<folly::Unit> setup,
246+ moxygen::Fetch fetch,
247+ std::shared_ptr<moxygen::MoQSession> session,
248+ std::shared_ptr<moxygen::FetchConsumer> consumer
249+ ) {
250+ // A joining fetch pipelined behind its SUBSCRIBE resolves against the Largest from that
251+ // setup. The setup can finish before this task runs. Another SUBSCRIBE's setup may replace a
252+ // failed one.
253+ do {
254+ co_await folly::coro::co_awaitTry (std::move (setup));
255+ auto next = relay_->resolveJoiningFetchOnSubscriberExec (fetch, *session);
256+ if (next.hasError ()) {
257+ co_return folly::makeUnexpected (std::move (next.error ()));
258+ }
259+ setup = std::move (*next);
260+ } while (!setup.isReady ());
261+ co_return co_await PublisherCrossExecFilter::fetch (std::move (fetch), std::move (consumer));
262+ }
263+
233264 std::shared_ptr<MoqxRelay> relay_;
234265};
235266
@@ -2541,24 +2572,32 @@ MoqxRelay::trackStatusOnSubscriberExec(const TrackStatus& req) {
25412572 return Publisher::TrackStatusResult (buildTrackStatusOk (*localFwd, /* hasHandle=*/ true , req));
25422573}
25432574
2544- Fetch MoqxRelay::fetchOnSubscriberExec (Fetch fetch, const std::shared_ptr<MoQSession>& session) {
2575+ folly::Expected<folly::SemiFuture<folly::Unit>, FetchError>
2576+ MoqxRelay::resolveJoiningFetchOnSubscriberExec (Fetch& fetch, const MoQSession& session) {
25452577 auto [standalone, joining] = fetchType (fetch);
25462578 if (!joining) {
2547- return fetch ;
2579+ return folly::makeSemiFuture () ;
25482580 }
2581+ // All of a session's subscriptions are on its thread's forwarders.
25492582 auto * localReg = tlForwarders_.get ();
2550- auto localFwd = localReg ? localReg->getIfReady (fetch.fullTrackName ) : nullptr ;
2551- if (localFwd) {
2552- auto res = localFwd->resolveJoiningFetch (session->sessionId (), *joining);
2553- if (res.hasValue ()) {
2554- fetch.args = StandaloneFetch (res.value ().start , res.value ().end );
2555- return fetch;
2556- }
2583+ auto state = localReg ? localReg->lookup (fetch.fullTrackName ) : LocalForwarderRegistry::State{};
2584+ if (auto * pending = std::get_if<LocalForwarderRegistry::Pending>(&state)) {
2585+ return std::move (pending->ready );
2586+ }
2587+ auto * ready = std::get_if<LocalForwarderRegistry::Ready>(&state);
2588+ if (!ready) {
2589+ return folly::makeUnexpected (FetchError{
2590+ fetch.requestID ,
2591+ FetchErrorCode::DOES_NOT_EXIST ,
2592+ " No subscription for joining fetch"
2593+ });
2594+ }
2595+ auto res = ready->forwarder ->resolveJoiningFetch (session.sessionId (), *joining);
2596+ if (res.hasError ()) {
2597+ return folly::makeUnexpected (std::move (res.error ()));
25572598 }
2558- // Not resolvable yet (no largest, or pre-PUBLISH_OK on draft-18's separate
2559- // stream): defer upstream by track name. Rare; fails if there's no upstream.
2560- joining->joiningRequestID = std::nullopt ;
2561- return fetch;
2599+ fetch.args = StandaloneFetch (res.value ().start , res.value ().end );
2600+ return folly::makeSemiFuture ();
25622601}
25632602
25642603folly::coro::Task<Publisher::FetchResult>
@@ -2572,7 +2611,7 @@ MoqxRelay::fetchImpl(Fetch fetch, std::shared_ptr<FetchConsumer> consumer) {
25722611 }
25732612
25742613 auto [standalone, joining] = fetchType (fetch);
2575- // LF mode resolves/defers joining in fetchOnSubscriberExec on the subscriber exec.
2614+ // LF mode resolves joining fetches on the subscriber exec.
25762615 if (joining && mode () != Mode::LocalForwarder) {
25772616 auto fetchView = registry_.getFetchView (fetch.fullTrackName );
25782617 if (!fetchView) {
0 commit comments