From 604a3c059f13f4b045a2c8b939cc46cea43ae3df Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 17:51:12 -0700 Subject: [PATCH 1/2] chore(quest): claim linger-ffi retirement Co-Authored-By: GPT-6 From 54fcb06b4c90a24bc43d06efa104c54d4481166a Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 21 Oct 2026 17:54:57 -0700 Subject: [PATCH 2/2] chore(quest): retire merged binding linger work Co-Authored-By: GPT-6 --- quest/m0/audit-quests.md | 2 -- quest/m1/README.md | 1 - quest/m1/interop-finish.md | 1 - quest/m1/linger-ffi.md | 37 ------------------------------- quest/m2/go-first-dial-failure.md | 4 ---- 5 files changed, 45 deletions(-) delete mode 100644 quest/m1/linger-ffi.md diff --git a/quest/m0/audit-quests.md b/quest/m0/audit-quests.md index 556e8afcf5..f287d531c9 100644 --- a/quest/m0/audit-quests.md +++ b/quest/m0/audit-quests.md @@ -68,8 +68,6 @@ settled. The unattended instruction was to skip uncertain choices: live session's grant, but not how independent concurrent sessions select grants and refusals. Existing reconnect code only designates one active generation alongside draining predecessors. -- [Binding linger](/quest/m1/linger-ffi.md) extends the hand-written C ABI, - while [generated C](/quest/m1/c/README.md) freezes it except claim safety. - [Catalog HLS](/quest/m1/archive/catalog-hls.md) names `.hls.z` tracks, while [compression migration](/quest/m1/compress/migrate.md) removes `.z` names. Their shared timeline edits need a consistent final name and order. diff --git a/quest/m1/README.md b/quest/m1/README.md index e8509e003e..eccd92e838 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -99,7 +99,6 @@ lands before the cut. - [Close tail drain](/quest/m1/close-tail-drain.md) - after a peer close, subscribers read the group streams their transport already delivered instead of aborting them, so a finished tail arrives on every version - [GOAWAY deadline](/quest/m1/goaway-deadline.md) - the drain deadline counts from the trigger in moq-net and js/net, so a peer that stops reading cannot stall it - [Silent-peer close](/quest/m1/quic-silent-peer-drain.md) - moq-quic closes a connection that never heard its peer without the 3 s drain -- [Binding linger](/quest/m1/linger-ffi.md) - `moq-ffi` and every binding finish a broadcast cleanly, so subscribers drain it until its media is stale - [JS linger](/quest/m1/linger-js.md) - `js/net` finishes a broadcast cleanly like Rust, and `@moq/publish` uses it when a source stops - [Gateway linger](/quest/m1/linger-gateways.md) - HLS, RTMP, SRT and RTC imports linger at a clean end, cut short by a newer epoch at the same path - [Interop finish](/quest/m1/interop-finish.md) - the interop matrix proves a clean broadcast finish across implementations diff --git a/quest/m1/interop-finish.md b/quest/m1/interop-finish.md index e048604a76..6d4a96770c 100644 --- a/quest/m1/interop-finish.md +++ b/quest/m1/interop-finish.md @@ -20,5 +20,4 @@ Public API: none. Wire: none. ## Required -- [Binding linger](/quest/m1/linger-ffi.md) - bindings finish a broadcast, so the binding interop clients can - [JS linger](/quest/m1/linger-js.md) - js/net finishes a broadcast, so the JS interop client can diff --git a/quest/m1/linger-ffi.md b/quest/m1/linger-ffi.md deleted file mode 100644 index 16df48346c..0000000000 --- a/quest/m1/linger-ffi.md +++ /dev/null @@ -1,37 +0,0 @@ -# [L] Bindings finish a broadcast cleanly - -## Goal - -A binding app whose input ended cleanly can finish its broadcast through -`moq-ffi` and every binding, so subscribers still catching up drain the -finished tracks until their media is stale. An interrupt ends the wait early. -`close` keeps its abrupt meaning. - -## Plan - -Decided (2026-10-16), mirroring the Rust library: - -- `finish()` consumes the broadcast producer and returns a finish handle, - as `broadcast::Producer::finish` returns `broadcast::Finish` in Rust. The - handle holds the finished tracks and keeps the broadcast announced; it - exposes `linger()` (how long to keep it) and an async `wait()` that - closes after the linger. Closing or dropping the handle is the interrupt - and closes at once. Rejected: a linger helper that each app holds and - sleeps on, because every publisher would repeat the hold, and an - origin-held linger, because it ties the session to what the origin holds. -- Tracks without a max-age add nothing to the wait. The alternative, - `cache_duration`, defaults to `MAX` and would linger forever. - -Expose the handle as a `moq-ffi` object with `linger()`, async `wait()` and -`close()`, and map cancelling `wait()` to each language's idiom (a dropped -future, a cancelled task or coroutine). The C ABI has no futures, but it -needs none: a C caller keeps the handle for `linger()` on its own clock and -then closes it. Report the API shape across languages before landing. - -Follow the Cross-Package Sync row for `rs/moq-ffi`: hand-written wrappers -in py, swift, kt, go, dart, c and cpp, plus the `doc/lib` pages. Check -whether `cpp/obs` should finish on a clean stop. - -Test in `moq-ffi` with a mocked clock: a late subscriber within max-age gets -the media, the broadcast closes once it passes, and cancelling closes early. -Each binding needs at least a smoke test that the call exists and returns. diff --git a/quest/m2/go-first-dial-failure.md b/quest/m2/go-first-dial-failure.md index 7fe5983f01..a161c12cf4 100644 --- a/quest/m2/go-first-dial-failure.md +++ b/quest/m2/go-first-dial-failure.md @@ -19,7 +19,3 @@ while their examples only use `Moq.connect`, which never hands back a binding PRs that also touch those pages have landed. Public API: Go additive. Wire: none. - -## Required - -- [Binding linger](/quest/m1/linger-ffi.md) - touches the same binding docs