[libpeas] Add nonglobal plugin loader support



commit cbd4ecf6f2026f9be895efa49ad5dbbb7a1c806c
Author: Garrett Regier <garrett regier riftio com>
Date:   Sun Nov 16 12:22:33 2014 -0800

    Add nonglobal plugin loader support
    
    This allows multiple threads to each have a PeasEngine
    and be used without internal locking. It also allows using
    lua5.1 plugins from multiple threads.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=739831

 libpeas/peas-engine.c                     |  136 ++++++++++++++++++++++-------
 libpeas/peas-engine.h                     |    2 +
 libpeas/peas-plugin-loader.c              |   15 +++
 libpeas/peas-plugin-loader.h              |    2 +
 loaders/lua5.1/peas-plugin-loader-lua.c   |    7 ++
 tests/libpeas/testing/testing-extension.c |   50 ++++++++---
 tests/libpeas/testing/testing.c           |    4 +-
 tests/libpeas/testing/testing.h           |    8 +-
 tests/testing-util/testing-util.c         |    8 ++-
 tests/testing-util/testing-util.h         |   17 ++--
 10 files changed, 190 insertions(+), 59 deletions(-)
---
diff --git a/libpeas/peas-engine.c b/libpeas/peas-engine.c
index 520d8ee..8ffd39e 100644
--- a/libpeas/peas-engine.c
+++ b/libpeas/peas-engine.c
@@ -4,6 +4,7 @@
  *
  * Copyright (C) 2002-2005 Paolo Maggi
  * Copyright (C) 2009 Steve Frécinaux
+ * Copyright (C) 2010-2014 Garrett Regier
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Library General Public License as published by
@@ -71,12 +72,21 @@ enum {
   PROP_0,
   PROP_PLUGIN_LIST,
   PROP_LOADED_PLUGINS,
+  PROP_NONGLOBAL_LOADERS,
   N_PROPERTIES
 };
 
 static guint signals[LAST_SIGNAL];
 static GParamSpec *properties[N_PROPERTIES] = { NULL };
 
