mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-09-27 17:52:58 +02:00
Make refcounting threadsafe by using atomic operations. (#166020, Wim
2005-07-15 Matthias Clasen <mclasen@redhat.com> Make refcounting threadsafe by using atomic operations. (#166020, Wim Taymans) * gobject.c: Use a recursive lock to protect the notify queue. (g_object_unref): Get rid of g_object_last_unref and do the last unref handling in g_object_unref. (g_object_ref, g_object_unref): Use atomic operations. * gsignal.c (struct _HandlerMatch): Use a full integer for the ref_count field. (handler_ref, handler_unref_R): Use atomic operations. * gparam.c (g_param_spec_ref, g_param_spec_unref): Use atomic operations instead of a lock to make the refcounting threadsafe. * gclosure.c (g_closure_ref, g_closure_unref): Use atomic operations. This is more complicated here, since the refcount is stored in a bitfield, so we also have to access all other bitfield members atomically. * gsignal.c (handlers_find): Read the meta_marshal flag of the closure atomically. * tests/Makefile.am (SUBDIRS): Add tests/refcount * configure.in: Add tests/refcount * tests/refcount/properties.c: Test property changes from multiple threads. * tests/refcount/signals.c: Test signal emission from multiple threads. * tests/refcount/objects.c: Test refcounting from multiple threads. * tests/refcount/objects2.c: * tests/refcount/properties2.c: Tests to measure the overhead of threadsafe refcounting. * glib/giochannel.c (g_io_channel_ref, g_io_channel_unref): Use atomic operations to make refcounting threadsafe. (#166020, Wim Taymans)
This commit is contained in:
committed by
Matthias Clasen
parent
58729b464b
commit
39ea11ce6b
@@ -222,8 +222,7 @@ struct _Handler
|
||||
Handler *next;
|
||||
Handler *prev;
|
||||
GQuark detail;
|
||||
guint ref_count : 16;
|
||||
#define HANDLER_MAX_REF_COUNT (1 << 16)
|
||||
guint ref_count; /* ABI change, was 16 bits but since it's internal... */
|
||||
guint block_count : 12;
|
||||
#define HANDLER_MAX_BLOCK_COUNT (1 << 12)
|
||||
guint after : 1;
|
||||
@@ -455,6 +454,17 @@ handler_match_free1_R (HandlerMatch *node,
|
||||
return next;
|
||||
}
|
||||
|
||||
/* copy of gclosure.c code here to make the first 32 bits of the closure
|
||||
* atomic. */
|
||||
typedef union
|
||||
{
|
||||
GClosure bits;
|
||||
gint atomic;
|
||||
} GAtomicClosureBits;
|
||||
|
||||
#define BITS_AS_INT(b) (((GAtomicClosureBits*)(b))->atomic)
|
||||
#define CLOSURE_READ_BITS(cl,bits) (BITS_AS_INT(bits) = g_atomic_int_get ((gint*)(cl)))
|
||||
|
||||
static HandlerMatch*
|
||||
handlers_find (gpointer instance,
|
||||
GSignalMatchType mask,
|
||||
@@ -481,20 +491,26 @@ handlers_find (gpointer instance,
|
||||
}
|
||||
|
||||
mask = ~mask;
|
||||
for (handler = hlist ? hlist->handlers : NULL; handler; handler = handler->next)
|
||||
if (handler->sequential_number &&
|
||||
((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
|
||||
((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
|
||||
((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
|
||||
((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
|
||||
((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
|
||||
handler->closure->meta_marshal == 0 &&
|
||||
((GCClosure*) handler->closure)->callback == func)))
|
||||
{
|
||||
mlist = handler_match_prepend (mlist, handler, signal_id);
|
||||
if (one_and_only)
|
||||
return mlist;
|
||||
}
|
||||
for (handler = hlist ? hlist->handlers : NULL; handler; handler = handler->next)
|
||||
{
|
||||
GClosure bits;
|
||||
|
||||
CLOSURE_READ_BITS (handler->closure, &bits);
|
||||
|
||||
if (handler->sequential_number &&
|
||||
((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
|
||||
((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
|
||||
((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
|
||||
((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
|
||||
((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
|
||||
bits.meta_marshal == 0 &&
|
||||
((GCClosure*) handler->closure)->callback == func)))
|
||||
{
|
||||
mlist = handler_match_prepend (mlist, handler, signal_id);
|
||||
if (one_and_only)
|
||||
return mlist;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -519,19 +535,25 @@ handlers_find (gpointer instance,
|
||||
}
|
||||
|
||||
for (handler = hlist->handlers; handler; handler = handler->next)
|
||||
if (handler->sequential_number &&
|
||||
((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
|
||||
((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
|
||||
((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
|
||||
((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
|
||||
((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
|
||||
handler->closure->meta_marshal == 0 &&
|
||||
((GCClosure*) handler->closure)->callback == func)))
|
||||
{
|
||||
mlist = handler_match_prepend (mlist, handler, hlist->signal_id);
|
||||
if (one_and_only)
|
||||
return mlist;
|
||||
}
|
||||
{
|
||||
GClosure bits;
|
||||
|
||||
CLOSURE_READ_BITS (handler->closure, &bits);
|
||||
|
||||
if (handler->sequential_number &&
|
||||
((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
|
||||
((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
|
||||
((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
|
||||
((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
|
||||
((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
|
||||
bits.meta_marshal == 0 &&
|
||||
((GCClosure*) handler->closure)->callback == func)))
|
||||
{
|
||||
mlist = handler_match_prepend (mlist, handler, hlist->signal_id);
|
||||
if (one_and_only)
|
||||
return mlist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -567,12 +589,7 @@ handler_ref (Handler *handler)
|
||||
{
|
||||
g_return_if_fail (handler->ref_count > 0);
|
||||
|
||||
#ifndef G_DISABLE_CHECKS
|
||||
if (handler->ref_count >= HANDLER_MAX_REF_COUNT - 1)
|
||||
g_error (G_STRLOC ": handler ref_count overflow, %s", REPORT_BUG);
|
||||
#endif
|
||||
|
||||
handler->ref_count += 1;
|
||||
g_atomic_int_inc (&handler->ref_count);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -580,10 +597,13 @@ handler_unref_R (guint signal_id,
|
||||
gpointer instance,
|
||||
Handler *handler)
|
||||
{
|
||||
gboolean is_zero;
|
||||
|
||||
g_return_if_fail (handler->ref_count > 0);
|
||||
|
||||
handler->ref_count -= 1;
|
||||
if (!handler->ref_count)
|
||||
is_zero = g_atomic_int_dec_and_test (&handler->ref_count);
|
||||
|
||||
if (G_UNLIKELY (is_zero))
|
||||
{
|
||||
HandlerList *hlist = NULL;
|
||||
|
||||
|
Reference in New Issue
Block a user