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).
This commit is contained in:
Maja Kądziołka
2024-12-03 19:37:36 +01:00
parent 20ca48c3b8
commit 6d28740896

View File

@ -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); volume = std::clamp(volume, min_volume, max_volume);
pa_cvolume_set(&pa_volume, pa_volume_.channels, volume * volume_tick); 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_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) { 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_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_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
pa_threaded_mainloop_unlock(mainloop_);
} }
void AudioBackend::toggleSinkMute() { void AudioBackend::toggleSinkMute() {
muted_ = !muted_; muted_ = !muted_;
pa_threaded_mainloop_lock(mainloop_);
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr, pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
nullptr); nullptr);
pa_threaded_mainloop_unlock(mainloop_);
} }
void AudioBackend::toggleSinkMute(bool mute) { void AudioBackend::toggleSinkMute(bool mute) {
muted_ = mute; muted_ = mute;
pa_threaded_mainloop_lock(mainloop_);
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr, pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
nullptr); nullptr);
pa_threaded_mainloop_unlock(mainloop_);
} }
void AudioBackend::toggleSourceMute() { void AudioBackend::toggleSourceMute() {
source_muted_ = !muted_; source_muted_ = !muted_;
pa_threaded_mainloop_lock(mainloop_);
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_), pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
nullptr, nullptr); nullptr, nullptr);
pa_threaded_mainloop_unlock(mainloop_);
} }
void AudioBackend::toggleSourceMute(bool mute) { void AudioBackend::toggleSourceMute(bool mute) {
source_muted_ = mute; source_muted_ = mute;
pa_threaded_mainloop_lock(mainloop_);
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_), pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
nullptr, nullptr); nullptr, nullptr);
pa_threaded_mainloop_unlock(mainloop_);
} }
bool AudioBackend::isBluetooth() { bool AudioBackend::isBluetooth() {