# HG changeset patch
# Node ID 30bd12b49df3
# Parent  c8e4dba3aa65
diff --git a/gplugin-query/gplugin-query.c b/gplugin-query/gplugin-query.c
--- a/gplugin-query/gplugin-query.c
+++ b/gplugin-query/gplugin-query.c
@@ -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));
diff --git a/gplugin/gplugin-loader-tests.c b/gplugin/gplugin-loader-tests.c
--- a/gplugin/gplugin-loader-tests.c
+++ b/gplugin/gplugin-loader-tests.c
@@ -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},
 };
diff --git a/gplugin/gplugin-loader.h b/gplugin/gplugin-loader.h
--- a/gplugin/gplugin-loader.h
+++ b/gplugin/gplugin-loader.h
@@ -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
diff --git a/gplugin/gplugin-loader.c b/gplugin/gplugin-loader.c
--- a/gplugin/gplugin-loader.c
+++ b/gplugin/gplugin-loader.c
@@ -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) {
diff --git a/gplugin/gplugin-manager.h b/gplugin/gplugin-manager.h
--- a/gplugin/gplugin-manager.h
+++ b/gplugin/gplugin-manager.h
@@ -47,7 +47,6 @@
 	GObjectClass parent;
 
 	/*< public >*/
-
 	gboolean (*loading_plugin)(
 		GPluginManager *manager,
 		GPluginPlugin *plugin,
diff --git a/gplugin/gplugin-manager.c b/gplugin/gplugin-manager.c
--- a/gplugin/gplugin-manager.c
+++ b/gplugin/gplugin-manager.c
@@ -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);
diff --git a/gplugin/gplugin-native-loader.c b/gplugin/gplugin-native-loader.c
--- a/gplugin/gplugin-native-loader.c
+++ b/gplugin/gplugin-native-loader.c
@@ -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 */
 }
-
diff --git a/gplugin/gplugin-native-plugin.h b/gplugin/gplugin-native-plugin.h
--- a/gplugin/gplugin-native-plugin.h
+++ b/gplugin/gplugin-native-plugin.h
@@ -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
diff --git a/gplugin/gplugin-plugin-info.h b/gplugin/gplugin-plugin-info.h
--- a/gplugin/gplugin-plugin-info.h
+++ b/gplugin/gplugin-plugin-info.h
@@ -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
 
