gplugin/gplugin

Parents c8e4dba3aa65
Children cde752f30c6b
Add an unloadable property to GPluginPluginInfo and add a shutdown parameter to gplugin_unload.

Add an unloadable property to GPluginPluginInfo and add a shutdown parameter to `gplugin_unload`.

This allows plugins that can only be shutdown once to handle things properly. Examples of this are basically anything that pulls in another library that doesn't allow you to `_init` and `_uninit` more than once.

Testing Done:
Ran the unit tests and gplugin-query.

Reviewed at https://reviews.imfreedom.org/r/943/
  • +3 -1
    gplugin-query/gplugin-query.c
  • +43 -3
    gplugin/gplugin-loader-tests.c
  • +3 -1
    gplugin/gplugin-loader.c
  • +7 -3
    gplugin/gplugin-loader.h
  • +12 -3
    gplugin/gplugin-manager.c
  • +0 -1
    gplugin/gplugin-manager.h
  • +2 -2
    gplugin/gplugin-native-loader.c
  • +7 -9
    gplugin/gplugin-native-plugin.h
  • +65 -1
    gplugin/gplugin-plugin-info.c
  • +1 -0
    gplugin/gplugin-plugin-info.h
  • +1 -0
    gplugin/tests/bad-plugins/query-error.c
  • +1 -0
    gplugin/tests/bind-global/bind-global.c
  • +1 -0
    gplugin/tests/dynamic-type/dynamic-type-provider.c
  • +4 -1
    gplugin/tests/dynamic-type/dynamic-type-user.c
  • +1 -0
    gplugin/tests/id-collision/id-collision1.c
  • +1 -0
    gplugin/tests/id-collision/id-collision2.c
  • +1 -0
    gplugin/tests/load-on-query-fail/load-on-query-fail.c
  • +1 -0
    gplugin/tests/load-on-query-pass/load-on-query-pass.c
  • +1 -0
    gplugin/tests/newest-version/multiple-semantic-1.0.0.c
  • +1 -0
    gplugin/tests/newest-version/multiple-semantic-1.1.0.c
  • +1 -0
    gplugin/tests/newest-version/no-version-and-semantic-no-version.c
  • +1 -0
    gplugin/tests/newest-version/no-version-and-semantic-semantic.c
  • +1 -0
    gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c
  • +1 -0
    gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c
  • +1 -0
    gplugin/tests/newest-version/solo-no-version.c
  • +1 -0
    gplugin/tests/newest-version/solo-non-semantic.c
  • +4 -1
    gplugin/tests/plugins/basic-plugin.c
  • +1 -0
    gplugin/tests/plugins/broken-dependent-plugin.c
  • +1 -0
    gplugin/tests/plugins/dependent-plugin.c
  • +1 -0
    gplugin/tests/plugins/load-exception.c
  • +1 -0
    gplugin/tests/plugins/load-failed.c
  • +4 -0
    gplugin/tests/plugins/meson.build
  • +4 -1
    gplugin/tests/plugins/unload-failed.c
  • +53 -0
    gplugin/tests/plugins/unload-shutdown.c
  • +3 -3
    gplugin/tests/test-find-plugins.c
  • +1 -0
    gplugin/tests/test-loader-registration.c
  • +2 -1
    gplugin/tests/test-loader.c
  • +1 -0
    gplugin/tests/unresolved-symbol/unresolved-symbol.c
  • +4 -1
    gplugin/tests/versioned-dependencies/bar.c
  • +4 -1
    gplugin/tests/versioned-dependencies/baz.c
  • +4 -1
    gplugin/tests/versioned-dependencies/exact1.c
  • +4 -1
    gplugin/tests/versioned-dependencies/exact2.c
  • +4 -1
    gplugin/tests/versioned-dependencies/fez.c
  • +1 -0
    gplugin/tests/versioned-dependencies/greater-equal.c
  • +1 -0
    gplugin/tests/versioned-dependencies/greater.c
  • +1 -0
    gplugin/tests/versioned-dependencies/less-equal.c
  • +4 -1
    gplugin/tests/versioned-dependencies/less.c
  • +1 -0
    gplugin/tests/versioned-dependencies/no-version.c
  • +1 -0
    gplugin/tests/versioned-dependencies/super-dependent.c
  • +22 -7
    lua/gplugin-lua-core.c
  • +48 -42
    lua/gplugin-lua-loader.c
  • +5 -1
    lua/tests/plugins/basic.lua
  • +1 -1
    lua/tests/plugins/dependent.lua
  • +1 -1
    lua/tests/plugins/load-exception.lua
  • +1 -1
    lua/tests/plugins/load-failed.lua
  • +1 -1
    lua/tests/plugins/unload-failed.lua
  • +37 -0
    lua/tests/plugins/unload-shutdown.lua
  • +1 -1
    meson.build
  • +22 -7
    perl5/gplugin-perl5-core.c
  • +7 -12
    perl5/gplugin-perl5-loader.c
  • +8 -1
    perl5/tests/plugins/basic.pl
  • +3 -1
    perl5/tests/plugins/unload-failed.pl
  • +40 -0
    perl5/tests/plugins/unload-shutdown.pl
  • +22 -7
    python3/gplugin-python3-core.c
  • +5 -2
    python3/gplugin-python3-loader.c
  • +4 -1
    python3/tests/plugins/basic.py
  • +1 -1
    python3/tests/plugins/dependent.py
  • +1 -1
    python3/tests/plugins/load-exception.py
  • +1 -1
    python3/tests/plugins/load-failed.py
  • +1 -1
    python3/tests/plugins/unload-failed.py
  • +37 -0
    python3/tests/plugins/unload-shutdown.py
  • +22 -7
    tcc/gplugin-tcc-core.c
  • +1 -0
    tcc/tests/plugins/basic-plugin.c
  • +1 -0
    tcc/tests/plugins/dependent.c
  • +1 -0
    tcc/tests/plugins/load-exception.c
  • +1 -0
    tcc/tests/plugins/load-failed.c
  • +4 -1
    tcc/tests/plugins/unload-failed.c
  • +5 -1
    vala/tests/genie-plugins/basic.gs
  • +1 -1
    vala/tests/genie-plugins/dependent.gs
  • +1 -1
    vala/tests/genie-plugins/load-exception.gs
  • +1 -1
    vala/tests/genie-plugins/load-failed.gs
  • +3 -0
    vala/tests/genie-plugins/meson.build
  • +1 -1
    vala/tests/genie-plugins/unload-failed.gs
  • +47 -0
    vala/tests/genie-plugins/unload-shutdown.gs
  • +6 -1
    vala/tests/plugins/basic.vala
  • +1 -1
    vala/tests/plugins/dependent.vala
  • +1 -1
    vala/tests/plugins/load-exception.vala
  • +1 -1
    vala/tests/plugins/load-failed.vala
  • +3 -0
    vala/tests/plugins/meson.build
  • +1 -1
    vala/tests/plugins/unload-failed.vala
  • +49 -0
    vala/tests/plugins/unload-shutdown.vala
  • --- a/gplugin-query/gplugin-query.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin-query/gplugin-query.c Mon Sep 27 08:41:55 2021 -0500
    @@ -119,7 +119,7 @@
    for(l = plugins; l; l = l->next) {
    GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data);
    GPluginPluginInfo *info = gplugin_plugin_get_info(plugin);
    - gboolean internal, loq, bind_global;
    + gboolean internal, loq, bind_global, unloadable;
    guint32 abi_version;
    gchar *name, *version;
    gchar *license_id, *license_text, *license_url;
    @@ -155,6 +155,7 @@
    "website", &website,
    "dependencies", &dependencies,
    "bind-global", &bind_global,
    + "unloadable", &unloadable,
    NULL);
    /* clang-format on */
    @@ -196,6 +197,7 @@
    printf(MAIN_FORMAT, "internal", (internal) ? "yes" : "no");
    printf(MAIN_FORMAT, "load on query", (loq) ? "yes" : "no");
    printf(MAIN_FORMAT, "bind globally", (bind_global) ? "yes" : "no");
    + printf(MAIN_FORMAT, "unloadable", (unloadable) ? "yes" : "no");
    printf(MAIN_FORMAT, "loader", G_OBJECT_TYPE_NAME(loader));
    g_object_unref(G_OBJECT(loader));
    --- a/gplugin/gplugin-loader-tests.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-loader-tests.c Mon Sep 27 08:41:55 2021 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -144,10 +144,9 @@
    g_assert_nonnull(plugin);
    ret = gplugin_manager_load_plugin(manager, plugin, &error);
    - g_assert_false(ret);
    -
    g_assert_error(error, GPLUGIN_DOMAIN, 0);
    g_clear_error(&error);
    + g_assert_false(ret);
    g_assert_cmpint(
    gplugin_plugin_get_state(plugin),
    @@ -163,6 +162,46 @@
    }
    static void
    +gplugin_test_loader_unload_shutdown(gconstpointer d)
    +{
    + GPluginLoader *loader = NULL;
    + GPluginManager *manager = NULL;
    + GPluginPlugin *plugin = NULL;
    + GError *error = NULL;
    + gchar *id = NULL;
    + gboolean ret = FALSE;
    +
    + manager = gplugin_manager_get_default();
    +
    + id = g_strdup_printf("gplugin/%s-unload-shutdown", (const gchar *)d);
    + plugin = gplugin_manager_find_plugin(manager, id);
    + g_free(id);
    + g_assert_nonnull(plugin);
    +
    + ret = gplugin_manager_load_plugin(manager, plugin, &error);
    + g_assert_no_error(error);
    + g_assert_true(ret);
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_LOADED);
    +
    + loader = gplugin_plugin_get_loader(plugin);
    + ret = gplugin_loader_unload_plugin(loader, plugin, TRUE, &error);
    + g_clear_object(&loader);
    +
    + g_assert_no_error(error);
    + g_assert_true(ret);
    +
    + g_assert_cmpint(
    + gplugin_plugin_get_state(plugin),
    + ==,
    + GPLUGIN_PLUGIN_STATE_QUERIED);
    +
    + g_object_unref(G_OBJECT(plugin));
    +}
    +
    +static void
    gplugin_test_loader_unload_failed(gconstpointer d)
    {
    GPluginManager *manager = gplugin_manager_get_default();
    @@ -237,6 +276,7 @@
    {"/loaders/%s/load-failed", gplugin_test_loader_load_failed},
    {"/loaders/%s/load-exception", gplugin_test_loader_load_exception},
    {"/loaders/%s/unload-failed", gplugin_test_loader_unload_failed},
    + {"/loaders/%s/unload-shutdown", gplugin_test_loader_unload_shutdown},
    {"/loaders/%s/dependencies", gplugin_test_loader_dependencies},
    {NULL, NULL},
    };
    --- a/gplugin/gplugin-loader.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-loader.c Mon Sep 27 08:41:55 2021 -0500
    @@ -328,6 +328,7 @@
    * gplugin_loader_unload_plugin:
    * @loader: The #GPluginLoader instance performing the unload.
    * @plugin: The #GPluginPlugin instance to unload.
    + * @shutdown: Whether or not GPlugin is shutting down.
    * @error: (nullable): The return location for a #GError, or %NULL.
    *
    * This function is called by the plugin manager to ask @loader to unload
    @@ -339,6 +340,7 @@
    gplugin_loader_unload_plugin(
    GPluginLoader *loader,
    GPluginPlugin *plugin,
    + gboolean shutdown,
    GError **error)
    {
    GPluginLoaderClass *klass = NULL;
    @@ -356,7 +358,7 @@
    klass = GPLUGIN_LOADER_GET_CLASS(loader);
    if(klass != NULL && klass->unload != NULL) {
    - ret = klass->unload(loader, plugin, &real_error);
    + ret = klass->unload(loader, plugin, shutdown, &real_error);
    }
    if(!ret) {
    --- a/gplugin/gplugin-loader.h Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-loader.h Mon Sep 27 08:41:55 2021 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -50,8 +50,11 @@
    gboolean (
    *load)(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    - gboolean (
    - *unload)(GPluginLoader *loader, GPluginPlugin *plugin, GError **error);
    + gboolean (*unload)(
    + GPluginLoader *loader,
    + GPluginPlugin *plugin,
    + gboolean shutdown,
    + GError **error);
    /*< private >*/
    gpointer reserved[4];
    @@ -73,6 +76,7 @@
    gboolean gplugin_loader_unload_plugin(
    GPluginLoader *loader,
    GPluginPlugin *plugin,
    + gboolean shutdown,
    GError **error);
    G_END_DECLS
    --- a/gplugin/gplugin-manager.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-manager.c Mon Sep 27 08:41:55 2021 -0500
    @@ -149,7 +149,7 @@
    }
    loader = gplugin_plugin_get_loader(plugin);
    - if(!gplugin_loader_unload_plugin(loader, plugin, &error)) {
    + if(!gplugin_loader_unload_plugin(loader, plugin, TRUE, &error)) {
    g_warning(
    "failed to unload plugin with id %s: %s",
    id,
    @@ -508,7 +508,16 @@
    g_regex_unref(dependency_regex);
    - g_clear_object(&default_manager);
    + /* g_clear_pointer (and therefore g_clear_object), clears the pointer
    + * before calling the destroy function. So we have to handle this ourself
    + * and clear the pointer after destruction since plugins are unloaded
    + * during destruction and may need to unregister a loader during their
    + * unload.
    + */
    + if(default_manager != NULL) {
    + g_object_unref(G_OBJECT(default_manager));
    + default_manager = NULL;
    + }
    }
    /******************************************************************************
    @@ -1687,7 +1696,7 @@
    return ret;
    }
    - ret = gplugin_loader_unload_plugin(loader, plugin, &real_error);
    + ret = gplugin_loader_unload_plugin(loader, plugin, FALSE, &real_error);
    if(ret) {
    g_clear_error(&real_error);
    g_signal_emit(manager, signals[SIG_UNLOADED], 0, plugin);
    --- a/gplugin/gplugin-manager.h Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-manager.h Mon Sep 27 08:41:55 2021 -0500
    @@ -47,7 +47,6 @@
    GObjectClass parent;
    /*< public >*/
    -
    gboolean (*loading_plugin)(
    GPluginManager *manager,
    GPluginPlugin *plugin,
    --- a/gplugin/gplugin-native-loader.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-native-loader.c Mon Sep 27 08:41:55 2021 -0500
    @@ -304,6 +304,7 @@
    gplugin_native_loader_unload(
    G_GNUC_UNUSED GPluginLoader *loader,
    GPluginPlugin *plugin,
    + gboolean shutdown,
    GError **error)
    {
    GPluginNativePluginUnloadFunc func;
    @@ -326,7 +327,7 @@
    }
    /* now call the function */
    - if(!func(plugin, error)) {
    + if(!func(plugin, shutdown, error)) {
    if(error && *error == NULL)
    g_set_error_literal(error, GPLUGIN_DOMAIN, 0, _("unknown failure"));
    @@ -380,4 +381,3 @@
    NULL);
    /* clang-format on */
    }
    -
    --- a/gplugin/gplugin-native-plugin.h Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-native-plugin.h Mon Sep 27 08:41:55 2021 -0500
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    @@ -44,7 +44,7 @@
    /* clang-format off */
    typedef GPluginPluginInfo *(*GPluginNativePluginQueryFunc)(GError **error);
    typedef gboolean (*GPluginNativePluginLoadFunc)(GPluginPlugin *plugin, GError **error);
    -typedef gboolean (*GPluginNativePluginUnloadFunc)(GPluginPlugin *plugin, GError **error);
    +typedef gboolean (*GPluginNativePluginUnloadFunc)(GPluginPlugin *plugin, gboolean shutdown, GError **error);
    /* clang-format on */
    GModule *gplugin_native_plugin_get_module(GPluginNativePlugin *plugin);
    @@ -66,14 +66,12 @@
    return name##_load(plugin, error); \
    } \
    \
    - G_MODULE_EXPORT gboolean gplugin_unload( \
    - GPluginPlugin *plugin, \
    - GError **error); \
    - G_MODULE_EXPORT gboolean gplugin_unload( \
    - GPluginPlugin *plugin, \
    - GError **error) \
    + G_MODULE_EXPORT gboolean \
    + gplugin_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error); \
    + G_MODULE_EXPORT gboolean \
    + gplugin_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) \
    { \
    - return name##_unload(plugin, error); \
    + return name##_unload(plugin, shutdown, error); \
    }
    G_END_DECLS
    --- a/gplugin/gplugin-plugin-info.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-plugin-info.c Mon Sep 27 08:41:55 2021 -0500
    @@ -79,6 +79,7 @@
    gboolean load_on_query;
    gboolean bind_global;
    + gboolean unloadable;
    } GPluginPluginInfoPrivate;
    /******************************************************************************
    @@ -105,6 +106,7 @@
    PROP_AUTHORS,
    PROP_WEBSITE,
    PROP_DEPENDENCIES,
    + PROP_UNLOADABLE,
    N_PROPERTIES,
    };
    static GParamSpec *properties[N_PROPERTIES] = {
    @@ -333,6 +335,17 @@
    priv->dependencies = g_strdupv((gchar **)dependencies);
    }
    +static void
    +gplugin_plugin_info_set_unloadable(GPluginPluginInfo *info, gboolean unloadable)
    +{
    + GPluginPluginInfoPrivate *priv =
    + gplugin_plugin_info_get_instance_private(info);
    +
    + priv->unloadable = unloadable;
    +
    + g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_UNLOADABLE]);
    +}
    +
    /******************************************************************************
    * Object Stuff
    *****************************************************************************/
    @@ -415,6 +428,11 @@
    value,
    gplugin_plugin_info_get_dependencies(info));
    break;
    + case PROP_UNLOADABLE:
    + g_value_set_boolean(
    + value,
    + gplugin_plugin_info_get_unloadable(info));
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    break;
    @@ -500,6 +518,11 @@
    info,
    g_value_get_boxed(value));
    break;
    + case PROP_UNLOADABLE:
    + gplugin_plugin_info_set_unloadable(
    + info,
    + g_value_get_boolean(value));
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    break;
    @@ -841,6 +864,21 @@
    G_TYPE_STRV,
    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
    + /**
    + * GPluginPluginInfo:unloadable:
    + *
    + * Sets whether or not a plugin is unloadable. See
    + * gplugin_plugin_info_get_unloadable() for more information.
    + *
    + * Since: 0.35.0
    + */
    + properties[PROP_UNLOADABLE] = g_param_spec_boolean(
    + "unloadable",
    + "unloadable",
    + "Whether or not plugin is unloadable",
    + TRUE,
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
    +
    g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    }
    @@ -1267,7 +1305,7 @@
    * gplugin_plugin_info_get_bind_global:
    * @info: The #GPluginPluginInfo instance.
    *
    - * This propoerty and therefore function is only used by the native plugin
    + * This property and therefore function is only used by the native plugin
    * loader.
    *
    * Returns: %TRUE if the plugin has requested to be loaded with its symbols
    @@ -1284,3 +1322,29 @@
    return priv->bind_global;
    }
    +
    +/**
    + * gplugin_plugin_info_get_unloadable:
    + * @info: The #GPluginPluginInfo instance.
    + *
    + * Gets whether or not the plugin is unloadable. Certain libraries can not be
    + * shutdown cleanly and then re-enabled during the life time of a program. A
    + * plugin using one of these libraries should set the
    + * GPluginPluginInfo::unloadable property to %FALSE to tell #GPluginManager to
    + * not even attempt to unload it.
    + *
    + * Returns: %TRUE if the plugin is unloadable, otherwise %FALSE.
    + *
    + * Since: 0.35.0
    + */
    +gboolean
    +gplugin_plugin_info_get_unloadable(GPluginPluginInfo *info)
    +{
    + GPluginPluginInfoPrivate *priv = NULL;
    +
    + g_return_val_if_fail(GPLUGIN_IS_PLUGIN_INFO(info), FALSE);
    +
    + priv = gplugin_plugin_info_get_instance_private(info);
    +
    + return priv->unloadable;
    +}
    --- a/gplugin/gplugin-plugin-info.h Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/gplugin-plugin-info.h Mon Sep 27 08:41:55 2021 -0500
    @@ -75,6 +75,7 @@
    const gchar *const *gplugin_plugin_info_get_dependencies(
    GPluginPluginInfo *info);
    gboolean gplugin_plugin_info_get_bind_global(GPluginPluginInfo *info);
    +gboolean gplugin_plugin_info_get_unloadable(GPluginPluginInfo *info);
    G_END_DECLS
    --- a/gplugin/tests/bad-plugins/query-error.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/bad-plugins/query-error.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,6 +37,7 @@
    static gboolean
    query_error_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/bind-global/bind-global.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/bind-global/bind-global.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    bind_global_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/dynamic-type/dynamic-type-provider.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/dynamic-type/dynamic-type-provider.c Mon Sep 27 08:41:55 2021 -0500
    @@ -60,6 +60,7 @@
    static gboolean
    dynamic_provider_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/dynamic-type/dynamic-type-user.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/dynamic-type/dynamic-type-user.c Mon Sep 27 08:41:55 2021 -0500
    @@ -55,7 +55,10 @@
    }
    static gboolean
    -dynamic_user_unload(G_GNUC_UNUSED GPluginPlugin *plugin, GError **error)
    +dynamic_user_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + GError **error)
    {
    gpointer weak_test_object = test_object;
    g_object_add_weak_pointer(G_OBJECT(test_object), &weak_test_object);
    --- a/gplugin/tests/id-collision/id-collision1.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/id-collision/id-collision1.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    id_collision1_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/id-collision/id-collision2.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/id-collision/id-collision2.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    id_collision2_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/load-on-query-fail/load-on-query-fail.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/load-on-query-fail/load-on-query-fail.c Mon Sep 27 08:41:55 2021 -0500
    @@ -43,6 +43,7 @@
    static gboolean
    loq_fail_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/load-on-query-pass/load-on-query-pass.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/load-on-query-pass/load-on-query-pass.c Mon Sep 27 08:41:55 2021 -0500
    @@ -39,6 +39,7 @@
    static gboolean
    loq_pass_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/multiple-semantic-1.0.0.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/multiple-semantic-1.0.0.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    multiple_semantic_1_0_0_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/multiple-semantic-1.1.0.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/multiple-semantic-1.1.0.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    multiple_semantic_1_1_0_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/no-version-and-semantic-no-version.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/no-version-and-semantic-no-version.c Mon Sep 27 08:41:55 2021 -0500
    @@ -40,6 +40,7 @@
    static gboolean
    no_version_and_semantic_no_version_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/no-version-and-semantic-semantic.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/no-version-and-semantic-semantic.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    no_version_an_semantic_semantic_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    non_semantic_and_semantic_non_semantic_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    non_semantic_and_semantic_semantic_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/solo-no-version.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/solo-no-version.c Mon Sep 27 08:41:55 2021 -0500
    @@ -40,6 +40,7 @@
    static gboolean
    solo_no_version_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/newest-version/solo-non-semantic.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/newest-version/solo-non-semantic.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    solo_non_semantic_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/plugins/basic-plugin.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/basic-plugin.c Mon Sep 27 08:41:55 2021 -0500
    @@ -52,7 +52,10 @@
    }
    static gboolean
    -basic_unload(GPluginPlugin *plugin, GError **error)
    +basic_unload(
    + GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + GError **error)
    {
    if(!GPLUGIN_IS_PLUGIN(plugin)) {
    g_set_error_literal(error, GPLUGIN_DOMAIN, 0, "plugin was not set");
    --- a/gplugin/tests/plugins/broken-dependent-plugin.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/broken-dependent-plugin.c Mon Sep 27 08:41:55 2021 -0500
    @@ -43,6 +43,7 @@
    static gboolean
    broken_dependent_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/plugins/dependent-plugin.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/dependent-plugin.c Mon Sep 27 08:41:55 2021 -0500
    @@ -43,6 +43,7 @@
    static gboolean
    dependent_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/plugins/load-exception.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/load-exception.c Mon Sep 27 08:41:55 2021 -0500
    @@ -38,6 +38,7 @@
    static gboolean
    load_exception_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/plugins/load-failed.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/load-failed.c Mon Sep 27 08:41:55 2021 -0500
    @@ -38,6 +38,7 @@
    static gboolean
    load_failed_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/plugins/meson.build Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/meson.build Mon Sep 27 08:41:55 2021 -0500
    @@ -21,3 +21,7 @@
    shared_library('unload-failed', 'unload-failed.c',
    name_prefix : '',
    dependencies : [gplugin_dep, GLIB])
    +
    +shared_library('unload-shutdown', 'unload-shutdown.c',
    + name_prefix : '',
    + dependencies : [gplugin_dep, GLIB])
    --- a/gplugin/tests/plugins/unload-failed.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/plugins/unload-failed.c Mon Sep 27 08:41:55 2021 -0500
    @@ -36,7 +36,10 @@
    }
    static gboolean
    -unload_failed_unload(G_GNUC_UNUSED GPluginPlugin *plugin, GError **error)
    +unload_failed_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + GError **error)
    {
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gplugin/tests/plugins/unload-shutdown.c Mon Sep 27 08:41:55 2021 -0500
    @@ -0,0 +1,53 @@
    +/*
    + * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <https://www.gnu.org/licenses/>.
    + */
    +
    +#include <gplugin.h>
    +#include <gplugin-native.h>
    +
    +static GPluginPluginInfo *
    +unload_shutdown_query(G_GNUC_UNUSED GError **error)
    +{
    + return gplugin_plugin_info_new(
    + "gplugin/native-unload-shutdown",
    + GPLUGIN_NATIVE_PLUGIN_ABI_VERSION,
    + NULL);
    +}
    +
    +static gboolean
    +unload_shutdown_load(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED GError **error)
    +{
    + return TRUE;
    +}
    +
    +static gboolean
    +unload_shutdown_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + GError **error)
    +{
    + if(!shutdown) {
    + g_set_error(error, GPLUGIN_DOMAIN, 0, "shutdown was false");
    +
    + return FALSE;
    + }
    +
    + return TRUE;
    +}
    +
    +GPLUGIN_NATIVE_PLUGIN_DECLARE(unload_shutdown)
    --- a/gplugin/tests/test-find-plugins.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/test-find-plugins.c Mon Sep 27 08:41:55 2021 -0500
    @@ -79,11 +79,11 @@
    * load-failed: load failed
    * unload-failed: unload failed
    */
    - gint QUERIED = 6;
    - gint LOADED = 2;
    + gint QUERIED = 7;
    + gint LOADED = 3;
    gint LOAD_FAILED = 2;
    gint UNLOADED = 3; /* unloaded plugins go back to the queried state */
    - gint UNLOAD_FAILED = 1;
    + gint UNLOAD_FAILED = 2;
    gplugin_init(GPLUGIN_CORE_FLAGS_NONE);
    --- a/gplugin/tests/test-loader-registration.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/test-loader-registration.c Mon Sep 27 08:41:55 2021 -0500
    @@ -64,6 +64,7 @@
    test_gplugin_loader_unload(
    G_GNUC_UNUSED GPluginLoader *loader,
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return FALSE;
    --- a/gplugin/tests/test-loader.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/test-loader.c Mon Sep 27 08:41:55 2021 -0500
    @@ -216,6 +216,7 @@
    test_gplugin_loader_unload(
    GPluginLoader *l,
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    TestGPluginLoader *loader = TEST_GPLUGIN_LOADER(l);
    @@ -302,7 +303,7 @@
    g_assert_true(r);
    g_assert_true(tl->load);
    - r = gplugin_loader_unload_plugin(loader, plugin, &error);
    + r = gplugin_loader_unload_plugin(loader, plugin, FALSE, &error);
    g_assert_no_error(error);
    g_assert_true(r);
    g_assert_true(tl->unload);
    --- a/gplugin/tests/unresolved-symbol/unresolved-symbol.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/unresolved-symbol/unresolved-symbol.c Mon Sep 27 08:41:55 2021 -0500
    @@ -39,6 +39,7 @@
    static gboolean
    unresolved_symbol_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/versioned-dependencies/bar.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/bar.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,7 +37,10 @@
    }
    static gboolean
    -bar_unload(G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
    +bar_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    --- a/gplugin/tests/versioned-dependencies/baz.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/baz.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,7 +37,10 @@
    }
    static gboolean
    -baz_unload(G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
    +baz_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    --- a/gplugin/tests/versioned-dependencies/exact1.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/exact1.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,7 +37,10 @@
    }
    static gboolean
    -exact1_unload(G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
    +exact1_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    --- a/gplugin/tests/versioned-dependencies/exact2.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/exact2.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,7 +37,10 @@
    }
    static gboolean
    -exact2_unload(G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
    +exact2_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    --- a/gplugin/tests/versioned-dependencies/fez.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/fez.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,7 +37,10 @@
    }
    static gboolean
    -fez_unload(G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
    +fez_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    --- a/gplugin/tests/versioned-dependencies/greater-equal.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/greater-equal.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    greater_equal_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/versioned-dependencies/greater.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/greater.c Mon Sep 27 08:41:55 2021 -0500
    @@ -39,6 +39,7 @@
    static gboolean
    greater_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/versioned-dependencies/less-equal.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/less-equal.c Mon Sep 27 08:41:55 2021 -0500
    @@ -41,6 +41,7 @@
    static gboolean
    less_equal_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/versioned-dependencies/less.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/less.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,7 +37,10 @@
    }
    static gboolean
    -less_unload(G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error)
    +less_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    }
    --- a/gplugin/tests/versioned-dependencies/no-version.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/no-version.c Mon Sep 27 08:41:55 2021 -0500
    @@ -35,6 +35,7 @@
    static gboolean
    no_version_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/gplugin/tests/versioned-dependencies/super-dependent.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/gplugin/tests/versioned-dependencies/super-dependent.c Mon Sep 27 08:41:55 2021 -0500
    @@ -58,6 +58,7 @@
    static gboolean
    super_dependent_unload(
    G_GNUC_UNUSED GPluginPlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/lua/gplugin-lua-core.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/gplugin-lua-core.c Mon Sep 27 08:41:55 2021 -0500
    @@ -51,6 +51,7 @@
    "website", GPLUGIN_WEBSITE,
    "category", "loaders",
    "bind-global", TRUE,
    + "unloadable", FALSE,
    NULL);
    /* clang-format on */
    }
    @@ -76,15 +77,29 @@
    }
    static gboolean
    -gplugin_lua_unload(G_GNUC_UNUSED GPluginPlugin *plugin, GError **error)
    +gplugin_lua_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gboolean shutdown,
    + GError **error)
    {
    - g_set_error_literal(
    - error,
    - GPLUGIN_DOMAIN,
    - 0,
    - _("The Lua loader can not be unloaded"));
    + GPluginManager *manager = NULL;
    + gboolean ret = FALSE;
    - return FALSE;
    + if(!shutdown) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The Lua loader can not be unloaded"));
    +
    + return FALSE;
    + }
    +
    + manager = gplugin_manager_get_default();
    + ret = gplugin_manager_unregister_loader(manager, loader, error);
    + g_clear_object(&loader);
    +
    + return ret;
    }
    GPLUGIN_NATIVE_PLUGIN_DECLARE(gplugin_lua)
    --- a/lua/gplugin-lua-loader.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/gplugin-lua-loader.c Mon Sep 27 08:41:55 2021 -0500
    @@ -50,38 +50,6 @@
    g_set_error_literal(error, GPLUGIN_DOMAIN, 0, (msg) ? msg : "Unknown");
    }
    -static gboolean
    -_gplugin_lua_loader_load_unload_plugin(
    - G_GNUC_UNUSED GPluginLoader *loader,
    - GPluginPlugin *plugin,
    - const gchar *function,
    - GError **error)
    -{
    - gboolean ret = TRUE;
    - lua_State *L = gplugin_lua_plugin_get_state(GPLUGIN_LUA_PLUGIN(plugin));
    -
    - lua_getglobal(L, function);
    - lua_pushlightuserdata(L, plugin);
    - if(lua_pcall(L, 1, 1, 0) != 0) {
    - _gplugin_lua_error_to_gerror(L, error);
    -
    - return FALSE;
    - }
    -
    - if(!lua_isboolean(L, -1)) {
    - _gplugin_lua_error_to_gerror(L, error);
    -
    - return FALSE;
    - }
    -
    - if(!lua_toboolean(L, -1)) {
    - ret = FALSE;
    - _gplugin_lua_error_to_gerror(L, error);
    - }
    -
    - return ret;
    -}
    -
    /******************************************************************************
    * GPluginLoaderInterface API
    *****************************************************************************/
    @@ -162,24 +130,62 @@
    GPluginPlugin *plugin,
    GError **error)
    {
    - return _gplugin_lua_loader_load_unload_plugin(
    - loader,
    - plugin,
    - "gplugin_load",
    - error);
    + gboolean ret = TRUE;
    + lua_State *L = gplugin_lua_plugin_get_state(GPLUGIN_LUA_PLUGIN(plugin));
    +
    + lua_getglobal(L, "gplugin_load");
    + lua_pushlightuserdata(L, plugin);
    + if(lua_pcall(L, 1, 1, 0) != 0) {
    + _gplugin_lua_error_to_gerror(L, error);
    +
    + return FALSE;
    + }
    +
    + if(!lua_isboolean(L, -1)) {
    + _gplugin_lua_error_to_gerror(L, error);
    +
    + return FALSE;
    + }
    +
    + if(!lua_toboolean(L, -1)) {
    + ret = FALSE;
    + _gplugin_lua_error_to_gerror(L, error);
    + }
    +
    + return ret;
    }
    static gboolean
    gplugin_lua_loader_unload(
    GPluginLoader *loader,
    GPluginPlugin *plugin,
    + gboolean shutdown,
    GError **error)
    {
    - return _gplugin_lua_loader_load_unload_plugin(
    - loader,
    - plugin,
    - "gplugin_unload",
    - error);
    + gboolean ret = TRUE;
    + lua_State *L = gplugin_lua_plugin_get_state(GPLUGIN_LUA_PLUGIN(plugin));
    +
    + lua_getglobal(L, "gplugin_unload");
    + lua_pushlightuserdata(L, plugin);
    + lua_pushboolean(L, shutdown);
    + if(lua_pcall(L, 2, 1, 0) != 0) {
    + _gplugin_lua_error_to_gerror(L, error);
    +
    + return FALSE;
    + }
    +
    + if(!lua_isboolean(L, -1)) {
    + _gplugin_lua_error_to_gerror(L, error);
    +
    + return FALSE;
    + }
    +
    + if(!lua_toboolean(L, -1)) {
    + ret = FALSE;
    + _gplugin_lua_error_to_gerror(L, error);
    + }
    +
    + return ret;
    }
    /******************************************************************************
    --- a/lua/tests/plugins/basic.lua Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/tests/plugins/basic.lua Mon Sep 27 08:41:55 2021 -0500
    @@ -41,10 +41,14 @@
    return true
    end
    -function gplugin_unload(plugin)
    +function gplugin_unload(plugin, shutdown)
    if plugin == nil then
    error('plugin is nil')
    end
    + if shutdown then
    + error('shutdown is true')
    + end
    +
    return true
    end
    --- a/lua/tests/plugins/dependent.lua Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/tests/plugins/dependent.lua Mon Sep 27 08:41:55 2021 -0500
    @@ -29,6 +29,6 @@
    return false
    end
    -function gplugin_unload(plugin)
    +function gplugin_unload(plugin, shutdown)
    return false
    end
    --- a/lua/tests/plugins/load-exception.lua Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/tests/plugins/load-exception.lua Mon Sep 27 08:41:55 2021 -0500
    @@ -28,6 +28,6 @@
    error('explosion!')
    end
    -function gplugin_unload(plugin)
    +function gplugin_unload(plugin, shutdown)
    return false
    end
    --- a/lua/tests/plugins/load-failed.lua Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/tests/plugins/load-failed.lua Mon Sep 27 08:41:55 2021 -0500
    @@ -28,6 +28,6 @@
    return false
    end
    -function gplugin_unload(plugin)
    +function gplugin_unload(plugin, shutdown)
    return true
    end
    --- a/lua/tests/plugins/unload-failed.lua Sat Sep 25 14:18:43 2021 -0500
    +++ b/lua/tests/plugins/unload-failed.lua Mon Sep 27 08:41:55 2021 -0500
    @@ -28,6 +28,6 @@
    return true
    end
    -function gplugin_unload(plugin)
    +function gplugin_unload(plugin, shutdown)
    return false
    end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/lua/tests/plugins/unload-shutdown.lua Mon Sep 27 08:41:55 2021 -0500
    @@ -0,0 +1,37 @@
    +--[[
    + Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    +
    + This library is free software; you can redistribute it and/or
    + modify it under the terms of the GNU Lesser General Public
    + License as published by the Free Software Foundation; either
    + version 2 of the License, or (at your option) any later version.
    +
    + This library is distributed in the hope that it will be useful,
    + but WITHOUT ANY WARRANTY; without even the implied warranty of
    + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + Lesser General Public License for more details.
    +
    + You should have received a copy of the GNU Lesser General Public
    + License along with this library; if not, see <https://www.gnu.org/licenses/>.
    +--]]
    +
    +local lgi = require 'lgi'
    +local GPlugin = lgi.require('GPlugin', '1.0')
    +
    +function gplugin_query()
    + return GPlugin.PluginInfo {
    + id="gplugin/lua-unload-shutdown",
    + }
    +end
    +
    +function gplugin_load(plugin)
    + return true
    +end
    +
    +function gplugin_unload(plugin, shutdown)
    + if (not shutdown) then
    + error("shutdown was false")
    + end
    +
    + return true
    +end
    --- a/meson.build Sat Sep 25 14:18:43 2021 -0500
    +++ b/meson.build Mon Sep 27 08:41:55 2021 -0500
    @@ -3,7 +3,7 @@
    ###############################################################################
    project('gplugin', 'c',
    license : 'LGPL-2.0-or-later',
    - version : '0.34.1',
    + version : '0.35.0-dev',
    meson_version : '>=0.56.0',
    default_options : ['c_std=c99'])
    --- a/perl5/gplugin-perl5-core.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/perl5/gplugin-perl5-core.c Mon Sep 27 08:41:55 2021 -0500
    @@ -50,6 +50,7 @@
    "website", GPLUGIN_WEBSITE,
    "category", "loaders",
    "bind-global", TRUE,
    + "unloadable", FALSE,
    NULL);
    /* clang-format on */
    }
    @@ -75,15 +76,29 @@
    }
    static gboolean
    -gplugin_perl5_unload(G_GNUC_UNUSED GPluginPlugin *plugin, GError **error)
    +gplugin_perl5_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gboolean shutdown,
    + GError **error)
    {
    - g_set_error_literal(
    - error,
    - GPLUGIN_DOMAIN,
    - 0,
    - _("The Perl5 loader can not be unloaded"));
    + GPluginManager *manager = NULL;
    + gboolean ret = FALSE;
    - return FALSE;
    + if(!shutdown) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The Perl5 loader can not be unloaded"));
    +
    + return FALSE;
    + }
    +
    + manager = gplugin_manager_get_default();
    + ret = gplugin_manager_unregister_loader(manager, loader, error);
    + g_clear_object(&loader);
    +
    + return ret;
    }
    GPLUGIN_NATIVE_PLUGIN_DECLARE(gplugin_perl5)
    --- a/perl5/gplugin-perl5-loader.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/perl5/gplugin-perl5-loader.c Mon Sep 27 08:41:55 2021 -0500
    @@ -235,7 +235,7 @@
    SAVETMPS;
    PUSHMARK(SP);
    EXTEND(SP, 1);
    - PUSHs(sv_2mortal(newSVGObject(G_OBJECT(pplugin))));
    + PUSHs(sv_2mortal(newSVGObject(g_object_ref(G_OBJECT(pplugin)))));
    PUTBACK;
    count = call_pv("gplugin_load", G_EVAL | G_SCALAR);
    @@ -267,14 +267,7 @@
    my_perl = old;
    - /* this is magic and is keeping this working. Why I don't know, but we're
    - * debating chucking this loader out the window and I want to create this
    - * review request so I'm leaving it in for now...
    - */
    - g_message(
    - "load returning: %d for %s",
    - r,
    - gplugin_plugin_get_filename(plugin));
    + PERL_SET_CONTEXT(my_perl);
    return r;
    }
    @@ -283,6 +276,7 @@
    gplugin_perl_loader_unload(
    G_GNUC_UNUSED GPluginLoader *loader,
    GPluginPlugin *plugin,
    + gboolean shutdown,
    GError **error)
    {
    GPluginPerlPlugin *pplugin = GPLUGIN_PERL_PLUGIN(plugin);
    @@ -292,7 +286,6 @@
    gint count = 0;
    dSP;
    -
    old = my_perl;
    my_perl = gplugin_perl_plugin_get_interpreter(pplugin);
    PERL_SET_CONTEXT(my_perl);
    @@ -300,8 +293,9 @@
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    - EXTEND(SP, 1);
    - PUSHs(sv_2mortal(newSVGObject(G_OBJECT(pplugin))));
    + EXTEND(SP, 2);
    + PUSHs(sv_2mortal(newSVGObject(g_object_ref(G_OBJECT(pplugin)))));
    + PUSHs(sv_2mortal(newSViv(shutdown)));
    PUTBACK;
    count = call_pv("gplugin_unload", G_EVAL | G_SCALAR);
    @@ -332,6 +326,7 @@
    LEAVE;
    my_perl = old;
    + PERL_SET_CONTEXT(my_perl);
    return r;
    }
    --- a/perl5/tests/plugins/basic.pl Sat Sep 25 14:18:43 2021 -0500
    +++ b/perl5/tests/plugins/basic.pl Mon Sep 27 08:41:55 2021 -0500
    @@ -22,7 +22,7 @@
    package => "GPlugin");
    sub gplugin_query {
    - return GPlugin::PluginInfo->new(
    + my $info = GPlugin::PluginInfo->new(
    id => "gplugin/perl5-basic-plugin",
    abi_version => 0x01020304,
    name => "basic plugin",
    @@ -34,6 +34,8 @@
    website => "website",
    description => "description",
    );
    +
    + return $info;
    }
    sub gplugin_load {
    @@ -48,10 +50,15 @@
    sub gplugin_unload {
    my $plugin = shift;
    + my $shutdown = shift;
    if(!defined($plugin) or (blessed($plugin) and !$plugin->isa("GPlugin::Plugin"))) {
    die("plugin is not a GPlugin::Plugin");
    }
    + if($shutdown) {
    + die("shutdown was true");
    + }
    +
    return 0;
    }
    --- a/perl5/tests/plugins/unload-failed.pl Sat Sep 25 14:18:43 2021 -0500
    +++ b/perl5/tests/plugins/unload-failed.pl Mon Sep 27 08:41:55 2021 -0500
    @@ -17,7 +17,7 @@
    use Glib::Object::Introspection;
    -Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package => "GPlugin");
    sub gplugin_query() {
    return GPlugin::PluginInfo->new(
    @@ -30,5 +30,7 @@
    }
    sub gplugin_unload() {
    + die("cya");
    +
    return 1;
    }
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/perl5/tests/plugins/unload-shutdown.pl Mon Sep 27 08:41:55 2021 -0500
    @@ -0,0 +1,40 @@
    +# Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <https://www.gnu.org/licenses/>.
    +
    +use strict;
    +
    +use Glib::Object::Introspection;
    +
    +Glib::Object::Introspection->setup(basename => "GPlugin", version => "1.0", package=> "GPlugin");
    +
    +sub gplugin_query() {
    + return GPlugin::PluginInfo->new(
    + id => "gplugin/perl5-unload-shutdown",
    + );
    +}
    +
    +sub gplugin_load() {
    + return 0;
    +}
    +
    +sub gplugin_unload() {
    + my ($plugin, $shutdown) = @_;
    +
    + if(not $shutdown) {
    + die("shutdown was false");
    + }
    +
    + return 0;
    +}
    --- a/python3/gplugin-python3-core.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/gplugin-python3-core.c Mon Sep 27 08:41:55 2021 -0500
    @@ -51,6 +51,7 @@
    "website", GPLUGIN_WEBSITE,
    "category", "loaders",
    "bind-global", TRUE,
    + "unloadable", FALSE,
    NULL);
    /* clang-format on */
    }
    @@ -77,15 +78,29 @@
    }
    static gboolean
    -gplugin_python3_unload(G_GNUC_UNUSED GPluginPlugin *plugin, GError **error)
    +gplugin_python3_unload(
    + G_GNUC_UNUSED GPluginPlugin *plugin,
    + gboolean shutdown,
    + GError **error)
    {
    - g_set_error_literal(
    - error,
    - GPLUGIN_DOMAIN,
    - 0,
    - _("The Python3 loader can not be unloaded"));
    + GPluginManager *manager = NULL;
    + gboolean ret = FALSE;
    - return FALSE;
    + if(!shutdown) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The Python3 loader can not be unloaded"));
    +
    + return FALSE;
    + }
    +
    + manager = gplugin_manager_get_default();
    + ret = gplugin_manager_unregister_loader(manager, loader, error);
    + g_clear_object(&loader);
    +
    + return ret;
    }
    GPLUGIN_NATIVE_PLUGIN_DECLARE(gplugin_python3)
    --- a/python3/gplugin-python3-loader.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/gplugin-python3-loader.c Mon Sep 27 08:41:55 2021 -0500
    @@ -247,18 +247,21 @@
    gplugin_python3_loader_unload(
    G_GNUC_UNUSED GPluginLoader *loader,
    GPluginPlugin *plugin,
    + gboolean shutdown,
    GError **error)
    {
    PyObject *unload = NULL, *result = NULL;
    - PyObject *pyplugin = NULL;
    + PyObject *pyplugin = NULL, *pyshutdown = NULL;
    gboolean ret = FALSE;
    g_object_get(G_OBJECT(plugin), "unload-func", &unload, NULL);
    pyplugin = pygobject_new(G_OBJECT(plugin));
    + pyshutdown = PyBool_FromLong(shutdown);
    - result = PyObject_CallFunctionObjArgs(unload, pyplugin, NULL);
    + result = PyObject_CallFunctionObjArgs(unload, pyplugin, pyshutdown, NULL);
    Py_DECREF(pyplugin);
    + Py_DECREF(pyshutdown);
    if(PyErr_Occurred()) {
    Py_XDECREF(result);
    --- a/python3/tests/plugins/basic.py Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/tests/plugins/basic.py Mon Sep 27 08:41:55 2021 -0500
    @@ -42,9 +42,12 @@
    return True
    -def gplugin_unload(plugin):
    +def gplugin_unload(plugin, shutdown):
    if not isinstance(plugin, GPlugin.Plugin):
    raise Exception('plugin is not a GPlugin.Plugin')
    + if shutdown:
    + raise Exception('shutdown was True')
    +
    return True
    --- a/python3/tests/plugins/dependent.py Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/tests/plugins/dependent.py Mon Sep 27 08:41:55 2021 -0500
    @@ -31,5 +31,5 @@
    return False
    -def gplugin_unload(plugin):
    +def gplugin_unload(plugin, shutdown):
    return False
    --- a/python3/tests/plugins/load-exception.py Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/tests/plugins/load-exception.py Mon Sep 27 08:41:55 2021 -0500
    @@ -30,5 +30,5 @@
    raise ValueError('explosion!')
    -def gplugin_unload(plugin):
    +def gplugin_unload(plugin, shutdown):
    return True
    --- a/python3/tests/plugins/load-failed.py Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/tests/plugins/load-failed.py Mon Sep 27 08:41:55 2021 -0500
    @@ -30,5 +30,5 @@
    return False
    -def gplugin_unload(plugin):
    +def gplugin_unload(plugin, shutdown):
    return True
    --- a/python3/tests/plugins/unload-failed.py Sat Sep 25 14:18:43 2021 -0500
    +++ b/python3/tests/plugins/unload-failed.py Mon Sep 27 08:41:55 2021 -0500
    @@ -30,5 +30,5 @@
    return True
    -def gplugin_unload(plugin):
    +def gplugin_unload(plugin, shutdown):
    return False
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/python3/tests/plugins/unload-shutdown.py Mon Sep 27 08:41:55 2021 -0500
    @@ -0,0 +1,37 @@
    +# vi:et:ts=4 sw=4 sts=4
    +# Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    +#
    +# This library is free software; you can redistribute it and/or
    +# modify it under the terms of the GNU Lesser General Public
    +# License as published by the Free Software Foundation; either
    +# version 2 of the License, or (at your option) any later version.
    +#
    +# This library is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    +# Lesser General Public License for more details.
    +#
    +# You should have received a copy of the GNU Lesser General Public
    +# License along with this library; if not, see <https://www.gnu.org/licenses/>.
    +
    +import gi
    +
    +gi.require_version('GPlugin', '1.0')
    +from gi.repository import GPlugin # noqa
    +
    +
    +def gplugin_query():
    + return GPlugin.PluginInfo(
    + id="gplugin/python3-unload-shutdown",
    + )
    +
    +
    +def gplugin_load(plugin):
    + return True
    +
    +
    +def gplugin_unload(plugin, shutdown):
    + if not shutdown:
    + raise Exception("shutdown was false");
    +
    + return True
    --- a/tcc/gplugin-tcc-core.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/tcc/gplugin-tcc-core.c Mon Sep 27 08:41:55 2021 -0500
    @@ -45,6 +45,7 @@
    "authors", authors,
    "website", GPLUGIN_WEBSITE,
    "category", "loaders",
    + "unloadable", FALSE,
    NULL);
    /* clang-format on */
    }
    @@ -70,13 +71,27 @@
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + gboolean shutdown,
    + GError **error)
    {
    - g_set_error_literal(
    - error,
    - GPLUGIN_DOMAIN,
    - 0,
    - _("The TCC loader can not be unloaded"));
    + GPluginManager *manager = NULL;
    + gboolean ret = FALSE;
    +
    + if(!shutdown) {
    + g_set_error_literal(
    + error,
    + GPLUGIN_DOMAIN,
    + 0,
    + _("The TCC loader can not be unloaded"));
    - return FALSE;
    + return FALSE;
    + }
    +
    + manager = gplugin_manager_get_default();
    + ret = gplugin_manager_unregister_loader(manager, loader, error);
    + g_clear_object(&loader);
    +
    + return ret;
    }
    --- a/tcc/tests/plugins/basic-plugin.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/tcc/tests/plugins/basic-plugin.c Mon Sep 27 08:41:55 2021 -0500
    @@ -49,6 +49,7 @@
    G_MODULE_EXPORT gboolean
    gplugin_unload(
    G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/tcc/tests/plugins/dependent.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/tcc/tests/plugins/dependent.c Mon Sep 27 08:41:55 2021 -0500
    @@ -42,6 +42,7 @@
    G_MODULE_EXPORT gboolean
    gplugin_unload(
    G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/tcc/tests/plugins/load-exception.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/tcc/tests/plugins/load-exception.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,6 +37,7 @@
    G_MODULE_EXPORT gboolean
    gplugin_unload(
    G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/tcc/tests/plugins/load-failed.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/tcc/tests/plugins/load-failed.c Mon Sep 27 08:41:55 2021 -0500
    @@ -37,6 +37,7 @@
    G_MODULE_EXPORT gboolean
    gplugin_unload(
    G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    G_GNUC_UNUSED GError **error)
    {
    return TRUE;
    --- a/tcc/tests/plugins/unload-failed.c Sat Sep 25 14:18:43 2021 -0500
    +++ b/tcc/tests/plugins/unload-failed.c Mon Sep 27 08:41:55 2021 -0500
    @@ -35,7 +35,10 @@
    }
    G_MODULE_EXPORT gboolean
    -gplugin_unload(G_GNUC_UNUSED GPluginNativePlugin *plugin, GError **error)
    +gplugin_unload(
    + G_GNUC_UNUSED GPluginNativePlugin *plugin,
    + G_GNUC_UNUSED gboolean shutdown,
    + GError **error)
    {
    g_set_error(error, GPLUGIN_DOMAIN, 0, "expected error");
    --- a/vala/tests/genie-plugins/basic.gs Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/genie-plugins/basic.gs Mon Sep 27 08:41:55 2021 -0500
    @@ -47,11 +47,15 @@
    return true
    -def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    +def gplugin_unload(plugin : GPlugin.Plugin, shutdown : bool, out error : Error) : bool
    if not(plugin isa GPlugin.Plugin)
    error = new Error(Quark.from_string("gplugin"), 0, "plugin was not set")
    return false
    + if shutdown
    + error = new Error(Quark.from_string("gplugin"), 0, "shutdown was true")
    + return false
    +
    error = null
    return true
    --- a/vala/tests/genie-plugins/dependent.gs Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/genie-plugins/dependent.gs Mon Sep 27 08:41:55 2021 -0500
    @@ -35,7 +35,7 @@
    return true
    -def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    +def gplugin_unload(plugin : GPlugin.Plugin, shutdown : bool, out error : Error) : bool
    error = null
    return false
    --- a/vala/tests/genie-plugins/load-exception.gs Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/genie-plugins/load-exception.gs Mon Sep 27 08:41:55 2021 -0500
    @@ -32,7 +32,7 @@
    return false
    -def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    +def gplugin_unload(plugin : GPlugin.Plugin, shutdown : bool, out error : Error) : bool
    error = null
    return true
    --- a/vala/tests/genie-plugins/load-failed.gs Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/genie-plugins/load-failed.gs Mon Sep 27 08:41:55 2021 -0500
    @@ -32,7 +32,7 @@
    return false
    -def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    +def gplugin_unload(plugin : GPlugin.Plugin, shutdown : bool, out error : Error) : bool
    error = null
    return true
    --- a/vala/tests/genie-plugins/meson.build Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/genie-plugins/meson.build Mon Sep 27 08:41:55 2021 -0500
    @@ -11,6 +11,9 @@
    shared_library('load-failed-plugin', 'load-failed.gs',
    name_prefix : '',
    dependencies : [gplugin_dep, gplugin_vapi])
    + shared_library('unload-shutdown-plugin', 'unload-shutdown.gs',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    shared_library('unload-failed-plugin', 'unload-failed.gs',
    name_prefix : '',
    dependencies : [gplugin_dep, gplugin_vapi])
    --- a/vala/tests/genie-plugins/unload-failed.gs Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/genie-plugins/unload-failed.gs Mon Sep 27 08:41:55 2021 -0500
    @@ -32,7 +32,7 @@
    return true
    -def gplugin_unload(plugin : GPlugin.Plugin, out error : Error) : bool
    +def gplugin_unload(plugin : GPlugin.Plugin, shutdown : bool, out error : Error) : bool
    error = null
    return false
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/genie-plugins/unload-shutdown.gs Mon Sep 27 08:41:55 2021 -0500
    @@ -0,0 +1,47 @@
    +/*
    + * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <https://www.gnu.org/licenses/>.
    + */
    +uses GLib
    +uses GPlugin
    +
    +class UnloadShutdownPluginInfo : GPlugin.PluginInfo
    + construct()
    + Object(
    + id: "gplugin/genie-unload-shutdown"
    + )
    +
    +exception UnloadError
    + ShutdownNotSet
    +
    +def gplugin_query(out error : Error) : GPlugin.PluginInfo
    + error = null
    +
    + return new UnloadShutdownPluginInfo()
    +
    +def gplugin_load(plugin : GPlugin.Plugin, out error : Error) : bool
    + error = null
    +
    + return true
    +
    +def gplugin_unload(plugin : GPlugin.Plugin, shutdown : bool, out error : Error) : bool
    + error = null
    +
    + if not shutdown
    + error = new Error(Quark.from_string("genie"), 0, "shutdown was false")
    +
    + return false
    +
    + return true
    --- a/vala/tests/plugins/basic.vala Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/plugins/basic.vala Mon Sep 27 08:41:55 2021 -0500
    @@ -52,12 +52,17 @@
    return true;
    }
    -public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    +public bool gplugin_unload(GPlugin.Plugin plugin, bool shutdown, out Error error) {
    if(!(plugin is GPlugin.Plugin)) {
    error = new Error(Quark.from_string("gplugin"), 0, "plugin as not set");
    return false;
    }
    + if(shutdown) {
    + error = new Error(Quark.from_string("gplugin"), 0, "shutdown was true");
    + return false;
    + }
    +
    error = null;
    return true;
    --- a/vala/tests/plugins/dependent.vala Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/plugins/dependent.vala Mon Sep 27 08:41:55 2021 -0500
    @@ -39,7 +39,7 @@
    return true;
    }
    -public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    +public bool gplugin_unload(GPlugin.Plugin plugin, bool shutdown, out Error error) {
    error = null;
    return false;
    --- a/vala/tests/plugins/load-exception.vala Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/plugins/load-exception.vala Mon Sep 27 08:41:55 2021 -0500
    @@ -36,7 +36,7 @@
    return false;
    }
    -public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    +public bool gplugin_unload(GPlugin.Plugin plugin, bool shutdown, out Error error) {
    error = null;
    return true;
    --- a/vala/tests/plugins/load-failed.vala Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/plugins/load-failed.vala Mon Sep 27 08:41:55 2021 -0500
    @@ -36,7 +36,7 @@
    return false;
    }
    -public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    +public bool gplugin_unload(GPlugin.Plugin plugin, bool shutdown, out Error error) {
    error = null;
    return true;
    --- a/vala/tests/plugins/meson.build Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/plugins/meson.build Mon Sep 27 08:41:55 2021 -0500
    @@ -14,4 +14,7 @@
    shared_library('vala-unload-failed', 'unload-failed.vala',
    name_prefix : '',
    dependencies : [gplugin_dep, gplugin_vapi])
    + shared_library('vala-unload-shutdown', 'unload-shutdown.vala',
    + name_prefix : '',
    + dependencies : [gplugin_dep, gplugin_vapi])
    endif # vapi
    --- a/vala/tests/plugins/unload-failed.vala Sat Sep 25 14:18:43 2021 -0500
    +++ b/vala/tests/plugins/unload-failed.vala Mon Sep 27 08:41:55 2021 -0500
    @@ -36,7 +36,7 @@
    return true;
    }
    -public bool gplugin_unload(GPlugin.Plugin plugin, out Error error) {
    +public bool gplugin_unload(GPlugin.Plugin plugin, bool shutdown, out Error error) {
    error = null;
    return false;
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/vala/tests/plugins/unload-shutdown.vala Mon Sep 27 08:41:55 2021 -0500
    @@ -0,0 +1,49 @@
    +/*
    + * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Lesser General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * Lesser General Public License for more details.
    + *
    + * You should have received a copy of the GNU Lesser General Public
    + * License along with this library; if not, see <https://www.gnu.org/licenses/>.
    + */
    +using GPlugin;
    +
    +public class UnloadShutdownPluginInfo : GPlugin.PluginInfo {
    + public UnloadShutdownPluginInfo() {
    + Object(
    + id: "gplugin/vala-unload-shutdown"
    + );
    + }
    +}
    +
    +public GPlugin.PluginInfo gplugin_query(out Error error) {
    + error = null;
    +
    + return new UnloadShutdownPluginInfo();
    +}
    +
    +public bool gplugin_load(GPlugin.Plugin plugin, out Error error) {
    + error = null;
    +
    + return true;
    +}
    +
    +public bool gplugin_unload(GPlugin.Plugin plugin, bool shutdown, out Error error) {
    + if(!shutdown) {
    + error = new Error(Quark.from_string("vala"), 0, "shutdown was false");
    +
    + return false;
    + }
    +
    + error = null;
    +
    + return true;
    +}