[pulseaudio-discuss] [PATCH 13/13] Remove refcounting from ports, sinks and sources

Tanu Kaskinen tanuk at iki.fi
Tue Feb 12 11:37:03 PST 2013


Well, refcounting is not completely removed from sinks and sources,
because they are still subclasses of pa_object, which implies
refcounting, but the only ones that have a reference to a sink or
source are the sink and source implementors themselves, so in practice
there's no recounting being used. (Actually, pa_asyncmsgq also holds
a reference to a sink or source when the IO thread sends a message to
the sink or source. To avoid problems with this, the messages have to
be dispatched using pa_thread_mq_done() after shutting down the IO
thread, but before unreffing the sink or source.)

This should fix an issue reported by Luiz von Dentz: freeing the
profiles hashmap of pa_device_port accessed already freed memory,
because the hashmap uses the string stored at pa_card_profile.name
as the key, and the profiles are freed when cards are freed. Card
ports might not be freed at that time, because they are (were)
refcounted.

I first tried to fix this by removing the profiles from
pa_device_port.profiles when the profiles are removed, but there are
more things that would have to be updated too on a port when it's not
connected to a card anymore, notably the card pointer. It looked like
the card pointer could not be safely set to NULL without a significant
amount of work, and since I dislike refcounting, I decided to spend
the effort on removing the refcounting from ports instead. That
required removing it also from sinks and sources.

Ports are now owned solely by cards, except if a sink or source
doesn't belong to a card, in which case the sink or source owns its
ports. Currently there aren't any instances where a sink or source
would have port but not be a part of a card, though. So, when a card
is removed, all its sinks, sources and ports are removed too. To avoid
stale pointers, other code is expected to use the card/sink/source
unlink hooks to drop references before the objects get freed.

A note about the removed pa_sink_ref() and pa_source_ref() calls in
module-bluetooth-device: things probably break badly if the SCO sink
or source is removed while being in the "SCO over PCM" mode, but I
don't think that mode has ever worked properly if the SCO sink or
source can disappear at runtime. On the systems that I know are
using the "SCO over PCM" mode, the SCO devices stay loaded all the
time. If this seems unacceptable, I can add some code to
module-bluetooth-device to better track the SCO sink and source
existence.
---
 src/modules/alsa/alsa-mixer.c                   |    4 +---
 src/modules/alsa/alsa-ucm.c                     |    4 +---
 src/modules/bluetooth/module-bluetooth-device.c |    8 +-------
 src/modules/dbus/iface-device.c                 |   13 +++++--------
 src/modules/module-suspend-on-idle.c            |    9 ++-------
 src/pulsecore/card.c                            |   13 +++++++++----
 src/pulsecore/device-port.c                     |   15 +++------------
 src/pulsecore/device-port.h                     |    5 +----
 src/pulsecore/sink.c                            |   20 ++------------------
 src/pulsecore/source.c                          |    4 ++--
 10 files changed, 27 insertions(+), 68 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 97f25af..a47b810 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -4473,10 +4473,8 @@ static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
     if (cp)
         pa_hashmap_put(p->profiles, cp->name, cp);
 
-    if (extra) {
+    if (extra)
         pa_hashmap_put(extra, p->name, p);
-        pa_device_port_ref(p);
-    }
 
     return p;
 }
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index e01721f..61595e0 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -713,10 +713,8 @@ static void ucm_add_port_combination(
         pa_hashmap_put(port->profiles, cp->name, cp);
     }
 
-    if (hash) {
+    if (hash)
         pa_hashmap_put(hash, port->name, port);
-        pa_device_port_ref(port);
-    }
 }
 
 static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 06fe0be..33b4b16 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1548,13 +1548,11 @@ static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_
         case PROFILE_A2DP:
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "bluetooth-output"));
             pa_assert_se(pa_hashmap_put(data.sink_new_data->ports, port->name, port) >= 0);
-            pa_device_port_ref(port);
             break;
 
         case PROFILE_A2DP_SOURCE:
             pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input"));
             pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0);
-            pa_device_port_ref(port);
             break;
 
         case PROFILE_HSP:
@@ -1565,7 +1563,6 @@ static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_
                 pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-input"));
                 pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0);
             }
-            pa_device_port_ref(port);
             break;
 
         case PROFILE_HFGW:
@@ -1576,7 +1573,6 @@ static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_
                 pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-input"));
                 pa_assert_se(pa_hashmap_put(data.source_new_data->ports, port->name, port) >= 0);
             }
-            pa_device_port_ref(port);
             break;
 
         default:
@@ -2023,8 +2019,6 @@ static int start_thread(struct userdata *u) {
             return -1;
         }
 
-        pa_sink_ref(u->sink);
-        pa_source_ref(u->source);
         /* FIXME: monitor stream_fd error */
         return 0;
     }
@@ -2432,7 +2426,7 @@ static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa
 
     pa_card_add_ports(u->card, new_ports);
 