diff --git a/gplugin/gplugin-plugin-info.c b/gplugin/gplugin-plugin-info.c
--- a/gplugin/gplugin-plugin-info.c
+++ b/gplugin/gplugin-plugin-info.c
@@ -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;
+}
diff --git a/gplugin/tests/bad-plugins/query-error.c b/gplugin/tests/bad-plugins/query-error.c
--- a/gplugin/tests/bad-plugins/query-error.c
+++ b/gplugin/tests/bad-plugins/query-error.c
@@ -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;
diff --git a/gplugin/tests/bind-global/bind-global.c b/gplugin/tests/bind-global/bind-global.c
--- a/gplugin/tests/bind-global/bind-global.c
+++ b/gplugin/tests/bind-global/bind-global.c
@@ -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;
diff --git a/gplugin/tests/dynamic-type/dynamic-type-provider.c b/gplugin/tests/dynamic-type/dynamic-type-provider.c
--- a/gplugin/tests/dynamic-type/dynamic-type-provider.c
+++ b/gplugin/tests/dynamic-type/dynamic-type-provider.c
@@ -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;
diff --git a/gplugin/tests/dynamic-type/dynamic-type-user.c b/gplugin/tests/dynamic-type/dynamic-type-user.c
--- a/gplugin/tests/dynamic-type/dynamic-type-user.c
+++ b/gplugin/tests/dynamic-type/dynamic-type-user.c
@@ -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);
diff --git a/gplugin/tests/id-collision/id-collision1.c b/gplugin/tests/id-collision/id-collision1.c
--- a/gplugin/tests/id-collision/id-collision1.c
+++ b/gplugin/tests/id-collision/id-collision1.c
@@ -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;
diff --git a/gplugin/tests/id-collision/id-collision2.c b/gplugin/tests/id-collision/id-collision2.c
--- a/gplugin/tests/id-collision/id-collision2.c
+++ b/gplugin/tests/id-collision/id-collision2.c
@@ -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;
diff --git a/gplugin/tests/load-on-query-fail/load-on-query-fail.c b/gplugin/tests/load-on-query-fail/load-on-query-fail.c
--- a/gplugin/tests/load-on-query-fail/load-on-query-fail.c
+++ b/gplugin/tests/load-on-query-fail/load-on-query-fail.c
@@ -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;
diff --git a/gplugin/tests/load-on-query-pass/load-on-query-pass.c b/gplugin/tests/load-on-query-pass/load-on-query-pass.c
--- a/gplugin/tests/load-on-query-pass/load-on-query-pass.c
+++ b/gplugin/tests/load-on-query-pass/load-on-query-pass.c
@@ -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;
diff --git a/gplugin/tests/newest-version/multiple-semantic-1.0.0.c b/gplugin/tests/newest-version/multiple-semantic-1.0.0.c
--- a/gplugin/tests/newest-version/multiple-semantic-1.0.0.c
+++ b/gplugin/tests/newest-version/multiple-semantic-1.0.0.c
@@ -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;
diff --git a/gplugin/tests/newest-version/multiple-semantic-1.1.0.c b/gplugin/tests/newest-version/multiple-semantic-1.1.0.c
--- a/gplugin/tests/newest-version/multiple-semantic-1.1.0.c
+++ b/gplugin/tests/newest-version/multiple-semantic-1.1.0.c
@@ -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;
diff --git a/gplugin/tests/newest-version/no-version-and-semantic-no-version.c b/gplugin/tests/newest-version/no-version-and-semantic-no-version.c
--- a/gplugin/tests/newest-version/no-version-and-semantic-no-version.c
+++ b/gplugin/tests/newest-version/no-version-and-semantic-no-version.c
@@ -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;
diff --git a/gplugin/tests/newest-version/no-version-and-semantic-semantic.c b/gplugin/tests/newest-version/no-version-and-semantic-semantic.c
--- a/gplugin/tests/newest-version/no-version-and-semantic-semantic.c
+++ b/gplugin/tests/newest-version/no-version-and-semantic-semantic.c
@@ -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;
diff --git a/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c b/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c
--- a/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c
+++ b/gplugin/tests/newest-version/non-semantic-and-semantic-non-semantic.c
@@ -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;
diff --git a/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c b/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c
--- a/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c
+++ b/gplugin/tests/newest-version/non-semantic-and-semantic-semantic.c
@@ -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;
diff --git a/gplugin/tests/newest-version/solo-no-version.c b/gplugin/tests/newest-version/solo-no-version.c
--- a/gplugin/tests/newest-version/solo-no-version.c
+++ b/gplugin/tests/newest-version/solo-no-version.c
@@ -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;
diff --git a/gplugin/tests/newest-version/solo-non-semantic.c b/gplugin/tests/newest-version/solo-non-semantic.c
--- a/gplugin/tests/newest-version/solo-non-semantic.c
+++ b/gplugin/tests/newest-version/solo-non-semantic.c
@@ -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;
diff --git a/gplugin/tests/plugins/basic-plugin.c b/gplugin/tests/plugins/basic-plugin.c
--- a/gplugin/tests/plugins/basic-plugin.c
+++ b/gplugin/tests/plugins/basic-plugin.c
@@ -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");
diff --git a/gplugin/tests/plugins/broken-dependent-plugin.c b/gplugin/tests/plugins/broken-dependent-plugin.c
--- a/gplugin/tests/plugins/broken-dependent-plugin.c
+++ b/gplugin/tests/plugins/broken-dependent-plugin.c
@@ -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;
diff --git a/gplugin/tests/plugins/dependent-plugin.c b/gplugin/tests/plugins/dependent-plugin.c
--- a/gplugin/tests/plugins/dependent-plugin.c
+++ b/gplugin/tests/plugins/dependent-plugin.c
@@ -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;
diff --git a/gplugin/tests/plugins/load-exception.c b/gplugin/tests/plugins/load-exception.c
--- a/gplugin/tests/plugins/load-exception.c
+++ b/gplugin/tests/plugins/load-exception.c
@@ -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;
diff --git a/gplugin/tests/plugins/load-failed.c b/gplugin/tests/plugins/load-failed.c
--- a/gplugin/tests/plugins/load-failed.c
+++ b/gplugin/tests/plugins/load-failed.c
@@ -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;
diff --git a/gplugin/tests/plugins/meson.build b/gplugin/tests/plugins/meson.build
--- a/gplugin/tests/plugins/meson.build
+++ b/gplugin/tests/plugins/meson.build
@@ -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])
diff --git a/gplugin/tests/plugins/unload-failed.c b/gplugin/tests/plugins/unload-failed.c
--- a/gplugin/tests/plugins/unload-failed.c
+++ b/gplugin/tests/plugins/unload-failed.c
@@ -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");
 
