From c6dd2662c3d6cb0d44d167015e222ed24f40a6fe Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 5 Apr 2025 23:00:07 -0400 Subject: [PATCH] pulseaudio: cleanup TLS every time we finish a threaded-mainloop callback. These callbacks run in a thread we don't control, but might call SDL_SetError, which will claim a TLS slot that never gets otherwise deallocated. To prevent the leak, finish each callback by explicitly freeing any thread-local data. Fixes #12732. (cherry picked from commit 3b91017682146b42e95a5bfcefbc9013615cb3b8) --- src/audio/pulseaudio/SDL_pulseaudio.c | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c index 69e8c1a84c..2403c7d0f9 100644 --- a/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/src/audio/pulseaudio/SDL_pulseaudio.c @@ -269,9 +269,19 @@ static const char *getAppName(void) return SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); } +static void ThreadedMainloopSignal(void) +{ + PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // alert waiting threads to unblock. + + // we need to kill any SDL_SetError state; we didn't create this thread + // so its SDL TLS slot will leak otherwise, so we do this every time + // we're (presumably) losing control of the thread. + SDL_CleanupTLS(); +} + static void OperationStateChangeCallback(pa_operation *o, void *userdata) { - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. + ThreadedMainloopSignal(); // just signal any waiting code, it can look up the details. } /* This function assume you are holding `mainloop`'s lock. The operation is unref'd in here, assuming @@ -313,7 +323,7 @@ static void DisconnectFromPulseServer(void) static void PulseContextStateChangeCallback(pa_context *context, void *userdata) { - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. + ThreadedMainloopSignal(); // just signal any waiting code, it can look up the details. } static bool ConnectToPulseServer(void) @@ -400,7 +410,7 @@ static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata) struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata; //SDL_Log("PULSEAUDIO WRITE CALLBACK! nbytes=%u", (unsigned int) nbytes); h->bytes_requested += nbytes; - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); + ThreadedMainloopSignal(); } // This function waits until it is possible to write a full sound buffer @@ -471,7 +481,7 @@ static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) { //SDL_Log("PULSEAUDIO READ CALLBACK! nbytes=%u", (unsigned int) nbytes); - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // the recording code queries what it needs, we just need to signal to end any wait + ThreadedMainloopSignal(); // the recording code queries what it needs, we just need to signal to end any wait } static bool PULSEAUDIO_WaitRecordingDevice(SDL_AudioDevice *device) @@ -592,7 +602,7 @@ static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *device) static void PulseStreamStateChangeCallback(pa_stream *stream, void *userdata) { - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. + ThreadedMainloopSignal(); // just signal any waiting code, it can look up the details. } static bool PULSEAUDIO_OpenDevice(SDL_AudioDevice *device) @@ -793,7 +803,7 @@ static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, if (i) { AddPulseAudioDevice(false, i->description, i->name, i->index, &i->sample_spec); } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); + ThreadedMainloopSignal(); } // This is called when PulseAudio adds a recording ("source") device. @@ -803,7 +813,7 @@ static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_la if (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) { AddPulseAudioDevice(true, i->description, i->name, i->index, &i->sample_spec); } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); + ThreadedMainloopSignal(); } static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data) @@ -828,7 +838,7 @@ static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *dat } } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); + ThreadedMainloopSignal(); } static bool FindAudioDeviceByIndex(SDL_AudioDevice *device, void *userdata) @@ -872,7 +882,7 @@ static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint3 SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByIndex, (void *)(uintptr_t)idx)); } } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); + ThreadedMainloopSignal(); } static bool CheckDefaultDevice(const bool changed, char *device_path)