pipewire: store SPA handles in a global list by age

Operating on the assumption that every SPA handle
can reference any other older SPA handle, the only
safe destruction order is from youngest to oldest.

To achieve this, store all handles across all plugins
sorted by age (youngest first), and use that as the
order of destruction in `pw_deinit()`.

This line of thinking does not account for what happens
when a handle that is referenced by others is unloaded,
but it does not make that case worse either.

See #2881
This commit is contained in:
Barnabás Pőcze 2022-12-10 00:40:21 +01:00
parent d4eff5b058
commit 3bdd2e01c5

View file

@ -64,7 +64,6 @@ struct plugin {
char *filename; char *filename;
void *hnd; void *hnd;
spa_handle_factory_enum_func_t enum_func; spa_handle_factory_enum_func_t enum_func;
struct spa_list handles;
int ref; int ref;
}; };
@ -78,6 +77,7 @@ struct handle {
struct registry { struct registry {
struct spa_list plugins; struct spa_list plugins;
struct spa_list handles; /* all handles across all plugins by age (youngest first) */
}; };
struct support { struct support {
@ -149,7 +149,6 @@ open_plugin(struct registry *registry,
plugin->filename = strdup(filename); plugin->filename = strdup(filename);
plugin->hnd = hnd; plugin->hnd = hnd;
plugin->enum_func = enum_func; plugin->enum_func = enum_func;
spa_list_init(&plugin->handles);
spa_list_append(&registry->plugins, &plugin->link); spa_list_append(&registry->plugins, &plugin->link);
@ -290,7 +289,7 @@ static struct spa_handle *load_spa_handle(const char *lib,
handle->ref = 1; handle->ref = 1;
handle->plugin = plugin; handle->plugin = plugin;
handle->factory_name = strdup(factory_name); handle->factory_name = strdup(factory_name);
spa_list_append(&plugin->handles, &handle->link); spa_list_prepend(&sup->registry.handles, &handle->link);
return &handle->handle; return &handle->handle;
@ -321,15 +320,13 @@ struct spa_handle *pw_load_spa_handle(const char *lib,
static struct handle *find_handle(struct spa_handle *handle) static struct handle *find_handle(struct spa_handle *handle)
{ {
struct registry *registry = &global_support.registry; struct registry *registry = &global_support.registry;
struct plugin *p;
struct handle *h; struct handle *h;
spa_list_for_each(p, &registry->plugins, link) { spa_list_for_each(h, &registry->handles, link) {
spa_list_for_each(h, &p->handles, link) { if (&h->handle == handle)
if (&h->handle == handle) return h;
return h;
}
} }
return NULL; return NULL;
} }
@ -611,6 +608,7 @@ void pw_init(int *argc, char **argv[])
support->support_lib = str; support->support_lib = str;
spa_list_init(&support->registry.plugins); spa_list_init(&support->registry.plugins);
spa_list_init(&support->registry.handles);
if (pw_log_is_default()) { if (pw_log_is_default()) {
char *patterns = NULL; char *patterns = NULL;
@ -684,7 +682,7 @@ void pw_deinit(void)
{ {
struct support *support = &global_support; struct support *support = &global_support;
struct registry *registry = &support->registry; struct registry *registry = &support->registry;
struct plugin *p; struct handle *h;
pthread_mutex_lock(&init_lock); pthread_mutex_lock(&init_lock);
if (support->init_count == 0) if (support->init_count == 0)
@ -694,13 +692,10 @@ void pw_deinit(void)
pthread_mutex_lock(&support_lock); pthread_mutex_lock(&support_lock);
pw_log_set(NULL); pw_log_set(NULL);
spa_list_consume(p, &registry->plugins, link) {
struct handle *h; spa_list_consume(h, &registry->handles, link)
p->ref++; unref_handle(h);
spa_list_consume(h, &p->handles, link)
unref_handle(h);
unref_plugin(p);
}
pw_free_strv(support->categories); pw_free_strv(support->categories);
free(support->i18n_domain); free(support->i18n_domain);
spa_zero(global_support); spa_zero(global_support);