Repository navigation
Pulse: fix hang when the input half of a duplex stream fails to open - #490
Merged
Merged
Conversation
… failure When the input half of a duplex PulseAudio stream fails to open, the error path in RtApiPulse::probeDeviceOpen destroyed runnable_cv while the callback thread started for the output half was still waiting on it. With glibc pthread_cond_destroy blocks until the waiter leaves, so openStream never returned. Stop and join the thread first, as closeStream does, and free the already opened simple streams.
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.
When a duplex stream is opened with the PulseAudio API and the input half fails to open,
openStreamnever returns.Cause
RtApi::openStreamcallsprobeDeviceOpenfor OUTPUT, then for INPUT. The OUTPUT call starts the callback thread, which waits incallbackEventonpah->runnable_cvbecause the stream is stopped. If the INPUT call then fails, theerror:path inRtApiPulse::probeDeviceOpencallspthread_cond_destroy( &pah->runnable_cv )while that thread is still waiting on it. glibc'spthread_cond_destroywaits for existing waiters to leave, so the caller blocks forever. On other implementations it's undefined behaviour: the thread later touches a destroyed condition variable and a deleted handle.Stacks of the hung process:
Fix
In the error path, stop the callback thread the same way
closeStreamdoes before destroying the condition variable: clearisRunning, setrunnableand signal under the stream mutex, then join. Thepa_simplestreams that were already opened are freed too; previously they leaked.Reproduction
Seen on PipeWire (pipewire-pulse) with a Focusrite Clarett 4Pre USB. Its source has 10 channels; a record stream asking for 8 fails in
pa_simple_new, and one asking for 4 succeeds.Tested on current
master(ea2c88c), built with the PulseAudio API only:RTAUDIO_SYSTEM_ERROR("error connecting input to PulseAudio server."), and the sameRtAudioobject then opens and runs a duplex stream with 4 input channels.Output-only and input-only streams, and duplex streams whose halves both open, don't go through this path.