Conversation
…overy discard API
The pull-model sinks report from a lock-free callback that cannot read SinkRecovery, so write() hands the gap over through an atomic OutageGapHandoff once the stream is alive again.
# Conflicts: # src/pipewire_sink.cpp # src/portaudio_sink.cpp # src/pulse_sink.cpp
chrisuthe
marked this pull request as ready for review
September 17, 2026 00:45
chrisuthe
added a commit
that referenced
this pull request
Sep 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #58
Depends on #55 -- merge that first. This branch merges #55's head (a6b1c23) and builds on its
SinkRecoverydiscard API.While a sink has no live stream,
write()still reports the audio as consumed, so the player's buffered-frame count takes it in, but nothing ever reports it as played. After recovery the playhead jumps ahead by the whole outage and sync cycles between Lost and Regained. This applies #55's ALSA fix to the other three device sinks.pa_stream_get_latency()timestamp under the lock, and report it together with the write's frames in one saturatedon_frames_playedcall. Frames written to a stream that dies mid-write join the gap; aclear()flush does not count.write()hands the gap to them through a new atomicOutageGapHandoff. A callback retires it only when it reports real frames with a timestamp; a PipeWire cycle without timing leaves it pending. Frames still in the ring when recovery closes a lost stream are added to the gap.clear()andconfigure()drop the gap, handoff included, without refilling the recovery budget. Nothing reports the gap fromreopen_in_place_()orpoll().Testing
SENDSPIN_CLI_WERROR=ON.sendspin-cli-testspasses 440/440. Underctestin a deeply nested build directory, two control-socket tests fail because the socket path goes past the 107-byte limit; they pass from a shorter path and are unrelated to this change.Known imprecision
These cases are rare:
clear()whose ring drain is still pending when a PortAudio or PipeWire device dies leaves a ring tail that can't be split into flushed and newer audio.configure()that fails while a write is waiting can count that one write's frames.