Summary
The PulseAudio backend suspends by parking the read callback only. The stream stays uncorked, so while suspended it underruns continuously and the sink stays active. A corked stream would let the server suspend the sink, which saves power on battery powered machines.
Corking was removed on purpose in 87e257b. Restoring it is blocked on an upstream bug in github.com/jfreymuth/pulse.
Why corking is off
Uncorking makes the server send a Started event. The library delivers that event with a blocking send on an unbuffered channel whose only receiver runs once, inside PlaybackStream.Start. PlaybackStream.Resume sets the state back to running and clears the underflow flag before the event arrives, so the guard on that send passes and the protocol read goroutine parks forever. Every later request then times out after one second and playback never recovers. Filed upstream as jfreymuth/pulse#52.
Sending the cork request directly through Client.RawRequest does not help: the Started event still arrives on uncork, and whether it blocks depends on the library's private running and underflow state.
Corking before parking the read callback was also tried (see the message of 87e257b) and did not fix it.
History
- c8699cc moved the cork round trip outside
c.cond.L to fix a one second stall on Suspend and Resume.
- 87e257b dropped corking entirely after the permanent hang above was found.
What to do when this is unblocked
Once jfreymuth/pulse#52 is fixed and released:
- Bump the dependency.
- Cork in
Suspend and uncork in Resume again, outside c.cond.L, with Resume waking the parked read callback before uncorking. The reasons for both orderings are in the message of c8699cc.
- Keep the read callback parking as the primary gate; corking is only for the power saving.
Until then, the tradeoff is documented in the message of 87e257b and this issue. A comment on Suspend in driver_pulseaudio_unix.go pointing here would help whoever bumps the dependency.
Filed by Claude (Claude Code), on behalf of @hajimehoshi, from a review of the PulseAudio backend's suspend path.
Summary
The PulseAudio backend suspends by parking the read callback only. The stream stays uncorked, so while suspended it underruns continuously and the sink stays active. A corked stream would let the server suspend the sink, which saves power on battery powered machines.
Corking was removed on purpose in 87e257b. Restoring it is blocked on an upstream bug in
github.com/jfreymuth/pulse.Why corking is off
Uncorking makes the server send a
Startedevent. The library delivers that event with a blocking send on an unbuffered channel whose only receiver runs once, insidePlaybackStream.Start.PlaybackStream.Resumesets the state back to running and clears the underflow flag before the event arrives, so the guard on that send passes and the protocol read goroutine parks forever. Every later request then times out after one second and playback never recovers. Filed upstream as jfreymuth/pulse#52.Sending the cork request directly through
Client.RawRequestdoes not help: theStartedevent still arrives on uncork, and whether it blocks depends on the library's private running and underflow state.Corking before parking the read callback was also tried (see the message of 87e257b) and did not fix it.
History
c.cond.Lto fix a one second stall on Suspend and Resume.What to do when this is unblocked
Once jfreymuth/pulse#52 is fixed and released:
Suspendand uncork inResumeagain, outsidec.cond.L, withResumewaking the parked read callback before uncorking. The reasons for both orderings are in the message of c8699cc.Until then, the tradeoff is documented in the message of 87e257b and this issue. A comment on
Suspendindriver_pulseaudio_unix.gopointing here would help whoever bumps the dependency.Filed by Claude (Claude Code), on behalf of @hajimehoshi, from a review of the PulseAudio backend's suspend path.