[glib: 2/3] gmain: Add GMainContextPusher convenience API



commit 21f8f8982020dfd65d8d20a5fd0ace67a6dee856
Author: Philip Withnall <withnall endlessm com>
Date:   Wed Oct 23 11:35:58 2019 +0100

    gmain: Add GMainContextPusher convenience API
    
    This is like `GMutexLocker`, in that if you are able to use
    `g_autoptr()`, it makes popping a `GMainContext` off the thread-default
    main context stack easier when exiting a function.
    
    A few uses of `G_GNUC_{BEGIN,END}_IGNORE_DEPRECATIONS` are needed to
    avoid warnings when building apps against GLib with
    `GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_64`.
    
    Signed-off-by: Philip Withnall <withnall endlessm com>

 docs/reference/glib/glib-sections.txt |  5 +++
 glib/glib-autocleanups.h              |  1 +
 glib/gmain.h                          | 83 +++++++++++++++++++++++++++++++++++
 glib/tests/autoptr.c                  | 25 +++++++++++
 4 files changed, 114 insertions(+)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index eea025c92..e9dfa73e9 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -781,6 +781,11 @@ g_main_set_poll_func
 g_main_context_invoke
 g_main_context_invoke_full
 
+<SUBSECTION>
+GMainContextPusher
+g_main_context_pusher_new
+g_main_context_pusher_free
+
 <SUBSECTION>
 g_main_context_get_thread_default
 g_main_context_ref_thread_default
diff --git a/glib/glib-autocleanups.h b/glib/glib-autocleanups.h
index 91b4be566..b71101f2d 100644
--- a/glib/glib-autocleanups.h
+++ b/glib/glib-autocleanups.h
@@ -58,6 +58,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GArray, g_array_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GByteArray, g_byte_array_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContextPusher, g_main_context_pusher_free)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref)
diff --git a/glib/gmain.h b/glib/gmain.h
index 6325ecbe3..ceb30cbd0 100644
--- a/glib/gmain.h
+++ b/glib/gmain.h
@@ -430,6 +430,89 @@ GMainContext *g_main_context_get_thread_default  (void);
 GLIB_AVAILABLE_IN_ALL
 GMainContext *g_main_context_ref_thread_default  (void);
 
+/**
+ * GMainContextPusher:
+ *
+ * Opaque type. See g_main_context_pusher_new() for details.
+ *
+ * Since: 2.64
+ */
+typedef void GMainContextPusher GLIB_AVAILABLE_TYPE_IN_2_64;
+
+/**
+ * g_main_context_pusher_new:
+ * @main_context: (transfer none): a main context to push
+ *
+ * Push @main_context as the new thread-default main context for the current
+ * thread, using g_main_context_push_thread_default(), and return a new
+ * #GMainContextPusher. Pop with g_main_context_pusher_free(). Using
+ * g_main_context_pop_thread_default() on @main_context while a
+ * #GMainContextPusher exists for it can lead to undefined behaviour.
+ *
+ * Using two #GMainContextPushers in the same scope is not allowed, as it leads
+ * to an undefined pop order.
+ *
+ * This is intended to be used with g_autoptr().  Note that g_autoptr()
+ * is only available when using GCC or clang, so the following example
+ * will only work with those compilers:
+ * |[
+ * typedef struct
+ * {
+ *   ...
+ *   GMainContext *context;
+ *   ...
+ * } MyObject;
+ *
+ * static void
+ * my_object_do_stuff (MyObject *self)
+ * {
+ *   g_autoptr(GMainContextPusher) pusher = g_main_context_pusher_new (self->context);
+ *
+ *   // Code with main context as the thread default here
+ *
+ *   if (cond)
+ *     // No need to pop
+ *     return;
+ *
+ *   // Optionally early pop
+ *   g_clear_pointer (&pusher, g_main_context_pusher_free);
+ *
+ *   // Code with main context no longer the thread default here
+ * }
+ * ]|
+ *
+ * Returns: (transfer full): a #GMainContextPusher
+ * Since: 2.64
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+static inline GMainContextPusher *
+g_main_context_pusher_new (GMainContext *main_context)
+{
+  g_main_context_push_thread_default (main_context);
+  return (GMainContextPusher *) main_context;
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/**
+ * g_main_context_pusher_free:
+ * @pusher: (transfer full): a #GMainContextPusher
+ *
+ * Pop @pusher’s main context as the thread default main context.
+ * See g_main_context_pusher_new() for details.
+ *
+ * This will pop the #GMainContext as the current thread-default main context,
+ * but will not call g_main_context_unref() on it.
+ *
+ * Since: 2.64
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+static inline void
+g_main_context_pusher_free (GMainContextPusher *pusher)
+{
+  g_main_context_pop_thread_default ((GMainContext *) pusher);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
 /* GMainLoop: */
 
 GLIB_AVAILABLE_IN_ALL
diff --git a/glib/tests/autoptr.c b/glib/tests/autoptr.c
index 4eed862af..14b95a9cc 100644
--- a/glib/tests/autoptr.c
+++ b/glib/tests/autoptr.c
@@ -161,6 +161,30 @@ test_g_main_context (void)
   g_assert_nonnull (val);
 }
 
+static void
+test_g_main_context_pusher (void)
+{
+  GMainContext *context, *old_thread_default;
+
+  context = g_main_context_new ();
+  old_thread_default = g_main_context_get_thread_default ();
+  g_assert_false (old_thread_default == context);
+
+  if (TRUE)
+    {
+      g_autoptr(GMainContextPusher) val = g_main_context_pusher_new (context);
+
+      /* Check it’s now the thread-default main context */
+      g_assert_true (g_main_context_get_thread_default () == context);
+    }
+
+  /* Check it’s now the old thread-default main context */
+  g_assert_false (g_main_context_get_thread_default () == context);
+  g_assert_true (g_main_context_get_thread_default () == old_thread_default);
+
+  g_main_context_unref (context);
+}
+
 static void
 test_g_main_loop (void)
 {
@@ -692,6 +716,7 @@ main (int argc, gchar *argv[])
   g_test_add_func ("/autoptr/g_ptr_array", test_g_ptr_array);
   g_test_add_func ("/autoptr/g_byte_array", test_g_byte_array);
   g_test_add_func ("/autoptr/g_main_context", test_g_main_context);
+  g_test_add_func ("/autoptr/g_main_context_pusher", test_g_main_context_pusher);
   g_test_add_func ("/autoptr/g_main_loop", test_g_main_loop);
   g_test_add_func ("/autoptr/g_source", test_g_source);
   g_test_add_func ("/autoptr/g_mapped_file", test_g_mapped_file);


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