1
0
mirror of https://gitlab.gnome.org/GNOME/glib.git synced 2025-04-01 05:13:06 +02:00

gbitlock: add g_pointer_bit_lock_and_get()

Usually, after g_pointer_bit_lock() we want to read the pointer that we
have. In many cases, when we g_pointer_bit_lock() a pointer, we can
access it afterwards without atomic, as nobody is going to modify the
pointer then.

However, gdataset also supports g_datalist_set_flags(), so the pointer
may change at any time and we must always use atomics to read it. For
that reason, g_datalist_lock_and_get() does an atomic read right after
g_pointer_bit_lock().

g_pointer_bit_lock() can easily access the value that it just set. Add
g_pointer_bit_lock_and_get() which can return the value that gets set
afterwards.

Aside from saving the second atomic-get in certain scenarios, the
returned value is also atomically the one that we just set.
This commit is contained in:
Thomas Haller 2024-01-05 14:05:58 +01:00
parent abe4b4e7d8
commit 5609370de9
4 changed files with 90 additions and 55 deletions

@ -412,6 +412,76 @@ pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr
return (gpointer) (x_ptr & ~lock_mask); 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: * g_pointer_bit_lock:
* @address: (not nullable): a pointer to a #gpointer-sized value * @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 void
(g_pointer_bit_lock) (volatile void *address, (g_pointer_bit_lock) (volatile void *address,
gint lock_bit) gint lock_bit)
{ {
void *address_nonvolatile = (void *) address; g_pointer_bit_lock_and_get ((gpointer *) address, (guint) lock_bit, NULL);
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
}
} }
/** /**

@ -44,6 +44,12 @@ void g_bit_unlock (volatile gint *address,
GLIB_AVAILABLE_IN_ALL GLIB_AVAILABLE_IN_ALL
void g_pointer_bit_lock (volatile void *address, void g_pointer_bit_lock (volatile void *address,
gint lock_bit); 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 GLIB_AVAILABLE_IN_ALL
gboolean g_pointer_bit_trylock (volatile void *address, gboolean g_pointer_bit_trylock (volatile void *address,
gint lock_bit); gint lock_bit);
@ -72,6 +78,12 @@ void g_pointer_bit_unlock_and_set (void *address,
g_pointer_bit_lock ((address), (lock_bit)); \ 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) \ #define g_pointer_bit_trylock(address, lock_bit) \
(G_GNUC_EXTENSION ({ \ (G_GNUC_EXTENSION ({ \
G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \ G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \

@ -41,6 +41,7 @@
defining our own version of the g_bit_*lock symbols defining our own version of the g_bit_*lock symbols
*/ */
#undef g_pointer_bit_lock #undef g_pointer_bit_lock
#undef g_pointer_bit_lock_and_get
#undef g_pointer_bit_trylock #undef g_pointer_bit_trylock
#undef g_pointer_bit_unlock #undef g_pointer_bit_unlock
#undef g_pointer_bit_unlock_and_set #undef g_pointer_bit_unlock_and_set
@ -49,6 +50,7 @@
#define g_bit_trylock _emufutex_g_bit_trylock #define g_bit_trylock _emufutex_g_bit_trylock
#define g_bit_unlock _emufutex_g_bit_unlock #define g_bit_unlock _emufutex_g_bit_unlock
#define g_pointer_bit_lock _emufutex_g_pointer_bit_lock #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_trylock _emufutex_g_pointer_bit_trylock
#define g_pointer_bit_unlock _emufutex_g_pointer_bit_unlock #define g_pointer_bit_unlock _emufutex_g_pointer_bit_unlock
#define g_pointer_bit_lock_mask_ptr _emufutex_g_pointer_bit_lock_mask_ptr #define g_pointer_bit_lock_mask_ptr _emufutex_g_pointer_bit_lock_mask_ptr

@ -515,6 +515,7 @@ test_threaded_g_pointer_bit_unlock_and_set (void)
GObject *obj; GObject *obj;
gpointer plock; gpointer plock;
gpointer ptr; gpointer ptr;
guintptr ptr2;
gpointer mangled_obj; gpointer mangled_obj;
#if defined(__GNUC__) #if defined(__GNUC__)
@ -548,14 +549,15 @@ test_threaded_g_pointer_bit_unlock_and_set (void)
g_assert_true (plock == obj); g_assert_true (plock == obj);
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_assert_true (plock != obj);
g_atomic_pointer_set (&plock, mangled_obj); g_atomic_pointer_set (&plock, mangled_obj);
g_pointer_bit_unlock_and_set (&plock, 0, obj, 0); g_pointer_bit_unlock_and_set (&plock, 0, obj, 0);
g_assert_true (plock == obj); g_assert_true (plock == obj);
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_assert_true (plock != obj);
g_atomic_pointer_set (&plock, mangled_obj); g_atomic_pointer_set (&plock, mangled_obj);
g_pointer_bit_unlock_and_set (&plock, 0, obj, 0x7); g_pointer_bit_unlock_and_set (&plock, 0, obj, 0x7);