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
This commit is contained in:
Wim Taymans 2021-05-01 09:33:06 +02:00
parent 85b136d8ce
commit ebe5fa7809

View file

@ -50,6 +50,12 @@ struct impl {
struct spa_list connection_list; struct spa_list connection_list;
}; };
struct source_data {
struct spa_list link;
struct spa_source *source;
struct connection *conn;
};
struct connection { struct connection {
struct spa_list link; struct spa_list link;
@ -57,8 +63,20 @@ struct connection {
struct impl *impl; struct impl *impl;
DBusConnection *conn; DBusConnection *conn;
struct spa_source *dispatch_event; 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) static void dispatch_cb(void *userdata)
{ {
struct connection *conn = userdata; struct connection *conn = userdata;
@ -128,17 +146,20 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *userdata)
{ {
struct connection *conn = userdata; struct connection *conn = userdata;
struct impl *impl = conn->impl; 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)); 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 /* we dup because dbus tends to add the same fd multiple times and our epoll
* implementation does not like that */ * 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)), dup(dbus_watch_get_unix_fd(watch)),
dbus_to_io(watch), true, handle_io_event, 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; return TRUE;
} }
@ -146,39 +167,29 @@ static void remove_watch(DBusWatch *watch, void *userdata)
{ {
struct connection *conn = userdata; struct connection *conn = userdata;
struct impl *impl = conn->impl; struct impl *impl = conn->impl;
struct spa_source *source;
spa_log_debug(impl->log, "remove watch %p", watch); spa_log_debug(impl->log, "remove watch %p", watch);
dbus_watch_set_data(watch, NULL, NULL);
if ((source = dbus_watch_get_data(watch)))
spa_loop_utils_destroy_source(conn->impl->utils, source);
} }
static void toggle_watch(DBusWatch *watch, void *userdata) static void toggle_watch(DBusWatch *watch, void *userdata)
{ {
struct connection *conn = userdata; struct connection *conn = userdata;
struct impl *impl = conn->impl; struct impl *impl = conn->impl;
struct spa_source *source; struct source_data *data;
spa_log_debug(impl->log, "toggle watch %p", watch); spa_log_debug(impl->log, "toggle watch %p", watch);
source = dbus_watch_get_data(watch); if ((data = dbus_watch_get_data(watch)) != NULL)
spa_loop_utils_update_io(impl->utils, data->source, dbus_to_io(watch));
spa_loop_utils_update_io(impl->utils, source, dbus_to_io(watch));
} }
struct timeout_data {
struct spa_source *source;
struct connection *conn;
};
static void static void
handle_timer_event(void *userdata, uint64_t expirations) handle_timer_event(void *userdata, uint64_t expirations)
{ {
DBusTimeout *timeout = userdata; DBusTimeout *timeout = userdata;
uint64_t t; uint64_t t;
struct timespec ts; 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 connection *conn = data->conn;
struct impl *impl = conn->impl; struct impl *impl = conn->impl;
@ -199,7 +210,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata)
struct connection *conn = userdata; struct connection *conn = userdata;
struct impl *impl = conn->impl; struct impl *impl = conn->impl;
struct timespec ts; struct timespec ts;
struct timeout_data *data; struct source_data *data;
uint64_t t; uint64_t t;
if (!dbus_timeout_get_enabled(timeout)) 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); 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->conn = conn;
data->source = spa_loop_utils_add_timer(impl->utils, handle_timer_event, timeout); 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; t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
ts.tv_sec = t / SPA_NSEC_PER_SEC; 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 connection *conn = userdata;
struct impl *impl = conn->impl; struct impl *impl = conn->impl;
struct timeout_data *data;
spa_log_debug(impl->log, "remove timeout %p conn:%p impl:%p", timeout, conn, impl); spa_log_debug(impl->log, "remove timeout %p conn:%p impl:%p", timeout, conn, impl);
dbus_timeout_set_data(timeout, NULL, NULL);
if ((data = dbus_timeout_get_data(timeout))) {
spa_loop_utils_destroy_source(impl->utils, data->source);
free(data);
}
} }
static void toggle_timeout(DBusTimeout *timeout, void *userdata) static void toggle_timeout(DBusTimeout *timeout, void *userdata)
{ {
struct connection *conn = userdata; struct connection *conn = userdata;
struct impl *impl = conn->impl; struct impl *impl = conn->impl;
struct timeout_data *data; struct source_data *data;
struct timespec ts, *tsp; struct timespec ts, *tsp;
data = dbus_timeout_get_data(timeout); data = dbus_timeout_get_data(timeout);
@ -271,6 +278,24 @@ impl_connection_get(struct spa_dbus_connection *conn)
return this->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 static void
impl_connection_destroy(struct spa_dbus_connection *conn) 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; struct impl *impl = this->impl;
spa_log_debug(impl->log, "destroy conn %p", this); spa_log_debug(impl->log, "destroy conn %p", this);
connection_free(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);
} }
static const struct spa_dbus_connection impl_connection = { static const struct spa_dbus_connection impl_connection = {
@ -318,6 +335,8 @@ impl_get_connection(void *object,
if (conn->dispatch_event == NULL) if (conn->dispatch_event == NULL)
goto no_event; goto no_event;
spa_list_init(&conn->source_list);
dbus_connection_set_exit_on_disconnect(conn->conn, false); dbus_connection_set_exit_on_disconnect(conn->conn, false);
dbus_connection_set_dispatch_status_function(conn->conn, dispatch_status, conn, NULL); 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, 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) 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_return_val_if_fail(handle != NULL, -EINVAL);
spa_list_consume(conn, &impl->connection_list, link)
connection_free(conn);
return 0; return 0;
} }