gobject: add needs_notify flag per object for efficient lookup

The class of a GObject cannot change during its lifetime. And also the
the class itself not change (e.g. change the virtual methods).
As such, we can cache CLASS_NEEDS_NOTIFY() per object.

This way, our _g_object_has_notify_handler() only needs to compare the
flags, instead of fetching the class and check the vtable.

You might think, CLASS_NEEDS_NOTIFY() is cheaper to check, because it
doesn't need an atomic operation to fetch the class and compare the
vtable (unlike checking the object's optional-flags). You might also
think a constant flag should not be attached to the object's
optional-flags (which otherwise change). However, in all cases where we
care about CLASS_NEEDS_NOTIFY(), we anyway also check for
OPTIONAL_FLAG_HAS_NOTIFY_HANDLER which is right beside in the the
optional-flags. So having the OPTIONAL_FLAG_HAS_NEEDS_NOTIFY flag right
beside has no downside.
This commit is contained in:
Thomas Haller 2025-02-28 10:40:10 +01:00
parent 0d596bab12
commit 490604e6d1

View File

@ -109,6 +109,7 @@ enum {
#define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2) /* Same, specifically for "notify" */
#define OPTIONAL_FLAG_LOCK (1 << 3) /* _OPTIONAL_BIT_LOCK */
#define OPTIONAL_FLAG_EVER_HAD_WEAK_REF (1 << 4) /* whether on the object ever g_weak_ref_set() was called. */
#define OPTIONAL_FLAG_NEEDS_NOTIFY (1 << 5) /* Corresponds to CLASS_NEEDS_NOTIFY(). */
/* We use g_bit_lock(), which only supports one lock per integer.
*
@ -1770,11 +1771,16 @@ _g_object_has_signal_handler (GObject *object)
return (object_get_optional_flags (object) & OPTIONAL_FLAG_HAS_SIGNAL_HANDLER) != 0;
}
static inline gboolean
_g_object_has_notify_handler_for_flags (guint flags)
{
return (flags & (OPTIONAL_FLAG_HAS_NOTIFY_HANDLER | OPTIONAL_FLAG_NEEDS_NOTIFY)) != 0;
}
static inline gboolean
_g_object_has_notify_handler (GObject *object)
{
return CLASS_NEEDS_NOTIFY (G_OBJECT_GET_CLASS (object)) ||
(object_get_optional_flags (object) & OPTIONAL_FLAG_HAS_NOTIFY_HANDLER) != 0;
return _g_object_has_notify_handler_for_flags (object_get_optional_flags (object));
}
void
@ -1782,8 +1788,10 @@ _g_object_set_has_signal_handler (GObject *object,
guint signal_id)
{
guint flags = OPTIONAL_FLAG_HAS_SIGNAL_HANDLER;
if (signal_id == gobject_signals[NOTIFY])
flags |= OPTIONAL_FLAG_HAS_NOTIFY_HANDLER;
object_set_optional_flags (object, flags);
}
@ -1804,6 +1812,7 @@ g_object_init (GObject *object,
GObjectClass *class)
{
guint *p_flags;
gboolean needs_notify = CLASS_NEEDS_NOTIFY (class);
object->ref_count = 1;
object->qdata = NULL;
@ -1812,8 +1821,10 @@ g_object_init (GObject *object,
* without atomic. */
p_flags = object_get_optional_flags_p (object);
*p_flags = OPTIONAL_FLAG_IN_CONSTRUCTION;
if (needs_notify)
*p_flags |= OPTIONAL_FLAG_NEEDS_NOTIFY;
if (CLASS_HAS_PROPS (class) && CLASS_NEEDS_NOTIFY (class))
if (CLASS_HAS_PROPS (class) && needs_notify)
{
/* freeze object's notification queue, g_object_new_internal() preserves pairedness */
g_object_notify_queue_freeze (object, TRUE);
@ -2021,8 +2032,7 @@ g_object_notify_by_spec_internal (GObject *object,
/* get all flags we need with a single atomic read */
object_flags = object_get_optional_flags (object);
needs_notify = ((object_flags & OPTIONAL_FLAG_HAS_NOTIFY_HANDLER) != 0) ||
CLASS_NEEDS_NOTIFY (G_OBJECT_GET_CLASS (object));
needs_notify = _g_object_has_notify_handler_for_flags (object_flags);
if (!needs_notify)
return;