-    pa_hashmap_free(new_ports, (pa_free_cb_t) pa_device_port_unref);
+    pa_hashmap_free(new_ports, NULL);
 
     return PA_HOOK_OK;
 }
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 888d407..bc8255f 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -1208,7 +1208,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si
 
     d = pa_xnew0(pa_dbusiface_device, 1);
     d->core = core;
-    d->sink = pa_sink_ref(sink);
+    d->sink = sink;
     d->type = PA_DEVICE_TYPE_SINK;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
     d->volume = *pa_sink_get_volume(sink, FALSE);
@@ -1242,7 +1242,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_
 
     d = pa_xnew0(pa_dbusiface_device, 1);
     d->core = core;
-    d->source = pa_source_ref(source);
+    d->source = source;
     d->type = PA_DEVICE_TYPE_SOURCE;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index);
     d->volume = *pa_source_get_volume(source, FALSE);
@@ -1271,14 +1271,11 @@ void pa_dbusiface_device_free(pa_dbusiface_device *d) {
 
     pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, device_interface_info.name) >= 0);
 
-    if (d->type == PA_DEVICE_TYPE_SINK) {
+    if (d->type == PA_DEVICE_TYPE_SINK)
         pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, sink_interface_info.name) >= 0);
-        pa_sink_unref(d->sink);
-
-    } else {
+    else
         pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, source_interface_info.name) >= 0);
-        pa_source_unref(d->source);
-    }
+
     pa_hashmap_free(d->ports, (pa_free_cb_t) pa_dbusiface_device_port_free);
     pa_proplist_free(d->proplist);
     pa_dbus_protocol_unref(d->dbus_protocol);
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 02a6b91..2325308 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -344,8 +344,8 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user
 
     d = pa_xnew(struct device_info, 1);
     d->userdata = u;
-    d->source = source ? pa_source_ref(source) : NULL;
-    d->sink = sink ? pa_sink_ref(sink) : NULL;
+    d->source = source;
+    d->sink = sink;
     d->time_event = pa_core_rttime_new(c, PA_USEC_INVALID, timeout_cb, d);
     pa_hashmap_put(u->device_infos, o, d);
 
