Skip to content

Commit fb8131a

Browse files
committed
fix(pulseaudio): suppress disconnect error during Stream::drop teardown
The play_all driver thread surfaces ClientError::Disconnected when the reactor drops the source's eof_tx — which happens not only on real server disconnect but also as a *direct consequence* of Stream::drop queueing DeletePlaybackStream and the server processing it. The error callback then fires "PulseAudio client disconnected" on every clean teardown, despite the connection being healthy. Check the cancel flag in the play_all spawn before emit_error, mirroring the latency thread's existing pre-poll cancel check. After a clean Stream::drop the cancel flag is already set when play_all unblocks, so the spurious notification is silenced; real disconnects (reactor exits, socket closed) leave the flag clear and still surface through the user's error_callback.
1 parent 3fecb31 commit fb8131a

2 files changed

Lines changed: 8 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
175175
- **WebAudio**: Report errors through the callback instead of panicking.
176176
- **PulseAudio**: Poisoned locks now exit the thread gracefully instead of panicking.
177177
- **PulseAudio**: Fix server-side stream leak when a `Stream` is dropped.
178+
- **PulseAudio**: Remove spurious "PulseAudio client disconnected" warnings during stream cleanup.
178179

179180
## [0.17.3] - 2026-02-18
180181

src/host/pulseaudio/stream.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ impl Stream {
241241
// when the stream is stopped by the user.
242242
let stream_clone = stream.clone();
243243
let error_callback_clone = error_callback.clone();
244+
let cancel_driver = handle.cancel.clone();
244245

245246
// The barrier prevents the worker and latency threads from firing callbacks before the
246247
// caller has received the Stream handle.
@@ -250,7 +251,12 @@ impl Stream {
250251
let driver_handle = std::thread::spawn(move || {
251252
ready_worker.wait();
252253
if let Err(e) = block_on(stream_clone.play_all()) {
253-
emit_error(&error_callback_clone, Error::from(e));
254+
// A server playback error is expected when the client
255+
// closes their stream. No need to report it back to
256+
// the client.
257+
if !cancel_driver.load(atomic::Ordering::Relaxed) {
258+
emit_error(&error_callback_clone, Error::from(e));
259+
}
254260
}
255261
});
256262

0 commit comments

Comments
 (0)