Skip to content

Pulse: fix hang when the input half of a duplex stream fails to open - #490

Merged
garyscavone merged 1 commit into
thestk:masterfrom
m1kor:pulse-duplex-open-error-hang
Oct 16, 2026
Merged

garyscavone merged 1 commit into
thestk:masterfrom
m1kor:pulse-duplex-open-error-hang

Conversation

@m1kor

@m1kor m1kor commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

When a duplex stream is opened with the PulseAudio API and the input half fails to open, openStream never returns.

Cause

RtApi::openStream calls probeDeviceOpen for OUTPUT, then for INPUT. The OUTPUT call starts the callback thread, which waits in callbackEvent on pah->runnable_cv because the stream is stopped. If the INPUT call then fails, the error: path in RtApiPulse::probeDeviceOpen calls pthread_cond_destroy( &pah->runnable_cv ) while that thread is still waiting on it. glibc's pthread_cond_destroy waits 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:

caller:           __pthread_cond_destroy
                  RtApiPulse::probeDeviceOpen (mode=INPUT, channels=8)
                  RtApi::openStream
callback thread:  __pthread_cond_wait
                  RtApiPulse::callbackEvent
                  pulseaudio_callback

Fix

In the error path, stop the callback thread the same way closeStream does before destroying the condition variable: clear isRunning, set runnable and signal under the stream mutex, then join. The pa_simple streams 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.

RtAudio rt(RtAudio::LINUX_PULSE);
RtAudio::StreamParameters out{clarett, 2, 0}, in{clarett, 8, 0};
unsigned frames = 256;
rt.openStream(&out, &in, RTAUDIO_FLOAT32, 48000, &frames, callback);  // hangs

Tested on current master (ea2c88c), built with the PulseAudio API only:

  • Before: the call above hangs.
  • After: it returns RTAUDIO_SYSTEM_ERROR ("error connecting input to PulseAudio server."), and the same RtAudio object 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.

… 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.
@garyscavone
garyscavone merged commit 06f97b6 into thestk:master Oct 16, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants