From ebe5fa780915fc0e8c10f793800b1d0db1cc140a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sat, 1 May 2021 09:33:06 +0200 Subject: [PATCH] dbus: clean up sources and connections Set source user data for all dbus sources and set a destroy notify when removed. Remove the dbus user data to remove the source user data. Clean up remaining sources when destoying a connection Clean up remaining connections when freeing the dbus plugins. Fixes #1114 --- spa/plugins/support/dbus.c | 101 +++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 38 deletions(-) diff --git a/spa/plugins/support/dbus.c b/spa/plugins/support/dbus.c index 169cce922..b33c44f94 100644 --- a/spa/plugins/support/dbus.c +++ b/spa/plugins/support/dbus.c @@ -50,6 +50,12 @@ struct impl { struct spa_list connection_list; }; +struct source_data { + struct spa_list link; + struct spa_source *source; + struct connection *conn; +}; + struct connection { struct spa_list link; @@ -57,8 +63,20 @@ struct connection { struct impl *impl; DBusConnection *conn; struct spa_source *dispatch_event; + struct spa_list source_list; }; +static void source_data_free(void *data) +{ + struct source_data *d = data; + struct connection *conn = d->conn; + struct impl *impl = conn->impl; + + spa_list_remove(&d->link); + spa_loop_utils_destroy_source(impl->utils, d->source); + free(d); +} + static void dispatch_cb(void *userdata) { struct connection *conn = userdata; @@ -128,17 +146,20 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *userdata) { struct connection *conn = userdata; struct impl *impl = conn->impl; - struct spa_source *source; + struct source_data *data; spa_log_debug(impl->log, "add watch %p %d", watch, dbus_watch_get_unix_fd(watch)); + data = calloc(1, sizeof(struct source_data)); + data->conn = conn; /* we dup because dbus tends to add the same fd multiple times and our epoll * implementation does not like that */ - source = spa_loop_utils_add_io(impl->utils, + data->source = spa_loop_utils_add_io(impl->utils, dup(dbus_watch_get_unix_fd(watch)), dbus_to_io(watch), true, handle_io_event, watch); + spa_list_append(&conn->source_list, &data->link); - dbus_watch_set_data(watch, source, NULL); + dbus_watch_set_data(watch, data, source_data_free); return TRUE; } @@ -146,39 +167,29 @@ static void remove_watch(DBusWatch *watch, void *userdata) { struct connection *conn = userdata; struct impl *impl = conn->impl; - struct spa_source *source; - spa_log_debug(impl->log, "remove watch %p", watch); - - if ((source = dbus_watch_get_data(watch))) - spa_loop_utils_destroy_source(conn->impl->utils, source); + dbus_watch_set_data(watch, NULL, NULL); } static void toggle_watch(DBusWatch *watch, void *userdata) { struct connection *conn = userdata; struct impl *impl = conn->impl; - struct spa_source *source; + struct source_data *data; spa_log_debug(impl->log, "toggle watch %p", watch); - source = dbus_watch_get_data(watch); - - spa_loop_utils_update_io(impl->utils, source, dbus_to_io(watch)); + if ((data = dbus_watch_get_data(watch)) != NULL) + spa_loop_utils_update_io(impl->utils, data->source, dbus_to_io(watch)); } -struct timeout_data { - struct spa_source *source; - struct connection *conn; -}; - static void handle_timer_event(void *userdata, uint64_t expirations) { DBusTimeout *timeout = userdata; uint64_t t; struct timespec ts; - struct timeout_data *data = dbus_timeout_get_data(timeout); + struct source_data *data = dbus_timeout_get_data(timeout); struct connection *conn = data->conn; struct impl *impl = conn->impl; @@ -199,7 +210,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata) struct connection *conn = userdata; struct impl *impl = conn->impl; struct timespec ts; - struct timeout_data *data; + struct source_data *data; uint64_t t; if (!dbus_timeout_get_enabled(timeout)) @@ -207,10 +218,12 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata) spa_log_debug(impl->log, "add timeout %p conn:%p impl:%p", timeout, conn, impl); - data = calloc(1, sizeof(struct timeout_data)); + data = calloc(1, sizeof(struct source_data)); data->conn = conn; data->source = spa_loop_utils_add_timer(impl->utils, handle_timer_event, timeout); - dbus_timeout_set_data(timeout, data, NULL); + spa_list_append(&conn->source_list, &data->link); + + dbus_timeout_set_data(timeout, data, source_data_free); t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC; ts.tv_sec = t / SPA_NSEC_PER_SEC; @@ -224,21 +237,15 @@ static void remove_timeout(DBusTimeout *timeout, void *userdata) { struct connection *conn = userdata; struct impl *impl = conn->impl; - struct timeout_data *data; - spa_log_debug(impl->log, "remove timeout %p conn:%p impl:%p", timeout, conn, impl); - - if ((data = dbus_timeout_get_data(timeout))) { - spa_loop_utils_destroy_source(impl->utils, data->source); - free(data); - } + dbus_timeout_set_data(timeout, NULL, NULL); } static void toggle_timeout(DBusTimeout *timeout, void *userdata) { struct connection *conn = userdata; struct impl *impl = conn->impl; - struct timeout_data *data; + struct source_data *data; struct timespec ts, *tsp; data = dbus_timeout_get_data(timeout); @@ -271,6 +278,24 @@ impl_connection_get(struct spa_dbus_connection *conn) return this->conn; } +static void connection_free(struct connection *conn) +{ + struct impl *impl = conn->impl; + struct source_data *data; + + spa_list_remove(&conn->link); + + dbus_connection_close(conn->conn); + dbus_connection_unref(conn->conn); + + spa_list_consume(data, &conn->source_list, link) + source_data_free(data); + + spa_loop_utils_destroy_source(impl->utils, conn->dispatch_event); + + free(conn); +} + static void impl_connection_destroy(struct spa_dbus_connection *conn) { @@ -278,15 +303,7 @@ impl_connection_destroy(struct spa_dbus_connection *conn) struct impl *impl = this->impl; spa_log_debug(impl->log, "destroy conn %p", this); - - dbus_connection_close(this->conn); - dbus_connection_unref(this->conn); - - spa_loop_utils_destroy_source(impl->utils, this->dispatch_event); - - spa_list_remove(&this->link); - - free(this); + connection_free(this); } static const struct spa_dbus_connection impl_connection = { @@ -318,6 +335,8 @@ impl_get_connection(void *object, if (conn->dispatch_event == NULL) goto no_event; + spa_list_init(&conn->source_list); + dbus_connection_set_exit_on_disconnect(conn->conn, false); dbus_connection_set_dispatch_status_function(conn->conn, dispatch_status, conn, NULL); dbus_connection_set_watch_functions(conn->conn, add_watch, remove_watch, toggle_watch, conn, @@ -375,7 +394,13 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void static int impl_clear(struct spa_handle *handle) { + struct impl *impl = (struct impl *) handle; + struct connection *conn; + spa_return_val_if_fail(handle != NULL, -EINVAL); + + spa_list_consume(conn, &impl->connection_list, link) + connection_free(conn); return 0; }