From 726eca7c898ade4c69ca03220c1a2d69b1842074 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 11 Sep 2023 23:20:23 -0700 Subject: [PATCH] gthread: introduce g_once_init_{enter,leave}_pointer These functions can be used to initalize pointer-type variables rather than a gsize. This is required to support CHERI-enabled platforms where gsize cannot be used to store pointers. Follow-up changes will migrate the uses of g_once_init that store pointers to the new API Helps: https://gitlab.gnome.org/GNOME/glib/-/issues/2842 --- docs/reference/glib/glib-sections.txt.in | 2 + glib/gthread.c | 87 +++++++++++++++++++++++- glib/gthread.h | 25 +++++++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index 5258544a3..6373a13f2 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -782,6 +782,8 @@ G_ONCE_INIT g_once g_once_init_enter g_once_init_leave +g_once_init_enter_pointer +g_once_init_leave_pointer g_bit_lock diff --git a/glib/gthread.c b/glib/gthread.c index eed759590..eebc6a5dd 100644 --- a/glib/gthread.c +++ b/glib/gthread.c @@ -87,7 +87,8 @@ * for condition variables to allow synchronization of threads (#GCond). * There are primitives for thread-private data - data that every * thread has a private instance of (#GPrivate). There are facilities - * for one-time initialization (#GOnce, g_once_init_enter()). Finally, + * for one-time initialization (#GOnce, g_once_init_enter_pointer(), + * g_once_init_enter()). Finally, * there are primitives to create and manage threads (#GThread). * * The GLib threading system used to be initialized with g_thread_init(). @@ -719,6 +720,54 @@ gboolean return need_init; } +/** + * g_once_init_enter_pointer: + * @location: (not nullable): location of a static initializable variable + * containing `NULL` + * + * This functions behaves in the same way as g_once_init_enter(), but can + * can be used to initialize pointers (or #guintptr) instead of #gsize. + * + * |[ + * static MyStruct *interesting_struct = NULL; + * + * if (g_once_init_enter_pointer (&interesting_struct)) + * { + * MyStruct *setup_value = allocate_my_struct (); // initialization code here + * + * g_once_init_leave_pointer (&interesting_struct, g_steal_pointer (&setup_value)); + * } + * + * // use interesting_struct here + * ]| + * + * Returns: %TRUE if the initialization section should be entered, + * %FALSE and blocks otherwise + * + * Since: 2.80 + */ +gboolean +(g_once_init_enter_pointer) (gpointer location) +{ + gpointer *value_location = (gpointer *) location; + gboolean need_init = FALSE; + g_mutex_lock (&g_once_mutex); + if (g_atomic_pointer_get (value_location) == 0) + { + if (!g_slist_find (g_once_init_list, (void *) value_location)) + { + need_init = TRUE; + g_once_init_list = g_slist_prepend (g_once_init_list, (void *) value_location); + } + else + do + g_cond_wait (&g_once_cond, &g_once_mutex); + while (g_slist_find (g_once_init_list, (void *) value_location)); + } + g_mutex_unlock (&g_once_mutex); + return need_init; +} + /** * g_once_init_leave: * @location: (not nullable): location of a static initializable variable @@ -755,6 +804,42 @@ void g_mutex_unlock (&g_once_mutex); } +/** + * g_once_init_leave_pointer: + * @location: (not nullable): location of a static initializable variable + * containing `NULL` + * @result: new non-`NULL` value for `*location` + * + * Counterpart to g_once_init_enter_pointer(). Expects a location of a static + * `NULL`-initialized initialization variable, and an initialization value + * other than `NULL`. Sets the variable to the initialization value, and + * releases concurrent threads blocking in g_once_init_enter_pointer() on this + * initialization variable. + * + * This functions behaves in the same way as g_once_init_leave(), but + * can be used to initialize pointers (or #guintptr) instead of #gsize. + * + * Since: 2.80 + */ +void +(g_once_init_leave_pointer) (gpointer location, + gpointer result) +{ + gpointer *value_location = (gpointer *) location; + gpointer old_value; + + g_return_if_fail (result != 0); + + old_value = g_atomic_pointer_exchange (value_location, result); + g_return_if_fail (old_value == 0); + + g_mutex_lock (&g_once_mutex); + g_return_if_fail (g_once_init_list != NULL); + g_once_init_list = g_slist_remove (g_once_init_list, (void *) value_location); + g_cond_broadcast (&g_once_cond); + g_mutex_unlock (&g_once_mutex); +} + /* GThread {{{1 -------------------------------------------------------- */ /** diff --git a/glib/gthread.h b/glib/gthread.h index 14bb5a07d..1b5a89a68 100644 --- a/glib/gthread.h +++ b/glib/gthread.h @@ -236,6 +236,12 @@ GLIB_AVAILABLE_IN_ALL void g_once_init_leave (volatile void *location, gsize result); +GLIB_AVAILABLE_IN_2_80 +gboolean g_once_init_enter_pointer (void *location); +GLIB_AVAILABLE_IN_2_80 +void g_once_init_leave_pointer (void *location, + gpointer result); + /* Use C11-style atomic extensions to check the fast path for status=ready. If * they are not available, fall back to using a mutex and condition variable in * g_once_impl(). @@ -268,11 +274,30 @@ void g_once_init_leave (volatile void *location, 0 ? (void) (*(location) = (result)) : (void) 0; \ g_once_init_leave ((location), (gsize) (result)); \ })) +# define g_once_init_enter_pointer(location) \ + (G_GNUC_EXTENSION ({ \ + G_STATIC_ASSERT (sizeof *(location) == sizeof (gpointer)); \ + (void) (0 ? (gpointer) * (location) : NULL); \ + (!g_atomic_pointer_get (location) && \ + g_once_init_enter_pointer (location)); \ + })) GLIB_AVAILABLE_MACRO_IN_2_80 +# define g_once_init_leave_pointer(location, result) \ + (G_GNUC_EXTENSION ({ \ + G_STATIC_ASSERT (sizeof *(location) == sizeof (gpointer)); \ + 0 ? (void) (*(location) = (result)) : (void) 0; \ + g_once_init_leave_pointer ((location), (gpointer) (guintptr) (result)); \ + })) GLIB_AVAILABLE_MACRO_IN_2_80 #else # define g_once_init_enter(location) \ (g_once_init_enter((location))) # define g_once_init_leave(location, result) \ (g_once_init_leave((location), (gsize) (result))) +# define g_once_init_enter_pointer(location) \ + (g_once_init_enter_pointer((location))) \ + GLIB_AVAILABLE_MACRO_IN_2_80 +# define g_once_init_leave_pointer(location, result) \ + (g_once_init_leave_pointer((location), (gpointer) (guintptr) (result))) \ + GLIB_AVAILABLE_MACRO_IN_2_80 #endif GLIB_AVAILABLE_IN_2_36