LibMedia: Stop using threading-unsafe Weakable for PulseAudioContext

We can't control whether the instantiation mutex is held when
~Weakable() is called, so we need to implement this via a static raw
pointer instead to ensure that all operations on it are effectively
atomic.
This commit is contained in:
Zaggy1024
2025-10-24 13:09:16 -05:00
committed by Gregory Bertilson
parent 3e485ba51c
commit 13cf0e72d8
Notes: github-actions[bot] 2025-11-24 21:10:44 +00:00
3 changed files with 24 additions and 20 deletions

View File

@@ -6,35 +6,28 @@
#include "PulseAudioWrappers.h"
#include <AK/WeakPtr.h>
#include <LibThreading/Mutex.h>
namespace Audio {
WeakPtr<PulseAudioContext> PulseAudioContext::weak_instance()
{
// Use a weak pointer to allow the context to be shut down if we stop outputting audio.
static WeakPtr<PulseAudioContext> the_instance;
return the_instance;
}
static PulseAudioContext* s_pulse_audio_context;
static Threading::Mutex s_pulse_audio_context_mutex;
ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::the()
{
static Threading::Mutex instantiation_mutex;
auto instantiation_locker = Threading::MutexLocker(s_pulse_audio_context_mutex);
// Lock and unlock the mutex to ensure that the mutex is fully unlocked at application
// exit.
auto atexit_result = atexit([]() {
instantiation_mutex.lock();
instantiation_mutex.unlock();
s_pulse_audio_context_mutex.lock();
s_pulse_audio_context_mutex.unlock();
});
if (atexit_result) {
return Error::from_string_literal("Unable to set PulseAudioContext atexit action");
}
auto instantiation_locker = Threading::MutexLocker(instantiation_mutex);
auto the_instance = weak_instance();
RefPtr<PulseAudioContext> strong_instance_pointer = the_instance.strong_ref();
RefPtr<PulseAudioContext> strong_instance_pointer = RefPtr<PulseAudioContext>(s_pulse_audio_context);
if (strong_instance_pointer == nullptr) {
auto* main_loop = pa_threaded_mainloop_new();
@@ -99,12 +92,18 @@ ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::the()
pa_context_set_state_callback(context, nullptr, nullptr);
}
the_instance = strong_instance_pointer;
s_pulse_audio_context = strong_instance_pointer;
}
return strong_instance_pointer.release_nonnull();
}
bool PulseAudioContext::is_connected()
{
auto locker = Threading::MutexLocker(s_pulse_audio_context_mutex);
return s_pulse_audio_context != nullptr;
}
PulseAudioContext::PulseAudioContext(pa_threaded_mainloop* main_loop, pa_mainloop_api* api, pa_context* context)
: m_main_loop(main_loop)
, m_api(api)
@@ -114,13 +113,17 @@ PulseAudioContext::PulseAudioContext(pa_threaded_mainloop* main_loop, pa_mainloo
PulseAudioContext::~PulseAudioContext()
{
auto locker = Threading::MutexLocker(s_pulse_audio_context_mutex);
{
auto locker = main_loop_locker();
auto loop_locker = main_loop_locker();
pa_context_disconnect(m_context);
pa_context_unref(m_context);
}
pa_threaded_mainloop_stop(m_main_loop);
pa_threaded_mainloop_free(m_main_loop);
s_pulse_audio_context = nullptr;
}
bool PulseAudioContext::current_thread_is_main_loop_thread()

View File

@@ -36,11 +36,10 @@ using PulseAudioDataRequestCallback = Function<ReadonlyBytes(PulseAudioStream&,
// A wrapper around the PulseAudio main loop and context structs.
// Generally, only one instance of this should be needed for a single process.
class MEDIA_API PulseAudioContext
: public AtomicRefCounted<PulseAudioContext>
, public Weakable<PulseAudioContext> {
: public AtomicRefCounted<PulseAudioContext> {
public:
static AK::WeakPtr<PulseAudioContext> weak_instance();
static ErrorOr<NonnullRefPtr<PulseAudioContext>> the();
static bool is_connected();
explicit PulseAudioContext(pa_threaded_mainloop*, pa_mainloop_api*, pa_context*);
PulseAudioContext(PulseAudioContext const& other) = delete;
@@ -69,6 +68,8 @@ public:
private:
friend class PulseAudioStream;
PulseAudioContext*& nullable_instance();
pa_threaded_mainloop* m_main_loop { nullptr };
pa_mainloop_api* m_api { nullptr };
pa_context* m_context;

View File

@@ -42,6 +42,6 @@ TEST_CASE(create_and_destroy_playback_stream)
}
#if defined(HAVE_PULSEAUDIO)
VERIFY(!Audio::PulseAudioContext::weak_instance());
VERIFY(!Audio::PulseAudioContext::is_connected());
#endif
}