From 39ea11ce6b107bf3969a2f94807675b458a5a887 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 15 Jul 2005 16:51:10 +0000 Subject: Make refcounting threadsafe by using atomic operations. (#166020, Wim 2005-07-15 Matthias Clasen 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) --- gobject/gsignal.c | 94 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 37 deletions(-) (limited to 'gobject/gsignal.c') diff --git a/gobject/gsignal.c b/gobject/gsignal.c index 2ecb3fc07..b834fd8d8 100644 --- a/gobject/gsignal.c +++ b/gobject/gsignal.c @@ -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; -- cgit v1.2.3-70-g09d2