@@ -359,11 +359,6 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user
 static void device_info_free(struct device_info *d) {
     pa_assert(d);
 
-    if (d->source)
-        pa_source_unref(d->source);
-    if (d->sink)
-        pa_sink_unref(d->sink);
-
     d->userdata->core->mainloop->time_free(d->time_event);
 
     pa_xfree(d);
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index c05fc7a..aa7f58a 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -125,12 +125,14 @@ void pa_card_new_data_done(pa_card_new_data *data) {
 
     pa_proplist_free(data->proplist);
 
+    /* Ports must be freed before profiles, because the profiles hashmap in
+     * pa_device_port uses profile names as keys. */
+    if (data->ports)
+        pa_hashmap_free(data->ports, (pa_free_cb_t) pa_device_port_free);
+
     if (data->profiles)
         pa_hashmap_free(data->profiles, (pa_free_cb_t) pa_card_profile_free);
 
-    if (data->ports)
-        pa_hashmap_free(data->ports, (pa_free_cb_t) pa_device_port_unref);
-
     pa_xfree(data->name);
     pa_xfree(data->active_profile);
 }
@@ -237,7 +239,10 @@ void pa_card_free(pa_card *c) {
     pa_assert(pa_idxset_isempty(c->sources));
     pa_idxset_free(c->sources, NULL);
 
-    pa_hashmap_free(c->ports, (pa_free_cb_t) pa_device_port_unref);
+    /* Ports must be freed before profiles, because the profiles hashmap in
+     * pa_device_port uses profile names as keys. */
+    if (c->ports)
+        pa_hashmap_free(c->ports, (pa_free_cb_t) pa_device_port_free);
 
     if (c->profiles)
         pa_hashmap_free(c->profiles, (pa_free_cb_t) pa_card_profile_free);
diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c
index e0f9560..5e5d48a 100644
--- a/src/pulsecore/device-port.c
+++ b/src/pulsecore/device-port.c
@@ -24,10 +24,7 @@
 #include "device-port.h"
 #include <pulsecore/card.h>
 
-PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object);
-
-void pa_device_port_set_available(pa_device_port *p, pa_port_available_t status)
-{
+void pa_device_port_set_available(pa_device_port *p, pa_port_available_t status) {
     pa_core *core;
 
     pa_assert(p);
@@ -48,11 +45,8 @@ void pa_device_port_set_available(pa_device_port *p, pa_port_available_t status)
     pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p);
 }
 
-static void device_port_free(pa_object *o) {
-    pa_device_port *p = PA_DEVICE_PORT(o);
-
+void pa_device_port_free(pa_device_port *p) {
     pa_assert(p);
-    pa_assert(pa_device_port_refcnt(p) == 0);
 
     if (p->proplist)
         pa_proplist_free(p->proplist);
@@ -65,15 +59,12 @@ static void device_port_free(pa_object *o) {
     pa_xfree(p);
 }
 
-
 pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, size_t extra) {
     pa_device_port *p;
 
     pa_assert(name);
 
-    p = PA_DEVICE_PORT(pa_object_new_internal(PA_ALIGN(sizeof(pa_device_port)) + extra, pa_device_port_type_id, pa_device_port_check_type));
-    p->parent.free = device_port_free;
-
+    p = pa_xmalloc0(PA_ALIGN(sizeof(pa_device_port)) + extra);
     p->name = pa_xstrdup(name);
     p->description = pa_xstrdup(description);
     p->core = c;
diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h
index 40306f5..ac41bc0 100644
--- a/src/pulsecore/device-port.h
+++ b/src/pulsecore/device-port.h
@@ -39,7 +39,6 @@ typedef struct pa_device_port pa_device_port;
 #include <pulsecore/card.h>
 
 struct pa_device_port {
-    pa_object parent; /* Needed for reference counting */
     pa_core *core;
     pa_card *card;
 
@@ -58,12 +57,10 @@ struct pa_device_port {
     /* .. followed by some implementation specific data */
 };
 
-PA_DECLARE_PUBLIC_CLASS(pa_device_port);
-#define PA_DEVICE_PORT(s) (pa_device_port_cast(s))
-
 #define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
 
 pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, size_t extra);
+void pa_device_port_free(pa_device_port *p);
 
 /* The port's available status has changed */
 void pa_device_port_set_available(pa_device_port *p, pa_port_available_t available);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index e0039fa..3bbc661 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -141,7 +141,7 @@ void pa_sink_new_data_done(pa_sink_new_data *data) {
     pa_proplist_free(data->proplist);
 
     if (data->ports)
-        pa_hashmap_free(data->ports, (pa_free_cb_t) pa_device_port_unref);
+        pa_hashmap_free(data->ports, data->card ? NULL : (pa_free_cb_t) pa_device_port_free);
 
     pa_xfree(data->name);
     pa_xfree(data->active_port);
@@ -742,7 +742,7 @@ static void sink_free(pa_object *o) {
         pa_proplist_free(s->proplist);
 
     if (s->ports)
-        pa_hashmap_free(s->ports, (pa_free_cb_t) pa_device_port_unref);
+        pa_hashmap_free(s->ports, s->card ? NULL : (pa_free_cb_t) pa_device_port_free);
 
     pa_xfree(s);
 }
@@ -1109,8 +1109,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
         return;
     }
 
-    pa_sink_ref(s);
-
     if (length <= 0)
         length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
 
@@ -1168,8 +1166,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
     }
 
     inputs_drop(s, info, n, result);
-
-    pa_sink_unref(s);
 }
 
 /* Called from IO thread context */
@@ -1194,8 +1190,6 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
         return;
     }
 
-    pa_sink_ref(s);
-
     length = target->length;
     block_size_max = pa_mempool_block_size_max(s->core->mempool);
     if (length > block_size_max)
@@ -1253,8 +1247,6 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
     }
 
     inputs_drop(s, info, n, target);
-
-    pa_sink_unref(s);
 }
 
 /* Called from IO thread context */
@@ -1278,8 +1270,6 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
         return;
     }
 
-    pa_sink_ref(s);
-
     l = target->length;
     d = 0;
     while (l > 0) {
@@ -1292,8 +1282,6 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
         d += chunk.length;
         l -= chunk.length;
     }
-
-    pa_sink_unref(s);
 }
 
 /* Called from IO thread context */
@@ -1308,8 +1296,6 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
-    pa_sink_ref(s);
-
     pa_sink_render(s, length, result);
 
     if (result->length < length) {
@@ -1325,8 +1311,6 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
 
         result->length = length;
     }
-
-    pa_sink_unref(s);
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 972113d..8f0ba68 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -133,7 +133,7 @@ void pa_source_new_data_done(pa_source_new_data *data) {
     pa_proplist_free(data->proplist);
 
     if (data->ports)
-        pa_hashmap_free(data->ports, (pa_free_cb_t) pa_device_port_unref);
+        pa_hashmap_free(data->ports, data->card ? NULL : (pa_free_cb_t) pa_device_port_free);
 
     pa_xfree(data->name);
     pa_xfree(data->active_port);
@@ -672,7 +672,7 @@ static void source_free(pa_object *o) {
         pa_proplist_free(s->proplist);
 
     if (s->ports)
-        pa_hashmap_free(s->ports, (pa_free_cb_t) pa_device_port_unref);
+        pa_hashmap_free(s->ports, s->card ? NULL : (pa_free_cb_t) pa_device_port_free);
 
     pa_xfree(s);
 }
-- 
1.7.10.4



More information about the pulseaudio-discuss mailing list