glib: add private _g_bit_lock_init() for fast initialization of locked bitlock

There will be case where we allocate a new structure that has a bitlock
flag. We want to lock on that flag right away (before the pointer is yet
shared to other threads).

We would usually call g_bit_lock(), but as we are inside glib itself, we
*know* that g_bit_lock() will do nothing of relevance except
(atomically) setting the lock bit. Note that we don't need the atomic
setting, as the pointer is not yet shared.

So we can do something cheaper: directly set the bit.

Add _g_bit_lock_init() for that.
This commit is contained in:
Thomas Haller
2025-08-04 10:53:11 +02:00
parent b31f91e813
commit 811b68695d
2 changed files with 69 additions and 0 deletions

View File

@@ -244,6 +244,35 @@ g_bit_lock_and_get (gint *address,
g_assert (lock_bit < 32u);
#endif
/* Beware: _g_bit_lock_init() relies that g_bit_lock() on a newly initialized
* integer (that is not yet shared between threads) is identical to just
* initializing the lock bit without atomic.
*
* In other words:
*
* mystruct = g_new (MyStruct, 1);
* mystruct->flags = 0;
* g_bit_lock (&mystruct->flags, 13);
* return mystruct; // share pointer.
*
* is effectively the same as:
*
* mystruct = g_new (MyStruct, 1);
* mystruct->flags = 0;
* mystruct->flags |= (1 << 13);
* return mystruct; // share pointer.
*
* and the same as:
*
* mystruct = g_new (MyStruct, 1);
* mystruct->flags = 0;
* _g_bit_lock_init (&mystruct->flags, 13);
* return mystruct; // share pointer.
*
* This requires that g_bit_lock() does nothing special in the uncontended
* case (except atomically setting the lock bit).
*/
#ifdef USE_ASM_GOTO
if (G_LIKELY (!out_val))
{

View File

@@ -58,6 +58,46 @@ G_BEGIN_DECLS
} \
G_STMT_END
/*< private >
* _g_bit_lock_init:
* @address: (type gpointer): a pointer to an integer
* @lock_bit: a bit value between 0 and 31
*
* g_bit_lock() in the uncontended case merely atomically sets the bit. Most
* importantly, it does nothing else (of relevance). When we initialize an
* object that is not shared between threads, we don't need this overhead
* and we can set the bit directly.
*
* This is required to have the same effect as g_bit_lock(address, lock_bit),
* as long as we are initializing and the address is not yet accessible to
* other threads.
*/
G_ALWAYS_INLINE static inline void
_g_bit_lock_init (gint *address, gint lock_bit)
{
*address |= (1 << lock_bit);
}
/*< private >
* _g_bit_lock_is_locked:
* @address: (type gpointer): a pointer to an integer
* @lock_bit: a bit value between 0 and 31
*
* Atomically checks whether the lock bit is currently set.
*
* This is only useful in special cases, because usually knowing
* in a multi threaded context whether the lock bit is set is
* irrelevant. Because you cannot know whether the bit gets locked
* right after the check. But there are some uses...
*
* Returns: whether the lock bit is set.
*/
G_ALWAYS_INLINE static inline gboolean
_g_bit_lock_is_locked (gint *address, gint lock_bit)
{
return !!(g_atomic_int_get (address) & (1 << lock_bit));
}
/*< private >
* GDataListUpdateAtomicFunc:
* @data: (inout) (nullable) (not optional): the existing data corresponding to