[libpeas] Add an extension test that uses multiple threads



commit 9a2b7a92741fd88fb059882c84eca9381246d554
Author: Garrett Regier <garrett regier riftio com>
Date:   Sat Nov 8 12:31:58 2014 -0800

    Add an extension test that uses multiple threads
    
    This should act as a good stress test for checking that
    libpeas is thread-safe. This also changes testing-utils to
    be thread aware.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=739619

 configure.ac                              |    4 +-
 tests/libpeas/testing/testing-extension.c |   62 +++++++++++++++++++
 tests/testing-util/testing-util.c         |   92 +++++++++++++++++++++-------
 3 files changed, 133 insertions(+), 25 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1786f09..298d4eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -84,7 +84,7 @@ dnl ================================================================
 
 PKG_PROG_PKG_CONFIG
 
-GLIB_REQUIRED=2.30.0
+GLIB_REQUIRED=2.36.0
 GIO_REQUIRED=2.32.0
 INTROSPECTION_REQUIRED=1.39.0
 
@@ -102,7 +102,7 @@ AC_SUBST(GLIB_REQUIRED)
 AC_SUBST(GIO_REQUIRED)
 AC_SUBST(INTROSPECTION_REQUIRED)
 
-AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_32, [minimum glib version])
+AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_36, [minimum glib version])
 AC_DEFINE(GLIB_VERSION_MAX_ALLOWED, GLIB_VERSION_2_38, [maximum glib version])
 
 dnl ================================================================
diff --git a/tests/libpeas/testing/testing-extension.c b/tests/libpeas/testing/testing-extension.c
index 2fd5dda..97e7dfc 100644
--- a/tests/libpeas/testing/testing-extension.c
+++ b/tests/libpeas/testing/testing-extension.c
@@ -270,6 +270,64 @@ test_extension_get_settings (PeasEngine     *engine,
 }
 
 static void
+multiple_threads_in_thread (guint nth_thread)
+{
+  gint i, j;
+  PeasEngine *engine;
+  PeasPluginInfo *info;
+  GObject *extension;
+  const gboolean is_slow = strstr (loader, "python") != NULL;
+
+  engine = testing_engine_new ();
+  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)
+    {
+      g_assert (peas_engine_load_plugin (engine, info));
+
+      for (j = 0; j < 5; ++j)
+        {
+          extension = peas_engine_create_extension (engine, info,
+                                                    INTROSPECTION_TYPE_BASE,
+                                                    NULL);
+
+          g_assert (extension != NULL);
+          g_object_unref (extension);
+        }
+
+      g_assert (peas_engine_unload_plugin (engine, info));
+    }
+
+  testing_engine_free (engine);
+}
+
+static void
+test_extension_multiple_threads (PeasEngine     *engine,
+                                 PeasPluginInfo *info)
+{
+  gint i;
+  GThreadPool *pool;
+  GError *error = NULL;
+  const gboolean is_slow = strstr (loader, "python") != NULL;
+
+  pool = g_thread_pool_new ((GFunc) multiple_threads_in_thread,
+                            NULL, g_get_num_processors (), TRUE, &error);
+  g_assert_no_error (error);
+
+  for (i = 0; i < (is_slow ? 20 : 100); ++i)
+    {
+      /* Cannot supply NULL as the data... */
+      g_thread_pool_push (pool, GUINT_TO_POINTER (i + 1), &error);
+      g_assert_no_error (error);
+    }
+
+  g_thread_pool_free (pool, FALSE, TRUE);
+}
+
+static void
 test_extension_call_no_args (PeasEngine     *engine,
                              PeasPluginInfo *info)
 {
@@ -428,6 +486,10 @@ testing_extension_basic (const gchar *loader_)
 
   _EXTENSION_TEST (loader, "plugin-info", plugin_info);
   _EXTENSION_TEST (loader, "get-settings", get_settings);
+
+  /* See peas_engine_enable_loader() */
+  if (g_strcmp0 (loader, "lua5.1") != 0)
+    _EXTENSION_TEST (loader, "multiple-threads", multiple_threads);
 }
 
 void
diff --git a/tests/testing-util/testing-util.c b/tests/testing-util/testing-util.c
index e67b502..bd0f839 100644
--- a/tests/testing-util/testing-util.c
+++ b/tests/testing-util/testing-util.c
@@ -37,10 +37,17 @@ typedef struct {
   gboolean hit;
 } LogHook;
 
-static PeasEngine *engine = NULL;
-static GPtrArray *log_hooks = NULL;
+static void engine_private_notify (gpointer value);
+static void log_hooks_private_notify (gpointer value);
+
 static gboolean initialized = FALSE;
 