+typedef struct _GlobalLoaderInfo {
+  PeasPluginLoader *loader;
+  PeasObjectModule *module;
+
+  guint enabled : 1;
+  guint failed : 1;
+} GlobalLoaderInfo;
+
 typedef struct _LoaderInfo {
   PeasPluginLoader *loader;
 
@@ -96,13 +106,14 @@ struct _PeasEnginePrivate {
   GList *plugin_list;
 
   guint in_dispose : 1;
+  guint use_nonglobal_loaders : 1;
 };
 
 static gboolean shutdown = FALSE;
 static PeasEngine *default_engine = NULL;
 
 static GMutex loaders_lock;
-static LoaderInfo loaders[PEAS_UTILS_N_LOADERS];
+static GlobalLoaderInfo loaders[PEAS_UTILS_N_LOADERS];
 
 static void peas_engine_load_plugin_real   (PeasEngine     *engine,
                                             PeasPluginInfo *info);
@@ -396,6 +407,9 @@ peas_engine_set_property (GObject      *object,
       peas_engine_set_loaded_plugins (engine,
                                       (const gchar **) g_value_get_boxed (value));
       break;
+    case PROP_NONGLOBAL_LOADERS:
+      engine->priv->use_nonglobal_loaders = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -420,6 +434,9 @@ peas_engine_get_property (GObject    *object,
       g_value_take_boxed (value,
                           (gconstpointer) peas_engine_get_loaded_plugins (engine));
       break;
+    case PROP_NONGLOBAL_LOADERS:
+      g_value_set_boolean (value, engine->priv->use_nonglobal_loaders);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -542,6 +559,22 @@ peas_engine_class_init (PeasEngineClass *klass)
                         G_PARAM_STATIC_STRINGS);
 
   /**
+   * PeasEngine:nonglobal-loaders:
+   *
+   * If non-global plugin loaders should be used.
+   *
+   * See peas_engine_new_with_nonglobal_loaders() for more information.
+   */
+  properties[PROP_NONGLOBAL_LOADERS] =
+    g_param_spec_boolean ("nonglobal-loaders",
+                          "Non-global Loaders",
+                          "Use non-global plugin loaders",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS);
+
+  /**
    * PeasEngine::load-plugin:
    * @engine: A #PeasEngine.
    * @info: A #PeasPluginInfo.
@@ -618,11 +651,14 @@ load_module (const gchar *module_name,
 static PeasObjectModule *
 get_plugin_loader_module (gint loader_id)
 {
+  GlobalLoaderInfo *global_loader_info = &loaders[loader_id];
   gint i, j;
-  PeasObjectModule *module;
   const gchar *loader_name;
   gchar *module_name, *module_dir;
 
+  if (global_loader_info->module != NULL)
+    return global_loader_info->module;
+
   loader_name = peas_utils_get_loader_from_id (loader_id);
   module_name = g_strconcat (loader_name, "loader", NULL);
   module_dir = peas_dirs_get_plugin_loaders_dir ();
@@ -636,14 +672,14 @@ get_plugin_loader_module (gint loader_id)
 
   module_name[j] = '\0';
 
-  module = load_module (module_name, module_dir);
+  global_loader_info->module = load_module (module_name, module_dir);
 
-  if (module == NULL)
+  if (global_loader_info->module == NULL)
     {
       gchar *tmp = module_dir;
 
       module_dir = g_build_filename (module_dir, loader_name, NULL);
-      module = load_module (module_name, module_dir);
+      global_loader_info->module = load_module (module_name, module_dir);
 
       g_free (tmp);
     }
@@ -651,26 +687,34 @@ get_plugin_loader_module (gint loader_id)
   g_free (module_dir);
   g_free (module_name);
 
-  if (module == NULL)
+  if (global_loader_info->module == NULL)
     g_warning ("Could not load plugin loader '%s'", loader_name);
 
-  return module;
+  return global_loader_info->module;
 }
 
 static PeasPluginLoader *
 create_plugin_loader (gint loader_id)
 {
-  PeasObjectModule *module;
   PeasPluginLoader *loader;
 
-  module = get_plugin_loader_module (loader_id);
-  if (module == NULL)
-    return NULL;
+  if (peas_utils_get_loader_id ("C") == loader_id)
+    {
+      loader = peas_plugin_loader_c_new ();
+    }
+  else
+    {
+      PeasObjectModule *module;
+
+      module = get_plugin_loader_module (loader_id);
+      if (module == NULL)
+        return NULL;
 
-  loader = PEAS_PLUGIN_LOADER (
-        peas_object_module_create_object (module,
-                                          PEAS_TYPE_PLUGIN_LOADER,
-                                          0, NULL));
+      loader = PEAS_PLUGIN_LOADER (
+            peas_object_module_create_object (module,
+                                              PEAS_TYPE_PLUGIN_LOADER,
+                                              0, NULL));
+    }
 
   if (loader == NULL || !peas_plugin_loader_initialize (loader))
     {
@@ -679,38 +723,41 @@ create_plugin_loader (gint loader_id)
       g_clear_object (&loader);
     }
 
-  /* Don't bother unloading the
-   * module as it is always resident
-   */
   return loader;
 }
 
 static PeasPluginLoader *
-get_local_plugin_loader (gint loader_id)
+get_local_plugin_loader (PeasEngine *engine,
+                         gint        loader_id)
 {
-  LoaderInfo *global_loader_info = &loaders[loader_id];
+  GlobalLoaderInfo *global_loader_info = &loaders[loader_id];
+  PeasPluginLoader *loader;
 
   if (global_loader_info->failed)
     return NULL;
 
-  if (global_loader_info->loader != NULL)
-    return g_object_ref (global_loader_info->loader);
-
-  if (peas_utils_get_loader_id ("C") == loader_id)
+  if (global_loader_info->loader != NULL &&
+      (!engine->priv->use_nonglobal_loaders ||
+       peas_plugin_loader_is_global (global_loader_info->loader)))
     {
-      global_loader_info->loader = peas_plugin_loader_c_new ();
       return g_object_ref (global_loader_info->loader);
     }
 
-  global_loader_info->loader = create_plugin_loader (loader_id);
+  loader = create_plugin_loader (loader_id);
 
-  if (global_loader_info->loader == NULL)
+  if (loader == NULL)
     {
       global_loader_info->failed = TRUE;
       return NULL;
     }
 
-  return g_object_ref (global_loader_info->loader);
+  if (!engine->priv->use_nonglobal_loaders ||
+      peas_plugin_loader_is_global (loader))
+    {
+      global_loader_info->loader = g_object_ref (loader);
+    }
+
+  return loader;
 }
 
 static PeasPluginLoader *
@@ -718,7 +765,7 @@ get_plugin_loader (PeasEngine *engine,
                    gint        loader_id)
 {
   LoaderInfo *loader_info = &engine->priv->loaders[loader_id];
-  LoaderInfo *global_loader_info = &loaders[loader_id];
+  GlobalLoaderInfo *global_loader_info = &loaders[loader_id];
 
   if (loader_info->loader != NULL || loader_info->failed)
     return loader_info->loader;
@@ -749,7 +796,7 @@ get_plugin_loader (PeasEngine *engine,
       return get_plugin_loader (engine, loader_id);
     }
 
-  loader_info->loader = get_local_plugin_loader (loader_id);
+  loader_info->loader = get_local_plugin_loader (engine, loader_id);
 
   if (loader_info->loader == NULL)
     loader_info->failed = TRUE;
@@ -775,7 +822,8 @@ get_plugin_loader (PeasEngine *engine,
  *
  * Unlike the C, python and python3 plugin loaders the lua5.1 plugin
  * loader can only be used from a single thread. Should this happen
- * a deadlock will occur.
+ * a deadlock will occur. This can be be avoided by using
+ * peas_engine_new_with_nonglobal_loaders().
  *
  * Note: plugin loaders used to be shared across #PeasEngines so enabling
  *       a loader on one #PeasEngine would enable it on all #PeasEngines.
@@ -1371,6 +1419,27 @@ peas_engine_new (void)
 }
 
 /**
+ * peas_engine_new_with_nonglobal_loaders:
+ *
+ * Return a new instance of #PeasEngine which will use non-global
+ * plugin loaders instead of the default global ones. This allows
+ * multiple threads to each have a #PeasEngine and be used without
+ * internal locking.
+ *
+ * Note: due to CPython's GIL the python and python3
+ *       plugin loaders are always global.
+ *
+ * Returns: a new instance of #PeasEngine that uses nonglobal loaders.
+ */
+PeasEngine *
+peas_engine_new_with_nonglobal_loaders (void)
+{
+  return PEAS_ENGINE (g_object_new (PEAS_TYPE_ENGINE,
+                                    "nonglobal-loaders", TRUE,
+                                    NULL));
+}
+
+/**
  * peas_engine_get_default:
  *
  * Return the existing instance of #PeasEngine or a subclass of it.
@@ -1423,7 +1492,7 @@ peas_engine_shutdown (void)
 
   for (i = 0; i < G_N_ELEMENTS (loaders); ++i)
     {
-      LoaderInfo *loader_info = &loaders[i];
+      GlobalLoaderInfo *loader_info = &loaders[i];
 
       if (loader_info->loader != NULL)
         {
@@ -1434,6 +1503,9 @@ peas_engine_shutdown (void)
           g_assert (loader_info->loader == NULL);
         }
 
+      /* Don't bother unloading the
+       * module as it is always resident
+       */
       loader_info->failed = TRUE;
     }
 
diff --git a/libpeas/peas-engine.h b/libpeas/peas-engine.h
index 8d23347..3618f7e 100644
--- a/libpeas/peas-engine.h
+++ b/libpeas/peas-engine.h
@@ -75,6 +75,8 @@ struct _PeasEngineClass {
 
 GType             peas_engine_get_type            (void) G_GNUC_CONST;
 PeasEngine       *peas_engine_new                 (void);
+PeasEngine       *peas_engine_new_with_nonglobal_loaders
+                                                  (void);
 PeasEngine       *peas_engine_get_default         (void);
 
 void              peas_engine_add_search_path     (PeasEngine      *engine,
diff --git a/libpeas/peas-plugin-loader.c b/libpeas/peas-plugin-loader.c
index ccd9078..4258bab 100644
--- a/libpeas/peas-plugin-loader.c
+++ b/libpeas/peas-plugin-loader.c
@@ -70,6 +70,21 @@ peas_plugin_loader_initialize (PeasPluginLoader *loader)
 }
 
 gboolean
+peas_plugin_loader_is_global (PeasPluginLoader *loader)
+{
+  PeasPluginLoaderClass *klass;
+
+  g_return_val_if_fail (PEAS_IS_PLUGIN_LOADER (loader), FALSE);
+
+  klass = PEAS_PLUGIN_LOADER_GET_CLASS (loader);
+
+  if (klass->is_global != NULL)
+    return klass->is_global (loader);
+
+  return TRUE;
+}
+
+gboolean
 peas_plugin_loader_load (PeasPluginLoader *loader,
                          PeasPluginInfo   *info)
 {
diff --git a/libpeas/peas-plugin-loader.h b/libpeas/peas-plugin-loader.h
index 30f6a88..5bf8357 100644
--- a/libpeas/peas-plugin-loader.h
+++ b/libpeas/peas-plugin-loader.h
@@ -47,6 +47,7 @@ struct _PeasPluginLoaderClass {
   GObjectClass parent;
 
   gboolean       (*initialize)            (PeasPluginLoader *loader);
+  gboolean       (*is_global)             (PeasPluginLoader *loader);
 
   gboolean       (*load)                  (PeasPluginLoader *loader,
                                            PeasPluginInfo   *info);
@@ -67,6 +68,7 @@ struct _PeasPluginLoaderClass {
 GType         peas_plugin_loader_get_type             (void)  G_GNUC_CONST;
 
 gboolean      peas_plugin_loader_initialize           (PeasPluginLoader *loader);
+gboolean      peas_plugin_loader_is_global            (PeasPluginLoader *loader);
 
 gboolean      peas_plugin_loader_load                 (PeasPluginLoader *loader,
                                                        PeasPluginInfo   *info);
diff --git a/loaders/lua5.1/peas-plugin-loader-lua.c b/loaders/lua5.1/peas-plugin-loader-lua.c
index 879e82a..fdb4a3f 100644
--- a/loaders/lua5.1/peas-plugin-loader-lua.c
+++ b/loaders/lua5.1/peas-plugin-loader-lua.c
@@ -370,6 +370,12 @@ peas_plugin_loader_lua_initialize (PeasPluginLoader *loader)
   return TRUE;
 }
 
+static gboolean
+peas_plugin_loader_lua_is_global (PeasPluginLoader *loader)
+{
+  return FALSE;
+}
+
 static void
 peas_plugin_loader_lua_init (PeasPluginLoaderLua *lua_loader)
 {
@@ -397,6 +403,7 @@ peas_plugin_loader_lua_class_init (PeasPluginLoaderLuaClass *klass)
   object_class->finalize = peas_plugin_loader_lua_finalize;
 
   loader_class->initialize = peas_plugin_loader_lua_initialize;
+  loader_class->is_global = peas_plugin_loader_lua_is_global;
   loader_class->load = peas_plugin_loader_lua_load;
   loader_class->unload = peas_plugin_loader_lua_unload;
   loader_class->create_extension = peas_plugin_loader_lua_create_extension;
diff --git a/tests/libpeas/testing/testing-extension.c b/tests/libpeas/testing/testing-extension.c
index 97e7dfc..d5dba02 100644
--- a/tests/libpeas/testing/testing-extension.c
+++ b/tests/libpeas/testing/testing-extension.c
@@ -1,8 +1,8 @@
 /*
- * testing-extensin.c
+ * testing-extension.c
  * This file is part of libpeas
  *
- * Copyright (C) 2011 - Garrett Regier
+ * Copyright (C) 2011-2014 - Garrett Regier
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Library General Public License as published by
@@ -270,25 +270,25 @@ test_extension_get_settings (PeasEngine     *engine,
 }
 
 static void
-multiple_threads_in_thread (guint nth_thread)
+multiple_threads_in_thread (guint    nth_thread,
+                            gboolean use_nonglobal_loaders)
 {
   gint i, j;
   PeasEngine *engine;
   PeasPluginInfo *info;
   GObject *extension;
-  const gboolean is_slow = strstr (loader, "python") != NULL;
 
-  engine = testing_engine_new ();
+  engine = testing_engine_new_full (use_nonglobal_loaders);
   peas_engine_enable_loader (engine, loader);
 
   info = peas_engine_get_plugin_info (engine, extension_plugin);
   g_assert (info != NULL);
 
-  for (i = 0; i < (is_slow ? 5 : 20); ++i)
+  for (i = 0; i < 10; ++i)
     {
       g_assert (peas_engine_load_plugin (engine, info));
 
-      for (j = 0; j < 5; ++j)
+      for (j = 0; j < 50; ++j)
         {
           extension = peas_engine_create_extension (engine, info,
                                                     INTROSPECTION_TYPE_BASE,
@@ -306,18 +306,22 @@ multiple_threads_in_thread (guint nth_thread)
 
 static void
 test_extension_multiple_threads (PeasEngine     *engine,
-                                 PeasPluginInfo *info)
+                                 PeasPluginInfo *info,
+                                 gboolean        use_nonglobal_loaders)
 {
-  gint i;
+  gint i, n_threads;
   GThreadPool *pool;
   GError *error = NULL;
-  const gboolean is_slow = strstr (loader, "python") != NULL;
+
+  /* Avoid too many threads, but try to get some good contention */
+  n_threads = g_get_num_processors () + 2;
 
   pool = g_thread_pool_new ((GFunc) multiple_threads_in_thread,
-                            NULL, g_get_num_processors (), TRUE, &error);
+                            GINT_TO_POINTER (use_nonglobal_loaders),
+                            n_threads, TRUE, &error);
   g_assert_no_error (error);
 
-  for (i = 0; i < (is_slow ? 20 : 100); ++i)
+  for (i = 0; i < g_thread_pool_get_max_threads (pool); ++i)
     {
       /* Cannot supply NULL as the data... */
       g_thread_pool_push (pool, GUINT_TO_POINTER (i + 1), &error);
@@ -328,6 +332,20 @@ test_extension_multiple_threads (PeasEngine     *engine,
 }
 
 static void
+test_extension_multiple_threads_global_loaders (PeasEngine     *engine,
+                                                PeasPluginInfo *info)
+{
+  test_extension_multiple_threads (engine, info, FALSE);
+}
+
+static void
+test_extension_multiple_threads_nonglobal_loaders (PeasEngine     *engine,
+                                                   PeasPluginInfo *info)
+{
+  test_extension_multiple_threads (engine, info, TRUE);
+}
+
+static void
 test_extension_call_no_args (PeasEngine     *engine,
                              PeasPluginInfo *info)
 {
@@ -489,7 +507,13 @@ testing_extension_basic (const gchar *loader_)
 
   /* See peas_engine_enable_loader() */
   if (g_strcmp0 (loader, "lua5.1") != 0)
-    _EXTENSION_TEST (loader, "multiple-threads", multiple_threads);
+    {
+      _EXTENSION_TEST (loader, "multiple-threads/global-loaders",
+                       multiple_threads_global_loaders);
+    }
+
+  _EXTENSION_TEST (loader, "multiple-threads/nonglobal-loaders",
+                   multiple_threads_nonglobal_loaders);
 }
 
 void
diff --git a/tests/libpeas/testing/testing.c b/tests/libpeas/testing/testing.c
index aeb387e..741e313 100644
--- a/tests/libpeas/testing/testing.c
+++ b/tests/libpeas/testing/testing.c
@@ -55,7 +55,7 @@ testing_init (gint    *argc,
 }
 
 PeasEngine *
-testing_engine_new (void)
+testing_engine_new_full (gboolean nonglobal_loaders)
 {
   PeasEngine *engine;
 
@@ -75,7 +75,7 @@ testing_engine_new (void)
   testing_util_push_log_hook ("*Error loading *unkown-loader.plugin*");
 
   /* Must be after pushing log hooks */
-  engine = testing_util_engine_new ();
+  engine = testing_util_engine_new_full (nonglobal_loaders);
   peas_engine_add_search_path (engine, BUILDDIR "/tests/libpeas/plugins",
                                        SRCDIR   "/tests/libpeas/plugins");
 
diff --git a/tests/libpeas/testing/testing.h b/tests/libpeas/testing/testing.h
index f638268..23a8932 100644
--- a/tests/libpeas/testing/testing.h
+++ b/tests/libpeas/testing/testing.h
@@ -27,10 +27,12 @@
 
 G_BEGIN_DECLS
 
-void        testing_init        (gint    *argc,
-                                 gchar ***argv);
+void        testing_init             (gint    *argc,
+                                      gchar ***argv);
 
-PeasEngine *testing_engine_new  (void);
+PeasEngine *testing_engine_new_full  (gboolean nonglobal_loaders);
+
+#define testing_engine_new() (testing_engine_new_full (FALSE))
 
 /* libtesting-util functions which do not need to be overridden */
 #define testing_engine_free testing_util_engine_free
diff --git a/tests/testing-util/testing-util.c b/tests/testing-util/testing-util.c
index bd0f839..3b98fe9 100644
--- a/tests/testing-util/testing-util.c
+++ b/tests/testing-util/testing-util.c
@@ -203,7 +203,7 @@ engine_weak_notify (gpointer    unused,
 }
 
 PeasEngine *
-testing_util_engine_new (void)
+testing_util_engine_new_full (gboolean nonglobal_loaders)
 {
   PeasEngine *engine;
 
@@ -215,7 +215,11 @@ testing_util_engine_new (void)
   g_assert (g_private_get (&engine_key) == NULL);
 
   /* Must be after requiring typelibs */
-  engine = peas_engine_new ();
+  if (!nonglobal_loaders)
+    engine = peas_engine_new ();
+  else
+    engine = peas_engine_new_with_nonglobal_loaders ();
+
   g_private_set (&engine_key, engine);
 
   g_object_weak_ref (G_OBJECT (engine),
diff --git a/tests/testing-util/testing-util.h b/tests/testing-util/testing-util.h
index 3832794..726120f 100644
--- a/tests/testing-util/testing-util.h
+++ b/tests/testing-util/testing-util.h
@@ -26,16 +26,19 @@
 
 G_BEGIN_DECLS
 
-void        testing_util_init          (void);
+void        testing_util_init            (void);
 
-PeasEngine *testing_util_engine_new    (void);
-void        testing_util_engine_free   (PeasEngine *engine);
+PeasEngine *testing_util_engine_new_full (gboolean    nonglobal_loaders);
+void        testing_util_engine_free     (PeasEngine *engine);
 
-int         testing_util_run_tests     (void);
+int         testing_util_run_tests       (void);
 
-void        testing_util_push_log_hook (const gchar *pattern);
-void        testing_util_pop_log_hook  (void);
-void        testing_util_pop_log_hooks (void);
+void        testing_util_push_log_hook   (const gchar *pattern);
+void        testing_util_pop_log_hook    (void);
+void        testing_util_pop_log_hooks   (void);
+
+
+#define testing_util_engine_new() (testing_util_engine_new_full (FALSE))
 
 G_END_DECLS
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]