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 + +GMainContextPusher +g_main_context_pusher_new +g_main_context_pusher_free + 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);