+static gpointer dead_engine = NULL;
+#define DEAD_ENGINE ((gpointer) &dead_engine)
+
+static GPrivate engine_key = G_PRIVATE_INIT (engine_private_notify);
+static GPrivate log_hooks_key = G_PRIVATE_INIT (log_hooks_private_notify);
+
 /* These are warnings and criticals that just have to happen
  * for testing purposes and as such we don't want to abort on them.
  *
@@ -55,11 +62,47 @@ static const gchar *allowed_patterns[] = {
 };
 
 static void
+engine_private_notify (gpointer value)
+{
+  if (value != NULL)
+    g_error ("A PeasEngine was not freed!");
+}
+
+static void
+log_hooks_private_notify (gpointer value)
+{
+  GPtrArray *log_hooks = value;
+
+  if (log_hooks != NULL)
+    {
+      g_assert_cmpuint (log_hooks->len, ==, 0);
+      g_ptr_array_unref (log_hooks);
+    }
+}
+
+static GPtrArray *
+get_log_hooks (void)
+{
+  GPtrArray *log_hooks = g_private_get (&log_hooks_key);
+
+  if (log_hooks != NULL)
+    return log_hooks;
+
+  g_assert (initialized);
+
+  log_hooks = g_ptr_array_new_with_free_func (g_free);
+  g_private_set (&log_hooks_key, log_hooks);
+
+  return log_hooks;
+}
+
+static void
 log_handler (const gchar    *log_domain,
              GLogLevelFlags  log_level,
              const gchar    *message,
              gpointer        user_data)
 {
+  GPtrArray *log_hooks = get_log_hooks ();
   guint i;
 
   /* We always want to log debug, info and message logs */
@@ -148,25 +191,36 @@ testing_util_init (void)
   g_irepository_require (g_irepository_get_default (), "Peas", "1.0", 0, &error);
   g_assert_no_error (error);
 
-  log_hooks = g_ptr_array_new_with_free_func (g_free);
-
   initialized = TRUE;
 }
 
+static void
+engine_weak_notify (gpointer    unused,
+                    PeasEngine *engine)
+{
+  /* Cannot use NULL because testing_util_engine_free() must be called */
+  g_private_set (&engine_key, DEAD_ENGINE);
+}
+
 PeasEngine *
 testing_util_engine_new (void)
 {
+  PeasEngine *engine;
+
   g_assert (initialized);
 
   /* testing_util_engine_free() checks that the
    * engine is freed so only one engine can be created
    */
-  g_assert (engine == NULL);
+  g_assert (g_private_get (&engine_key) == NULL);
 
   /* Must be after requiring typelibs */
   engine = peas_engine_new ();
+  g_private_set (&engine_key, engine);
 
-  g_object_add_weak_pointer (G_OBJECT (engine), (gpointer *) &engine);
+  g_object_weak_ref (G_OBJECT (engine),
+                     (GWeakNotify) engine_weak_notify,
+                     NULL);
 
   peas_engine_add_search_path (engine, BUILDDIR "/tests/plugins",
                                        SRCDIR   "/tests/plugins");
@@ -175,17 +229,19 @@ testing_util_engine_new (void)
 }
 
 void
-testing_util_engine_free (PeasEngine *engine_)
+testing_util_engine_free (PeasEngine *engine)
 {
   /* In case a test needs to free the engine */
-  if (engine != NULL)
+  if (g_private_get (&engine_key) != DEAD_ENGINE)
     {
-      g_object_unref (engine_);
+      g_object_unref (engine);
 
       /* Make sure that at the end of every test the engine is freed */
-      g_assert (engine == NULL);
+      g_assert (g_private_get (&engine_key) == DEAD_ENGINE);
     }
 
+  g_private_set (&engine_key, NULL);
+
   /* Pop the log hooks so the test cases don't have to */
   testing_util_pop_log_hooks ();
 }
@@ -199,15 +255,6 @@ testing_util_run_tests (void)
 
   retval = g_test_run ();
 
-  /* Make sure all the engines have been freed */
-  g_assert (engine == NULL);
-
-  if (log_hooks != NULL)
-    {
-      g_assert_cmpuint (log_hooks->len, ==, 0);
-      g_ptr_array_unref (log_hooks);
-    }
-
   /* Cannot call this with atexit() because
    * gcov does not register that it was called.
    */
@@ -219,9 +266,9 @@ testing_util_run_tests (void)
 void
 testing_util_push_log_hook (const gchar *pattern)
 {
+  GPtrArray *log_hooks = get_log_hooks ();
   LogHook *hook;
 
-  g_return_if_fail (log_hooks != NULL);
   g_return_if_fail (pattern != NULL && *pattern != '\0');
 
   hook = g_new (LogHook, 1);
@@ -235,9 +282,9 @@ testing_util_push_log_hook (const gchar *pattern)
 void
 testing_util_pop_log_hook (void)
 {
+  GPtrArray *log_hooks = get_log_hooks ();
   LogHook *hook;
 
-  g_return_if_fail (log_hooks != NULL);
   g_return_if_fail (log_hooks->len > 0);
 
   hook = g_ptr_array_index (log_hooks, log_hooks->len - 1);
@@ -251,12 +298,11 @@ testing_util_pop_log_hook (void)
 void
 testing_util_pop_log_hooks (void)
 {
+  GPtrArray *log_hooks = get_log_hooks ();
   guint i;
   LogHook *hook;
   GPtrArray *unhit_hooks;
 
-  g_return_if_fail (log_hooks != NULL);
-
   if (log_hooks->len == 0)
     return;
 


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