diff --git a/glib/gbitlock.c b/glib/gbitlock.c index fff2f33da..c2d2592e8 100644 --- a/glib/gbitlock.c +++ b/glib/gbitlock.c @@ -412,6 +412,76 @@ pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr return (gpointer) (x_ptr & ~lock_mask); } +/** + * g_pointer_bit_lock_and_get: + * @address: (not nullable): a pointer to a #gpointer-sized value + * @lock_bit: a bit value between 0 and 31 + * @out_ptr: (out) (optional): returns the set pointer atomically. + * This is the value after setting the lock, it thus always has the + * lock bit set, while previously @address had the lockbit unset. + * You may also use g_pointer_bit_lock_mask_ptr() to clear the lock bit. + * + * This is equivalent to g_bit_lock, but working on pointers (or other + * pointer-sized values). + * + * For portability reasons, you may only lock on the bottom 32 bits of + * the pointer. + * + * Since: 2.80 + **/ +void +(g_pointer_bit_lock_and_get) (gpointer address, + guint lock_bit, + guintptr *out_ptr) +{ + guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended); + guintptr mask; + guintptr v; + + g_return_if_fail (lock_bit < 32); + + mask = 1u << lock_bit; + +#ifdef USE_ASM_GOTO + if (G_LIKELY (!out_ptr)) + { + while (TRUE) + { + __asm__ volatile goto ("lock bts %1, (%0)\n" + "jc %l[contended]" + : /* no output */ + : "r"(address), "r"((gsize) lock_bit) + : "cc", "memory" + : contended); + return; + + contended: + v = (guintptr) g_atomic_pointer_get ((gpointer *) address); + if (v & mask) + { + g_atomic_int_add (&g_bit_lock_contended[class], +1); + g_futex_wait (g_futex_int_address (address), v); + g_atomic_int_add (&g_bit_lock_contended[class], -1); + } + } + } +#endif + +retry: + v = g_atomic_pointer_or ((gpointer *) address, mask); + if (v & mask) + /* already locked */ + { + g_atomic_int_add (&g_bit_lock_contended[class], +1); + g_futex_wait (g_futex_int_address (address), (guint) v); + g_atomic_int_add (&g_bit_lock_contended[class], -1); + goto retry; + } + + if (out_ptr) + *out_ptr = (v | mask); +} + /** * g_pointer_bit_lock: * @address: (not nullable): a pointer to a #gpointer-sized value @@ -430,60 +500,9 @@ pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr **/ void (g_pointer_bit_lock) (volatile void *address, - gint lock_bit) + gint lock_bit) { - void *address_nonvolatile = (void *) address; - - g_return_if_fail (lock_bit < 32); - - { -#ifdef USE_ASM_GOTO - retry: - __asm__ volatile goto ("lock bts %1, (%0)\n" - "jc %l[contended]" - : /* no output */ - : "r" (address), "r" ((gsize) lock_bit) - : "cc", "memory" - : contended); - return; - - contended: - { - gpointer *pointer_address = address_nonvolatile; - gsize mask = 1u << lock_bit; - gsize v; - - v = (gsize) g_atomic_pointer_get (pointer_address); - if (v & mask) - { - guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended); - - g_atomic_int_add (&g_bit_lock_contended[class], +1); - g_futex_wait (g_futex_int_address (address_nonvolatile), v); - g_atomic_int_add (&g_bit_lock_contended[class], -1); - } - } - goto retry; -#else - gpointer *pointer_address = address_nonvolatile; - gsize mask = 1u << lock_bit; - guintptr v; - - retry: - v = g_atomic_pointer_or (pointer_address, mask); - if (v & mask) - /* already locked */ - { - guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended); - - g_atomic_int_add (&g_bit_lock_contended[class], +1); - g_futex_wait (g_futex_int_address (address_nonvolatile), (guint) v); - g_atomic_int_add (&g_bit_lock_contended[class], -1); - - goto retry; - } -#endif - } + g_pointer_bit_lock_and_get ((gpointer *) address, (guint) lock_bit, NULL); } /** diff --git a/glib/gbitlock.h b/glib/gbitlock.h index b7ea32148..f44a52c37 100644 --- a/glib/gbitlock.h +++ b/glib/gbitlock.h @@ -44,6 +44,12 @@ void g_bit_unlock (volatile gint *address, GLIB_AVAILABLE_IN_ALL void g_pointer_bit_lock (volatile void *address, gint lock_bit); + +GLIB_AVAILABLE_IN_2_80 +void g_pointer_bit_lock_and_get (gpointer address, + guint lock_bit, + guintptr *out_ptr); + GLIB_AVAILABLE_IN_ALL gboolean g_pointer_bit_trylock (volatile void *address, gint lock_bit); @@ -72,6 +78,12 @@ void g_pointer_bit_unlock_and_set (void *address, g_pointer_bit_lock ((address), (lock_bit)); \ })) +#define g_pointer_bit_lock_and_get(address, lock_bit, out_ptr) \ + (G_GNUC_EXTENSION ({ \ + G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \ + g_pointer_bit_lock_and_get ((address), (lock_bit), (out_ptr)); \ + })) + #define g_pointer_bit_trylock(address, lock_bit) \ (G_GNUC_EXTENSION ({ \ G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \ diff --git a/glib/tests/1bit-mutex.c b/glib/tests/1bit-mutex.c index b999ea725..f6a90f0bd 100644 --- a/glib/tests/1bit-mutex.c +++ b/glib/tests/1bit-mutex.c @@ -41,6 +41,7 @@ defining our own version of the g_bit_*lock symbols */ #undef g_pointer_bit_lock + #undef g_pointer_bit_lock_and_get #undef g_pointer_bit_trylock #undef g_pointer_bit_unlock #undef g_pointer_bit_unlock_and_set @@ -49,6 +50,7 @@ #define g_bit_trylock _emufutex_g_bit_trylock #define g_bit_unlock _emufutex_g_bit_unlock #define g_pointer_bit_lock _emufutex_g_pointer_bit_lock + #define g_pointer_bit_lock_and_get _emufutex_g_pointer_bit_lock_and_get #define g_pointer_bit_trylock _emufutex_g_pointer_bit_trylock #define g_pointer_bit_unlock _emufutex_g_pointer_bit_unlock #define g_pointer_bit_lock_mask_ptr _emufutex_g_pointer_bit_lock_mask_ptr diff --git a/gobject/tests/threadtests.c b/gobject/tests/threadtests.c index 8fa625482..ea5d6e3ca 100644 --- a/gobject/tests/threadtests.c +++ b/gobject/tests/threadtests.c @@ -515,6 +515,7 @@ test_threaded_g_pointer_bit_unlock_and_set (void) GObject *obj; gpointer plock; gpointer ptr; + guintptr ptr2; gpointer mangled_obj; #if defined(__GNUC__) @@ -548,14 +549,15 @@ test_threaded_g_pointer_bit_unlock_and_set (void) g_assert_true (plock == obj); plock = obj; - g_pointer_bit_lock (&plock, 0); + g_pointer_bit_lock_and_get (&plock, 0, &ptr2); + g_assert_true ((gpointer) ptr2 == plock); g_assert_true (plock != obj); g_atomic_pointer_set (&plock, mangled_obj); g_pointer_bit_unlock_and_set (&plock, 0, obj, 0); g_assert_true (plock == obj); plock = obj; - g_pointer_bit_lock (&plock, 0); + g_pointer_bit_lock_and_get (&plock, 0, NULL); g_assert_true (plock != obj); g_atomic_pointer_set (&plock, mangled_obj); g_pointer_bit_unlock_and_set (&plock, 0, obj, 0x7);