fix(audio_backend): release leaked pa_operation handles
Every pa_operation* returned by the PulseAudio context introspection, subscribe and volume/mute calls was discarded without pa_operation_unref, leaking one operation object per call. Over a long session the periodic subscription events accumulate an unbounded number of these handles. Capture each returned handle and unref it (guarded against NULL) at every discard site. Callback behavior is unchanged; these calls already run under the threaded-mainloop lock, where unref is safe.
This commit is contained in:
+57
-36
@@ -4,6 +4,7 @@
|
|||||||
#include <pulse/def.h>
|
#include <pulse/def.h>
|
||||||
#include <pulse/error.h>
|
#include <pulse/error.h>
|
||||||
#include <pulse/introspect.h>
|
#include <pulse/introspect.h>
|
||||||
|
#include <pulse/operation.h>
|
||||||
#include <pulse/subscribe.h>
|
#include <pulse/subscribe.h>
|
||||||
#include <pulse/volume.h>
|
#include <pulse/volume.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
@@ -104,16 +105,17 @@ void AudioBackend::contextStateCb(pa_context* c, void* data) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PA_CONTEXT_READY:
|
case PA_CONTEXT_READY:
|
||||||
pa_context_get_server_info(c, serverInfoCb, data);
|
if (auto* o = pa_context_get_server_info(c, serverInfoCb, data)) pa_operation_unref(o);
|
||||||
pa_context_set_subscribe_callback(c, subscribeCb, data);
|
pa_context_set_subscribe_callback(c, subscribeCb, data);
|
||||||
pa_context_subscribe(c,
|
if (auto* o = pa_context_subscribe(c,
|
||||||
static_cast<enum pa_subscription_mask>(
|
static_cast<enum pa_subscription_mask>(
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK_INPUT) |
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK_INPUT) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE) |
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE) |
|
||||||
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)),
|
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)),
|
||||||
nullptr, nullptr);
|
nullptr, nullptr))
|
||||||
|
pa_operation_unref(o);
|
||||||
break;
|
break;
|
||||||
case PA_CONTEXT_FAILED:
|
case PA_CONTEXT_FAILED:
|
||||||
if (pa_context_errno(c) != PA_ERR_CONNECTIONREFUSED) {
|
if (pa_context_errno(c) != PA_ERR_CONNECTIONREFUSED) {
|
||||||
@@ -164,15 +166,18 @@ void AudioBackend::subscribeCb(pa_context* context, pa_subscription_event_type_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (facility == PA_SUBSCRIPTION_EVENT_SERVER) {
|
if (facility == PA_SUBSCRIPTION_EVENT_SERVER) {
|
||||||
pa_context_get_server_info(context, serverInfoCb, data);
|
if (auto* o = pa_context_get_server_info(context, serverInfoCb, data)) pa_operation_unref(o);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
||||||
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
|
if (auto* o = pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data))
|
||||||
|
pa_operation_unref(o);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
||||||
pa_context_get_sink_info_list(context, sinkInfoCb, data);
|
if (auto* o = pa_context_get_sink_info_list(context, sinkInfoCb, data)) pa_operation_unref(o);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
||||||
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
|
if (auto* o = pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data))
|
||||||
|
pa_operation_unref(o);
|
||||||
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
|
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
|
||||||
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
if (auto* o = pa_context_get_source_info_list(context, sourceInfoCb, data))
|
||||||
|
pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +189,9 @@ void AudioBackend::volumeModifyCb(pa_context* c, int success, void* data) {
|
|||||||
if (success != 0) {
|
if (success != 0) {
|
||||||
if ((backend->context_ != nullptr) &&
|
if ((backend->context_ != nullptr) &&
|
||||||
pa_context_get_state(backend->context_) == PA_CONTEXT_READY) {
|
pa_context_get_state(backend->context_) == PA_CONTEXT_READY) {
|
||||||
pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_, sinkInfoCb, data);
|
if (auto* o = pa_context_get_sink_info_by_index(backend->context_, backend->sink_idx_,
|
||||||
|
sinkInfoCb, data))
|
||||||
|
pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
spdlog::debug("Volume modification failed");
|
spdlog::debug("Volume modification failed");
|
||||||
@@ -196,8 +203,9 @@ void AudioBackend::sourceVolumeModifyCb(pa_context* c, int success, void* data)
|
|||||||
if (success != 0) {
|
if (success != 0) {
|
||||||
if ((backend->context_ != nullptr) &&
|
if ((backend->context_ != nullptr) &&
|
||||||
pa_context_get_state(backend->context_) == PA_CONTEXT_READY) {
|
pa_context_get_state(backend->context_) == PA_CONTEXT_READY) {
|
||||||
pa_context_get_source_info_by_index(backend->context_, backend->source_idx_, sourceInfoCb,
|
if (auto* o = pa_context_get_source_info_by_index(backend->context_, backend->source_idx_,
|
||||||
data);
|
sourceInfoCb, data))
|
||||||
|
pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
spdlog::debug("Source volume modification failed");
|
spdlog::debug("Source volume modification failed");
|
||||||
@@ -317,8 +325,8 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo
|
|||||||
backend->default_sink_name = i->default_sink_name ? i->default_sink_name : "";
|
backend->default_sink_name = i->default_sink_name ? i->default_sink_name : "";
|
||||||
backend->default_source_name_ = i->default_source_name ? i->default_source_name : "";
|
backend->default_source_name_ = i->default_source_name ? i->default_source_name : "";
|
||||||
|
|
||||||
pa_context_get_sink_info_list(context, sinkInfoCb, data);
|
if (auto* o = pa_context_get_sink_info_list(context, sinkInfoCb, data)) pa_operation_unref(o);
|
||||||
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
if (auto* o = pa_context_get_source_info_list(context, sourceInfoCb, data)) pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t AudioBackend::getVolume(PulseaudioTarget target) const {
|
uint16_t AudioBackend::getVolume(PulseaudioTarget target) const {
|
||||||
@@ -368,10 +376,13 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m
|
|||||||
// Apply the volume change
|
// Apply the volume change
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
if (is_source) {
|
if (is_source) {
|
||||||
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
|
if (auto* o = pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume,
|
||||||
this);
|
sourceVolumeModifyCb, this))
|
||||||
|
pa_operation_unref(o);
|
||||||
} else {
|
} else {
|
||||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
if (auto* o = pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume,
|
||||||
|
volumeModifyCb, this))
|
||||||
|
pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
@@ -412,10 +423,13 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
|||||||
// No need to continue with volume change if we had to create a new structure
|
// No need to continue with volume change if we had to create a new structure
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
if (is_source) {
|
if (is_source) {
|
||||||
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
|
if (auto* o = pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume,
|
||||||
this);
|
sourceVolumeModifyCb, this))
|
||||||
|
pa_operation_unref(o);
|
||||||
} else {
|
} else {
|
||||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
if (auto* o = pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume,
|
||||||
|
volumeModifyCb, this))
|
||||||
|
pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
return;
|
return;
|
||||||
@@ -458,10 +472,13 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
|||||||
// Apply the volume change
|
// Apply the volume change
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
if (is_source) {
|
if (is_source) {
|
||||||
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
|
if (auto* o = pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume,
|
||||||
this);
|
sourceVolumeModifyCb, this))
|
||||||
|
pa_operation_unref(o);
|
||||||
} else {
|
} else {
|
||||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
if (auto* o = pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume,
|
||||||
|
volumeModifyCb, this))
|
||||||
|
pa_operation_unref(o);
|
||||||
}
|
}
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
@@ -478,8 +495,9 @@ void AudioBackend::toggleSinkMute() {
|
|||||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||||
muted_ = !muted_;
|
muted_ = !muted_;
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
|
if (auto* o = pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_),
|
||||||
nullptr);
|
nullptr, nullptr))
|
||||||
|
pa_operation_unref(o);
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,8 +505,9 @@ void AudioBackend::toggleSinkMute(bool mute) {
|
|||||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||||
muted_ = mute;
|
muted_ = mute;
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
|
if (auto* o = pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_),
|
||||||
nullptr);
|
nullptr, nullptr))
|
||||||
|
pa_operation_unref(o);
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,8 +515,9 @@ void AudioBackend::toggleSourceMute() {
|
|||||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||||
source_muted_ = !source_muted_;
|
source_muted_ = !source_muted_;
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
|
if (auto* o = pa_context_set_source_mute_by_index(
|
||||||
nullptr, nullptr);
|
context_, source_idx_, static_cast<int>(source_muted_), nullptr, nullptr))
|
||||||
|
pa_operation_unref(o);
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,8 +525,9 @@ void AudioBackend::toggleSourceMute(bool mute) {
|
|||||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||||
source_muted_ = mute;
|
source_muted_ = mute;
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
|
if (auto* o = pa_context_set_source_mute_by_index(
|
||||||
nullptr, nullptr);
|
context_, source_idx_, static_cast<int>(source_muted_), nullptr, nullptr))
|
||||||
|
pa_operation_unref(o);
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user