component-loader: make wp_core_load_component() API asynchronous

This change completely refactors the way components are loaded in wireplumber:
- The module_init() function must return a GObject now. This object is either
a WpPlugin or a WpSiFactory in the current modules.
- When the component loader initializes a module, it automatically registers
the WpPlugin or WpSiFactory with their respective methods. There is no need
to register the WpPlugin or WpSiFactory in the module now.
- The wp_core_load_component() API has been refactored to be asynchronows. This
allows the component loader to automatically activate WpPlugin objects, and
therefore allows the application to directly get the WpPlugin without having
to find it. This simplifies a lot of things.
- The 'ifexists' and 'nofail' component flags now work even if the respective
WpPlugin could not be activated.
- The code that loads components in main.c has also been simplified a lot,
and the option to load dangling components has also been removed.
This commit is contained in:
Julian Bouzas
2023-02-09 13:11:14 -05:00
parent ad940b6efc
commit c61d1e4245
28 changed files with 519 additions and 703 deletions

View File

@@ -32,7 +32,7 @@
*/
#define WP_MODULE_INIT_SYMBOL "wireplumber__module_init"
typedef gboolean (*WpModuleInitFunc) (WpCore *, GVariant *, GError **);
typedef GObject *(*WpModuleInitFunc) (WpCore *, GVariant *, GError **);
G_DEFINE_ABSTRACT_TYPE (WpComponentLoader, wp_component_loader, WP_TYPE_PLUGIN)
@@ -46,7 +46,7 @@ wp_component_loader_class_init (WpComponentLoaderClass * klass)
{
}
static gboolean
static GObject *
load_module (WpCore * core, const gchar * module_name,
GVariant * args, GError ** error)
{
@@ -64,7 +64,7 @@ load_module (WpCore * core, const gchar * module_name,
if (!gmodule) {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"Failed to open module %s: %s", module_path, g_module_error ());
return FALSE;
return NULL;
}
if (!g_module_symbol (gmodule, WP_MODULE_INIT_SYMBOL, &module_init)) {
@@ -72,7 +72,7 @@ load_module (WpCore * core, const gchar * module_name,
"Failed to locate symbol " WP_MODULE_INIT_SYMBOL " in %s",
module_path);
g_module_close (gmodule);
return FALSE;
return NULL;
}
return ((WpModuleInitFunc) module_init) (core, args, error);
@@ -98,13 +98,28 @@ wp_component_loader_find (WpCore * core, const gchar * type)
return c ? WP_COMPONENT_LOADER (c) : NULL;
}
static gboolean
static void
wp_component_loader_load (WpComponentLoader * self, const gchar * component,
const gchar * type, GVariant * args, GError ** error)
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data)
{
g_return_val_if_fail (WP_IS_COMPONENT_LOADER (self), FALSE);
return WP_COMPONENT_LOADER_GET_CLASS (self)->load (self, component, type,
args, error);
g_return_if_fail (WP_IS_COMPONENT_LOADER (self));
WP_COMPONENT_LOADER_GET_CLASS (self)->load (self, component, type,
args, callback, data);
}
static void
on_object_loaded (WpObject *object, GAsyncResult *res, gpointer data)
{
g_autoptr (GTask) task = G_TASK (data);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (object, res, &error)) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_task_return_pointer (task, g_object_ref (object), g_object_unref);
}
/*!
@@ -121,25 +136,80 @@ wp_component_loader_load (WpComponentLoader * self, const gchar * component,
* \param type the type of the component
* \param args (transfer floating)(nullable): additional arguments for the component,
* usually a dict or a string
* \param error (out) (optional): return location for errors, or NULL to ignore
* \returns TRUE if loaded, FALSE if there was an error
* \param callback (scope async): the callback to call when the operation is done
* \param data (closure): data to pass to \a callback
*/
gboolean
void
wp_core_load_component (WpCore * self, const gchar * component,
const gchar * type, GVariant * args, GError ** error)
const gchar * type, GVariant * args, GAsyncReadyCallback callback,
gpointer data)
{
g_autoptr (GVariant) args_ref = args ? g_variant_ref_sink (args) : NULL;
g_autoptr (GTask) task = NULL;
g_autoptr (WpComponentLoader) c = NULL;
if (!g_strcmp0 (type, "module"))
return load_module (self, component, args_ref, error);
else {
g_autoptr (WpComponentLoader) c = wp_component_loader_find (self, type);
if (c)
return wp_component_loader_load (c, component, type, args, error);
else {
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"No component loader was found for components of type '%s'", type);
return FALSE;
/* Special case for "module" component type */
if (g_str_equal (type, "module")) {
task = g_task_new (self, NULL, callback, data);
g_autoptr (GError) error = NULL;
g_autoptr (GObject) o = NULL;
/* load Module */
o = load_module (self, component, args_ref, &error);
if (!o) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
if (WP_IS_OBJECT (o)) {
/* WpObject needs to be activated */
if (WP_IS_PLUGIN (o))
wp_plugin_register (WP_PLUGIN (g_object_ref (o)));
wp_object_activate (WP_OBJECT (o), WP_OBJECT_FEATURES_ALL, NULL,
(GAsyncReadyCallback) on_object_loaded, g_object_ref (task));
return;
} else if (WP_IS_SI_FACTORY (o)) {
/* WpSiFactory doesn't need to be activated */
wp_si_factory_register (self, WP_SI_FACTORY (g_object_ref (o)));
g_task_return_pointer (task, g_object_ref (o), g_object_unref);
return;
}
g_task_return_new_error (task, WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"Invalid module object for component %s", component);
return;
}
/* Otherwise find a component loader for that type and load the component */
c = wp_component_loader_find (self, type);
if (!c) {
task = g_task_new (self, NULL, callback, data);
g_task_return_new_error (task, WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"No component loader was found for components of type '%s'", type);
return;
}
wp_component_loader_load (c, component, type, args, callback, data);
}
/*!
* \brief Finishes the operation started by wp_core_load_component().
* This is meant to be called in the callback that was passed to that method.
*
*
* \ingroup wpcomponentloader
* \param self the component loader object
* \param res the async result
* \param error (out) (optional): the operation's error, if it occurred
* \returns (transfer full): The loaded component object, or NULL if an
* error happened.
*/
GObject *
wp_core_load_component_finish (WpCore * self, GAsyncResult * res,
GError ** error)
{
gpointer o = g_task_propagate_pointer (G_TASK (res), error);
return o ? g_object_ref (G_OBJECT (o)) : NULL;
}