diff --git a/gplugin/tests/plugins/unload-shutdown.c b/gplugin/tests/plugins/unload-shutdown.c
new file mode 100644
--- /dev/null
+++ b/gplugin/tests/plugins/unload-shutdown.c
@@ -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)
diff --git a/gplugin/tests/test-find-plugins.c b/gplugin/tests/test-find-plugins.c
--- a/gplugin/tests/test-find-plugins.c
+++ b/gplugin/tests/test-find-plugins.c
@@ -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);
 
diff --git a/gplugin/tests/test-loader-registration.c b/gplugin/tests/test-loader-registration.c
--- a/gplugin/tests/test-loader-registration.c
+++ b/gplugin/tests/test-loader-registration.c
@@ -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;
diff --git a/gplugin/tests/test-loader.c b/gplugin/tests/test-loader.c
--- a/gplugin/tests/test-loader.c
+++ b/gplugin/tests/test-loader.c
@@ -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);
diff --git a/gplugin/tests/unresolved-symbol/unresolved-symbol.c b/gplugin/tests/unresolved-symbol/unresolved-symbol.c
--- a/gplugin/tests/unresolved-symbol/unresolved-symbol.c
+++ b/gplugin/tests/unresolved-symbol/unresolved-symbol.c
@@ -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;
diff --git a/gplugin/tests/versioned-dependencies/bar.c b/gplugin/tests/versioned-dependencies/bar.c
--- a/gplugin/tests/versioned-dependencies/bar.c
+++ b/gplugin/tests/versioned-dependencies/bar.c
@@ -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;
 }
diff --git a/gplugin/tests/versioned-dependencies/baz.c b/gplugin/tests/versioned-dependencies/baz.c
--- a/gplugin/tests/versioned-dependencies/baz.c
+++ b/gplugin/tests/versioned-dependencies/baz.c
@@ -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;
 }
diff --git a/gplugin/tests/versioned-dependencies/exact1.c b/gplugin/tests/versioned-dependencies/exact1.c
--- a/gplugin/tests/versioned-dependencies/exact1.c
+++ b/gplugin/tests/versioned-dependencies/exact1.c
@@ -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;
 }
diff --git a/gplugin/tests/versioned-dependencies/exact2.c b/gplugin/tests/versioned-dependencies/exact2.c
--- a/gplugin/tests/versioned-dependencies/exact2.c
+++ b/gplugin/tests/versioned-dependencies/exact2.c
@@ -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;
 }
diff --git a/gplugin/tests/versioned-dependencies/fez.c b/gplugin/tests/versioned-dependencies/fez.c
--- a/gplugin/tests/versioned-dependencies/fez.c
+++ b/gplugin/tests/versioned-dependencies/fez.c
@@ -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;
 }
