From 6d28740896b33005cfe6344719bcebb8de0cabfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Tue, 3 Dec 2024 19:37:36 +0100 Subject: [PATCH] Add locking for interacting with the pulseaudio thread Before this commit, Waybar would sometimes get into a state where it would consume 100% of a CPU core, and the pulseaudio widget would stop responding to volume adjustments. In this state, the pulseaudio mainloop thread would spin, with the counter of enabled defer events at 1, but no actual enabled defer event in the list to get the counter back to zero after an iteration in the mainloop. This could happen if the unsynchronized interactions with the mainloop thread happened to modify the list of deferred events at the same time as the mainloop. This commit introduces locking in accordance with the PulseAudio documentation on the threaded mainloop: > The lock needs to be held whenever you call any PulseAudio function that > uses an object associated with this main loop. Those objects include > pa_mainloop, pa_context, pa_stream and pa_operation, and the various event > objects (pa_io_event, pa_time_event, pa_defer_event). --- src/util/audio_backend.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 73aac148..807b5dc7 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -236,7 +236,9 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m volume = std::clamp(volume, min_volume, max_volume); pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume) { @@ -265,31 +267,41 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma pa_cvolume_dec(&pa_volume, change); } } + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSinkMute() { muted_ = !muted_; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSinkMute(bool mute) { muted_ = mute; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast(muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSourceMute() { source_muted_ = !muted_; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } void AudioBackend::toggleSourceMute(bool mute) { source_muted_ = mute; + pa_threaded_mainloop_lock(mainloop_); pa_context_set_source_mute_by_index(context_, source_idx_, static_cast(source_muted_), nullptr, nullptr); + pa_threaded_mainloop_unlock(mainloop_); } bool AudioBackend::isBluetooth() {