diff --git a/gplugin/tests/versioned-dependencies/greater-equal.c b/gplugin/tests/versioned-dependencies/greater-equal.c
--- a/gplugin/tests/versioned-dependencies/greater-equal.c
+++ b/gplugin/tests/versioned-dependencies/greater-equal.c
@@ -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;
diff --git a/gplugin/tests/versioned-dependencies/greater.c b/gplugin/tests/versioned-dependencies/greater.c
--- a/gplugin/tests/versioned-dependencies/greater.c
+++ b/gplugin/tests/versioned-dependencies/greater.c
@@ -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;
diff --git a/gplugin/tests/versioned-dependencies/less-equal.c b/gplugin/tests/versioned-dependencies/less-equal.c
--- a/gplugin/tests/versioned-dependencies/less-equal.c
+++ b/gplugin/tests/versioned-dependencies/less-equal.c
@@ -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;
diff --git a/gplugin/tests/versioned-dependencies/less.c b/gplugin/tests/versioned-dependencies/less.c
--- a/gplugin/tests/versioned-dependencies/less.c
+++ b/gplugin/tests/versioned-dependencies/less.c
@@ -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;
 }
diff --git a/gplugin/tests/versioned-dependencies/no-version.c b/gplugin/tests/versioned-dependencies/no-version.c
--- a/gplugin/tests/versioned-dependencies/no-version.c
+++ b/gplugin/tests/versioned-dependencies/no-version.c
@@ -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;
diff --git a/gplugin/tests/versioned-dependencies/super-dependent.c b/gplugin/tests/versioned-dependencies/super-dependent.c
--- a/gplugin/tests/versioned-dependencies/super-dependent.c
+++ b/gplugin/tests/versioned-dependencies/super-dependent.c
@@ -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;
diff --git a/lua/gplugin-lua-core.c b/lua/gplugin-lua-core.c
--- a/lua/gplugin-lua-core.c
+++ b/lua/gplugin-lua-core.c
@@ -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)
diff --git a/lua/gplugin-lua-loader.c b/lua/gplugin-lua-loader.c
--- a/lua/gplugin-lua-loader.c
+++ b/lua/gplugin-lua-loader.c
@@ -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;
 }
 
 /******************************************************************************
diff --git a/lua/tests/plugins/basic.lua b/lua/tests/plugins/basic.lua
--- a/lua/tests/plugins/basic.lua
+++ b/lua/tests/plugins/basic.lua
@@ -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
diff --git a/lua/tests/plugins/dependent.lua b/lua/tests/plugins/dependent.lua
--- a/lua/tests/plugins/dependent.lua
+++ b/lua/tests/plugins/dependent.lua
@@ -29,6 +29,6 @@
 	return false
 end
 
-function gplugin_unload(plugin)
+function gplugin_unload(plugin, shutdown)
 	return false
 end
diff --git a/lua/tests/plugins/load-exception.lua b/lua/tests/plugins/load-exception.lua
--- a/lua/tests/plugins/load-exception.lua
+++ b/lua/tests/plugins/load-exception.lua
@@ -28,6 +28,6 @@
 	error('explosion!')
 end
 
-function gplugin_unload(plugin)
+function gplugin_unload(plugin, shutdown)
 	return false
 end
diff --git a/lua/tests/plugins/load-failed.lua b/lua/tests/plugins/load-failed.lua
--- a/lua/tests/plugins/load-failed.lua
+++ b/lua/tests/plugins/load-failed.lua
@@ -28,6 +28,6 @@
 	return false
 end
 
-function gplugin_unload(plugin)
+function gplugin_unload(plugin, shutdown)
 	return true
 end
diff --git a/lua/tests/plugins/unload-failed.lua b/lua/tests/plugins/unload-failed.lua
--- a/lua/tests/plugins/unload-failed.lua
+++ b/lua/tests/plugins/unload-failed.lua
@@ -28,6 +28,6 @@
 	return true
 end
 
-function gplugin_unload(plugin)
+function gplugin_unload(plugin, shutdown)
 	return false
 end
diff --git a/lua/tests/plugins/unload-shutdown.lua b/lua/tests/plugins/unload-shutdown.lua
new file mode 100644
--- /dev/null
+++ b/lua/tests/plugins/unload-shutdown.lua
@@ -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
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -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'])
 
diff --git a/perl5/gplugin-perl5-core.c b/perl5/gplugin-perl5-core.c
--- a/perl5/gplugin-perl5-core.c
+++ b/perl5/gplugin-perl5-core.c
@@ -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)
diff --git a/perl5/gplugin-perl5-loader.c b/perl5/gplugin-perl5-loader.c
--- a/perl5/gplugin-perl5-loader.c
+++ b/perl5/gplugin-perl5-loader.c
@@ -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;
 }
diff --git a/perl5/tests/plugins/basic.pl b/perl5/tests/plugins/basic.pl
--- a/perl5/tests/plugins/basic.pl
+++ b/perl5/tests/plugins/basic.pl
@@ -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;
 }
diff --git a/perl5/tests/plugins/unload-failed.pl b/perl5/tests/plugins/unload-failed.pl
--- a/perl5/tests/plugins/unload-failed.pl
+++ b/perl5/tests/plugins/unload-failed.pl
@@ -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;
 }
diff --git a/perl5/tests/plugins/unload-shutdown.pl b/perl5/tests/plugins/unload-shutdown.pl
new file mode 100644
--- /dev/null
+++ b/perl5/tests/plugins/unload-shutdown.pl
@@ -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;
+}
diff --git a/python3/gplugin-python3-core.c b/python3/gplugin-python3-core.c
--- a/python3/gplugin-python3-core.c
+++ b/python3/gplugin-python3-core.c
@@ -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)
diff --git a/python3/gplugin-python3-loader.c b/python3/gplugin-python3-loader.c
--- a/python3/gplugin-python3-loader.c
+++ b/python3/gplugin-python3-loader.c
@@ -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);
diff --git a/python3/tests/plugins/basic.py b/python3/tests/plugins/basic.py
--- a/python3/tests/plugins/basic.py
+++ b/python3/tests/plugins/basic.py
@@ -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
 
diff --git a/python3/tests/plugins/dependent.py b/python3/tests/plugins/dependent.py
--- a/python3/tests/plugins/dependent.py
+++ b/python3/tests/plugins/dependent.py
@@ -31,5 +31,5 @@
     return False
 
 
-def gplugin_unload(plugin):
+def gplugin_unload(plugin, shutdown):
     return False
diff --git a/python3/tests/plugins/load-exception.py b/python3/tests/plugins/load-exception.py
--- a/python3/tests/plugins/load-exception.py
+++ b/python3/tests/plugins/load-exception.py
@@ -30,5 +30,5 @@
     raise ValueError('explosion!')
 
 
-def gplugin_unload(plugin):
+def gplugin_unload(plugin, shutdown):
     return True
diff --git a/python3/tests/plugins/load-failed.py b/python3/tests/plugins/load-failed.py
--- a/python3/tests/plugins/load-failed.py
+++ b/python3/tests/plugins/load-failed.py
@@ -30,5 +30,5 @@
     return False
 
 
-def gplugin_unload(plugin):
+def gplugin_unload(plugin, shutdown):
     return True
diff --git a/python3/tests/plugins/unload-failed.py b/python3/tests/plugins/unload-failed.py
--- a/python3/tests/plugins/unload-failed.py
+++ b/python3/tests/plugins/unload-failed.py
@@ -30,5 +30,5 @@
     return True
 
 
-def gplugin_unload(plugin):
+def gplugin_unload(plugin, shutdown):
     return False
diff --git a/python3/tests/plugins/unload-shutdown.py b/python3/tests/plugins/unload-shutdown.py
new file mode 100644
--- /dev/null
+++ b/python3/tests/plugins/unload-shutdown.py
@@ -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
diff --git a/tcc/gplugin-tcc-core.c b/tcc/gplugin-tcc-core.c
--- a/tcc/gplugin-tcc-core.c
+++ b/tcc/gplugin-tcc-core.c
@@ -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;
 }
diff --git a/tcc/tests/plugins/basic-plugin.c b/tcc/tests/plugins/basic-plugin.c
--- a/tcc/tests/plugins/basic-plugin.c
+++ b/tcc/tests/plugins/basic-plugin.c
@@ -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;
diff --git a/tcc/tests/plugins/dependent.c b/tcc/tests/plugins/dependent.c
--- a/tcc/tests/plugins/dependent.c
+++ b/tcc/tests/plugins/dependent.c
@@ -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;
diff --git a/tcc/tests/plugins/load-exception.c b/tcc/tests/plugins/load-exception.c
--- a/tcc/tests/plugins/load-exception.c
+++ b/tcc/tests/plugins/load-exception.c
@@ -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;
diff --git a/tcc/tests/plugins/load-failed.c b/tcc/tests/plugins/load-failed.c
--- a/tcc/tests/plugins/load-failed.c
+++ b/tcc/tests/plugins/load-failed.c
@@ -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;
diff --git a/tcc/tests/plugins/unload-failed.c b/tcc/tests/plugins/unload-failed.c
--- a/tcc/tests/plugins/unload-failed.c
+++ b/tcc/tests/plugins/unload-failed.c
@@ -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");
 
diff --git a/vala/tests/genie-plugins/basic.gs b/vala/tests/genie-plugins/basic.gs
--- a/vala/tests/genie-plugins/basic.gs
+++ b/vala/tests/genie-plugins/basic.gs
@@ -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
diff --git a/vala/tests/genie-plugins/dependent.gs b/vala/tests/genie-plugins/dependent.gs
--- a/vala/tests/genie-plugins/dependent.gs
+++ b/vala/tests/genie-plugins/dependent.gs
@@ -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
diff --git a/vala/tests/genie-plugins/load-exception.gs b/vala/tests/genie-plugins/load-exception.gs
--- a/vala/tests/genie-plugins/load-exception.gs
+++ b/vala/tests/genie-plugins/load-exception.gs
@@ -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
diff --git a/vala/tests/genie-plugins/load-failed.gs b/vala/tests/genie-plugins/load-failed.gs
--- a/vala/tests/genie-plugins/load-failed.gs
+++ b/vala/tests/genie-plugins/load-failed.gs
@@ -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
diff --git a/vala/tests/genie-plugins/meson.build b/vala/tests/genie-plugins/meson.build
--- a/vala/tests/genie-plugins/meson.build
+++ b/vala/tests/genie-plugins/meson.build
@@ -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])
diff --git a/vala/tests/genie-plugins/unload-failed.gs b/vala/tests/genie-plugins/unload-failed.gs
--- a/vala/tests/genie-plugins/unload-failed.gs
+++ b/vala/tests/genie-plugins/unload-failed.gs
@@ -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
diff --git a/vala/tests/genie-plugins/unload-shutdown.gs b/vala/tests/genie-plugins/unload-shutdown.gs
new file mode 100644
--- /dev/null
+++ b/vala/tests/genie-plugins/unload-shutdown.gs
@@ -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
diff --git a/vala/tests/plugins/basic.vala b/vala/tests/plugins/basic.vala
--- a/vala/tests/plugins/basic.vala
+++ b/vala/tests/plugins/basic.vala
@@ -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;
diff --git a/vala/tests/plugins/dependent.vala b/vala/tests/plugins/dependent.vala
--- a/vala/tests/plugins/dependent.vala
+++ b/vala/tests/plugins/dependent.vala
@@ -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;
diff --git a/vala/tests/plugins/load-exception.vala b/vala/tests/plugins/load-exception.vala
--- a/vala/tests/plugins/load-exception.vala
+++ b/vala/tests/plugins/load-exception.vala
@@ -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;
diff --git a/vala/tests/plugins/load-failed.vala b/vala/tests/plugins/load-failed.vala
--- a/vala/tests/plugins/load-failed.vala
+++ b/vala/tests/plugins/load-failed.vala
@@ -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;
diff --git a/vala/tests/plugins/meson.build b/vala/tests/plugins/meson.build
--- a/vala/tests/plugins/meson.build
+++ b/vala/tests/plugins/meson.build
@@ -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
diff --git a/vala/tests/plugins/unload-failed.vala b/vala/tests/plugins/unload-failed.vala
--- a/vala/tests/plugins/unload-failed.vala
+++ b/vala/tests/plugins/unload-failed.vala
@@ -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;
diff --git a/vala/tests/plugins/unload-shutdown.vala b/vala/tests/plugins/unload-shutdown.vala
new file mode 100644
--- /dev/null
+++ b/vala/tests/plugins/unload-shutdown.vala
@@ -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;
+}
