diff options
author | Matthias Clasen <mclasen@redhat.com> | 2005-07-15 16:51:10 +0000 |
---|---|---|
committer | Matthias Clasen <matthiasc@src.gnome.org> | 2005-07-15 16:51:10 +0000 |
commit | 39ea11ce6b107bf3969a2f94807675b458a5a887 (patch) | |
tree | b06f87caa8052fd3a656168201eadba77cc133e8 /gobject | |
parent | 58729b464b44595704c4e8e65869fd2228554ed5 (diff) |
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)
Diffstat (limited to 'gobject')
-rw-r--r-- | gobject/ChangeLog | 27 | ||||
-rw-r--r-- | gobject/gclosure.c | 392 | ||||
-rw-r--r-- | gobject/gobject.c | 123 | ||||
-rw-r--r-- | gobject/gparam.c | 62 | ||||
-rw-r--r-- | gobject/gsignal.c | 94 |
5 files changed, 457 insertions, 241 deletions
diff --git a/gobject/ChangeLog b/gobject/ChangeLog index d10a391af..0e242672a 100644 --- a/gobject/ChangeLog +++ b/gobject/ChangeLog @@ -1,3 +1,30 @@ +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. + 2005-07-11 Matthias Clasen <mclasen@redhat.com> * gobject.h: diff --git a/gobject/gclosure.c b/gobject/gclosure.c index 2323384af..04e2d268a 100644 --- a/gobject/gclosure.c +++ b/gobject/gclosure.c @@ -36,6 +36,44 @@ #define CLOSURE_N_NOTIFIERS(cl) (CLOSURE_N_MFUNCS (cl) + \ (cl)->n_fnotifiers + \ (cl)->n_inotifiers) + +/* union of first int we need to make 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))) +#define CLOSURE_READ_BITS2(cl,old,new) (BITS_AS_INT(old) = CLOSURE_READ_BITS (cl, new)) +#define CLOSURE_SWAP_BITS(cl,old,new) (g_atomic_int_compare_and_exchange ((gint*)(cl), \ + BITS_AS_INT(old),BITS_AS_INT(new))) + +#define CLOSURE_REF(closure) \ +G_STMT_START { \ + GClosure old, new; \ + do { \ + CLOSURE_READ_BITS2 (closure, &old, &new); \ + new.ref_count++; \ + } \ + while (!CLOSURE_SWAP_BITS (closure, &old, &new)); \ +} G_STMT_END + + +#define CLOSURE_UNREF(closure, is_zero) \ +G_STMT_START { \ + GClosure old, new; \ + do { \ + CLOSURE_READ_BITS2 (closure, &old, &new); \ + if (old.ref_count == 1) /* last unref, invalidate first */ \ + g_closure_invalidate ((closure)); \ + new.ref_count--; \ + is_zero = (new.ref_count == 0); \ + } \ + while (!CLOSURE_SWAP_BITS (closure, &old, &new)); \ +} G_STMT_END + enum { FNOTIFY, INOTIFY, @@ -76,6 +114,8 @@ static inline void closure_invoke_notifiers (GClosure *closure, guint notify_type) { + GClosure bits, new; + /* notifier layout: * meta_marshal n_guards n_guards n_fnotif. n_inotifiers * ->[[meta_marshal][pre_guards][post_guards][fnotifiers][inotifiers]] @@ -99,11 +139,12 @@ closure_invoke_notifiers (GClosure *closure, GClosureNotifyData *ndata; guint i, offs; case FNOTIFY: - while (closure->n_fnotifiers) + CLOSURE_READ_BITS (closure, &bits); + while (bits.n_fnotifiers) { - register guint n = --closure->n_fnotifiers; + register guint n = --bits.n_fnotifiers; - ndata = closure->notifiers + CLOSURE_N_MFUNCS (closure) + n; + ndata = closure->notifiers + CLOSURE_N_MFUNCS (&bits) + n; closure->marshal = (GClosureMarshal) ndata->notify; closure->data = ndata->data; ndata->notify (ndata->data, closure); @@ -112,23 +153,34 @@ closure_invoke_notifiers (GClosure *closure, closure->data = NULL; break; case INOTIFY: - closure->in_inotify = TRUE; - while (closure->n_inotifiers) + do { + CLOSURE_READ_BITS2 (closure, &bits, &new); + new.in_inotify = TRUE; + } + while (!CLOSURE_SWAP_BITS (closure, &bits, &new)); + + while (bits.n_inotifiers) { - register guint n = --closure->n_inotifiers; + register guint n = --bits.n_inotifiers; - ndata = closure->notifiers + CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers + n; + ndata = closure->notifiers + CLOSURE_N_MFUNCS (&bits) + bits.n_fnotifiers + n; closure->marshal = (GClosureMarshal) ndata->notify; closure->data = ndata->data; ndata->notify (ndata->data, closure); } closure->marshal = NULL; closure->data = NULL; - closure->in_inotify = FALSE; + do { + CLOSURE_READ_BITS2 (closure, &bits, &new); + new.n_inotifiers = 0; + new.in_inotify = FALSE; + } + while (!CLOSURE_SWAP_BITS (closure, &bits, &new)); break; case PRE_NOTIFY: - i = closure->n_guards; - offs = closure->meta_marshal; + CLOSURE_READ_BITS (closure, &bits); + i = bits.n_guards; + offs = bits.meta_marshal; while (i--) { ndata = closure->notifiers + offs + i; @@ -136,8 +188,9 @@ closure_invoke_notifiers (GClosure *closure, } break; case POST_NOTIFY: - i = closure->n_guards; - offs = closure->meta_marshal + i; + CLOSURE_READ_BITS (closure, &bits); + i = bits.n_guards; + offs = bits.meta_marshal + i; while (i--) { ndata = closure->notifiers + offs + i; @@ -152,29 +205,48 @@ g_closure_set_meta_marshal (GClosure *closure, gpointer marshal_data, GClosureMarshal meta_marshal) { - GClosureNotifyData *notifiers; + GClosureNotifyData *old_notifiers, *new_notifiers; guint n; + GClosure old, new; g_return_if_fail (closure != NULL); g_return_if_fail (meta_marshal != NULL); - g_return_if_fail (closure->is_invalid == FALSE); - g_return_if_fail (closure->in_marshal == FALSE); - g_return_if_fail (closure->meta_marshal == 0); - - n = CLOSURE_N_NOTIFIERS (closure); - notifiers = closure->notifiers; - closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1); - if (notifiers) + +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + g_return_if_fail (old.is_invalid == FALSE); + g_return_if_fail (old.in_marshal == FALSE); + g_return_if_fail (old.meta_marshal == 0); + + n = CLOSURE_N_NOTIFIERS (&old); + + old_notifiers = closure->notifiers; + new_notifiers = g_renew (GClosureNotifyData, NULL, n + 1); + if (old_notifiers) { /* usually the meta marshal will be setup right after creation, so the * g_memmove() should be rare-case scenario */ - g_memmove (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0])); - g_free (notifiers); + g_memmove (new_notifiers + 1, old_notifiers, n * sizeof (old_notifiers[0])); } - closure->notifiers[0].data = marshal_data; - closure->notifiers[0].notify = (GClosureNotify) meta_marshal; - closure->meta_marshal = 1; + new_notifiers[0].data = marshal_data; + new_notifiers[0].notify = (GClosureNotify) meta_marshal; + + new.meta_marshal = 1; + + /* this cannot be made atomic, as soon as we switch on the meta_marshal + * bit, another thread could use the notifier while we have not yet + * copied it. the safest is to install the new_notifiers first and then + * switch on the meta_marshal flag. */ + closure->notifiers = new_notifiers; + + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) { + g_free (new_notifiers); + goto retry; + } + + g_free (old_notifiers); } void @@ -185,40 +257,56 @@ g_closure_add_marshal_guards (GClosure *closure, GClosureNotify post_marshal_notify) { guint i; + GClosure old, new; + GClosureNotifyData *old_notifiers, *new_notifiers; g_return_if_fail (closure != NULL); g_return_if_fail (pre_marshal_notify != NULL); g_return_if_fail (post_marshal_notify != NULL); - g_return_if_fail (closure->is_invalid == FALSE); - g_return_if_fail (closure->in_marshal == FALSE); - g_return_if_fail (closure->n_guards < CLOSURE_MAX_N_GUARDS); - - closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 2); - if (closure->n_inotifiers) - closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + - closure->n_inotifiers + 1)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + 0)]; - if (closure->n_inotifiers > 1) - closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + - closure->n_inotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + 1)]; - if (closure->n_fnotifiers) - closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + 1)] = closure->notifiers[CLOSURE_N_MFUNCS (closure) + 0]; - if (closure->n_fnotifiers > 1) - closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers)] = closure->notifiers[CLOSURE_N_MFUNCS (closure) + 1]; - if (closure->n_guards) - closure->notifiers[(closure->meta_marshal + - closure->n_guards + - closure->n_guards + 1)] = closure->notifiers[closure->meta_marshal + closure->n_guards]; - i = closure->n_guards++; - closure->notifiers[closure->meta_marshal + i].data = pre_marshal_data; - closure->notifiers[closure->meta_marshal + i].notify = pre_marshal_notify; - closure->notifiers[closure->meta_marshal + i + 1].data = post_marshal_data; - closure->notifiers[closure->meta_marshal + i + 1].notify = post_marshal_notify; + +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + g_return_if_fail (old.is_invalid == FALSE); + g_return_if_fail (old.in_marshal == FALSE); + g_return_if_fail (old.n_guards < CLOSURE_MAX_N_GUARDS); + + old_notifiers = closure->notifiers; + new_notifiers = g_renew (GClosureNotifyData, old_notifiers, CLOSURE_N_NOTIFIERS (&old) + 2); + if (old.n_inotifiers) + new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + + old.n_inotifiers + 1)] = new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + 0)]; + if (old.n_inotifiers > 1) + new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + + old.n_inotifiers)] = new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + 1)]; + if (old.n_fnotifiers) + new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + 1)] = new_notifiers[CLOSURE_N_MFUNCS (&old) + 0]; + if (old.n_fnotifiers > 1) + new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers)] = new_notifiers[CLOSURE_N_MFUNCS (&old) + 1]; + if (old.n_guards) + new_notifiers[(old.meta_marshal + + old.n_guards + + old.n_guards + 1)] = new_notifiers[old.meta_marshal + old.n_guards]; + i = old.n_guards; + + new.n_guards = i+1; + + new_notifiers[old.meta_marshal + i].data = pre_marshal_data; + new_notifiers[old.meta_marshal + i].notify = pre_marshal_notify; + new_notifiers[old.meta_marshal + i + 1].data = post_marshal_data; + new_notifiers[old.meta_marshal + i + 1].notify = post_marshal_notify; + + /* not really atomic */ + closure->notifiers = new_notifiers; + + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; } void @@ -227,20 +315,35 @@ g_closure_add_finalize_notifier (GClosure *closure, GClosureNotify notify_func) { guint i; + GClosure old, new; + GClosureNotifyData *old_notifiers, *new_notifiers; g_return_if_fail (closure != NULL); g_return_if_fail (notify_func != NULL); - g_return_if_fail (closure->n_fnotifiers < CLOSURE_MAX_N_FNOTIFIERS); - - closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 1); - if (closure->n_inotifiers) - closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + - closure->n_inotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + 0)]; - i = CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers++; - closure->notifiers[i].data = notify_data; - closure->notifiers[i].notify = notify_func; + +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + g_return_if_fail (old.n_fnotifiers < CLOSURE_MAX_N_FNOTIFIERS); + + old_notifiers = closure->notifiers; + new_notifiers = g_renew (GClosureNotifyData, old_notifiers, CLOSURE_N_NOTIFIERS (&old) + 1); + if (old.n_inotifiers) + new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + + old.n_inotifiers)] = new_notifiers[(CLOSURE_N_MFUNCS (&old) + + old.n_fnotifiers + 0)]; + i = CLOSURE_N_MFUNCS (&old) + old.n_fnotifiers; + new.n_fnotifiers++; + + new_notifiers[i].data = notify_data; + new_notifiers[i].notify = notify_func; + + /* not really atomic */ + closure->notifiers = new_notifiers; + + while (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; } void @@ -249,16 +352,31 @@ g_closure_add_invalidate_notifier (GClosure *closure, GClosureNotify notify_func) { guint i; + GClosure old, new; + GClosureNotifyData *old_notifiers, *new_notifiers; g_return_if_fail (closure != NULL); g_return_if_fail (notify_func != NULL); - g_return_if_fail (closure->is_invalid == FALSE); - g_return_if_fail (closure->n_inotifiers < CLOSURE_MAX_N_INOTIFIERS); - closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 1); - i = CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers + closure->n_inotifiers++; - closure->notifiers[i].data = notify_data; - closure->notifiers[i].notify = notify_func; +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + g_return_if_fail (old.is_invalid == FALSE); + g_return_if_fail (old.n_inotifiers < CLOSURE_MAX_N_INOTIFIERS); + + old_notifiers = closure->notifiers; + new_notifiers = g_renew (GClosureNotifyData, old_notifiers, CLOSURE_N_NOTIFIERS (&old) + 1); + i = CLOSURE_N_MFUNCS (&old) + old.n_fnotifiers + old.n_inotifiers; + new.n_inotifiers++; + + new_notifiers[i].data = notify_data; + new_notifiers[i].notify = notify_func; + + /* not really atomic */ + closure->notifiers = new_notifiers; + + while (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; } static inline gboolean @@ -267,12 +385,19 @@ closure_try_remove_inotify (GClosure *closure, GClosureNotify notify_func) { GClosureNotifyData *ndata, *nlast; + GClosure old, new; + +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); - nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (closure) - 1; - for (ndata = nlast + 1 - closure->n_inotifiers; ndata <= nlast; ndata++) + nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (&old) - 1; + for (ndata = nlast + 1 - old.n_inotifiers; ndata <= nlast; ndata++) if (ndata->notify == notify_func && ndata->data == notify_data) { - closure->n_inotifiers -= 1; + new.n_inotifiers -= 1; + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; + if (ndata < nlast) *ndata = *nlast; @@ -287,19 +412,27 @@ closure_try_remove_fnotify (GClosure *closure, GClosureNotify notify_func) { GClosureNotifyData *ndata, *nlast; + GClosure old, new; - nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (closure) - closure->n_inotifiers - 1; - for (ndata = nlast + 1 - closure->n_fnotifiers; ndata <= nlast; ndata++) +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (&old) - old.n_inotifiers - 1; + for (ndata = nlast + 1 - old.n_fnotifiers; ndata <= nlast; ndata++) if (ndata->notify == notify_func && ndata->data == notify_data) { - closure->n_fnotifiers -= 1; + new.n_fnotifiers -= 1; + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; + if (ndata < nlast) *ndata = *nlast; - if (closure->n_inotifiers) - closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) + - closure->n_fnotifiers + - closure->n_inotifiers)]; + + if (new.n_inotifiers) + closure->notifiers[(CLOSURE_N_MFUNCS (&new) + + new.n_fnotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (&new) + + new.n_fnotifiers + + new.n_inotifiers)]; return TRUE; } return FALSE; @@ -312,7 +445,7 @@ g_closure_ref (GClosure *closure) g_return_val_if_fail (closure->ref_count > 0, NULL); g_return_val_if_fail (closure->ref_count < CLOSURE_MAX_REF_COUNT, NULL); - closure->ref_count += 1; + CLOSURE_REF (closure); return closure; } @@ -320,12 +453,21 @@ g_closure_ref (GClosure *closure) void g_closure_invalidate (GClosure *closure) { + GClosure old, new; + g_return_if_fail (closure != NULL); - if (!closure->is_invalid) +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + if (!old.is_invalid) { - closure->ref_count += 1; /* preserve floating flag */ - closure->is_invalid = TRUE; + new.ref_count++; + new.is_invalid = TRUE; + + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; + closure_invoke_notifiers (closure, INOTIFY); g_closure_unref (closure); } @@ -334,15 +476,14 @@ g_closure_invalidate (GClosure *closure) void g_closure_unref (GClosure *closure) { + gboolean is_zero; + g_return_if_fail (closure != NULL); g_return_if_fail (closure->ref_count > 0); - if (closure->ref_count == 1) /* last unref, invalidate first */ - g_closure_invalidate (closure); + CLOSURE_UNREF (closure, is_zero); - closure->ref_count -= 1; - - if (closure->ref_count == 0) + if (G_UNLIKELY (is_zero)) { closure_invoke_notifiers (closure, FNOTIFY); g_free (closure->notifiers); @@ -353,6 +494,8 @@ g_closure_unref (GClosure *closure) void g_closure_sink (GClosure *closure) { + GClosure old, new; + g_return_if_fail (closure != NULL); g_return_if_fail (closure->ref_count > 0); @@ -361,13 +504,17 @@ g_closure_sink (GClosure *closure) * is unowned. with invoking g_closure_sink() code may * indicate that it takes over that intiial ref_count. */ - if (closure->floating) +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + if (old.floating) { - closure->floating = FALSE; - if (closure->ref_count > 1) - closure->ref_count -= 1; - else - g_closure_unref (closure); + new.floating = FALSE; + + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; + + g_closure_unref (closure); } } @@ -376,10 +523,14 @@ g_closure_remove_invalidate_notifier (GClosure *closure, gpointer notify_data, GClosureNotify notify_func) { + GClosure bits; + g_return_if_fail (closure != NULL); g_return_if_fail (notify_func != NULL); - if (closure->is_invalid && closure->in_inotify && /* account removal of notify_func() while its called */ + CLOSURE_READ_BITS (closure, &bits); + + if (bits.is_invalid && bits.in_inotify && /* account removal of notify_func() while its called */ ((gpointer) closure->marshal) == ((gpointer) notify_func) && closure->data == notify_data) closure->marshal = NULL; else if (!closure_try_remove_inotify (closure, notify_data, notify_func)) @@ -392,10 +543,14 @@ g_closure_remove_finalize_notifier (GClosure *closure, gpointer notify_data, GClosureNotify notify_func) { + GClosure bits; + g_return_if_fail (closure != NULL); g_return_if_fail (notify_func != NULL); - if (closure->is_invalid && !closure->in_inotify && /* account removal of notify_func() while its called */ + CLOSURE_READ_BITS (closure, &bits); + + if (bits.is_invalid && !bits.in_inotify && /* account removal of notify_func() while its called */ ((gpointer) closure->marshal) == ((gpointer) notify_func) && closure->data == notify_data) closure->marshal = NULL; else if (!closure_try_remove_fnotify (closure, notify_data, notify_func)) @@ -410,19 +565,29 @@ g_closure_invoke (GClosure *closure, const GValue *param_values, gpointer invocation_hint) { + GClosure old, new; + g_return_if_fail (closure != NULL); - if (!closure->is_invalid) - { +retry: + CLOSURE_READ_BITS2 (closure, &old, &new); + + if (!old.is_invalid) + { GClosureMarshal marshal; gpointer marshal_data; - gboolean in_marshal = closure->in_marshal; + gboolean in_marshal = old.in_marshal; + gboolean meta_marshal = old.meta_marshal; + + g_return_if_fail (closure->marshal || meta_marshal); - g_return_if_fail (closure->marshal || closure->meta_marshal); + new.ref_count++; + new.in_marshal = TRUE; - closure->ref_count += 1; /* preserve floating flag */ - closure->in_marshal = TRUE; - if (closure->meta_marshal) + if (!CLOSURE_SWAP_BITS (closure, &old, &new)) + goto retry; + + if (meta_marshal) { marshal_data = closure->notifiers[0].data; marshal = (GClosureMarshal) closure->notifiers[0].notify; @@ -434,15 +599,22 @@ g_closure_invoke (GClosure *closure, } if (!in_marshal) closure_invoke_notifiers (closure, PRE_NOTIFY); + marshal (closure, return_value, n_param_values, param_values, invocation_hint, marshal_data); + if (!in_marshal) closure_invoke_notifiers (closure, POST_NOTIFY); - closure->in_marshal = in_marshal; - g_closure_unref (closure); + + do { + CLOSURE_READ_BITS2 (closure, &old, &new); + new.in_marshal = in_marshal; + new.ref_count--; + } + while (!CLOSURE_SWAP_BITS (closure, &old, &new)); } } diff --git a/gobject/gobject.c b/gobject/gobject.c index aaf4d54a7..073497f38 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -66,7 +66,6 @@ static void g_object_init (GObject *object); static GObject* g_object_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params); -static void g_object_last_unref (GObject *object); static void g_object_real_dispose (GObject *object); static void g_object_finalize (GObject *object); static void g_object_do_set_property (GObject *object, @@ -117,6 +116,7 @@ static gulong gobject_signals[LAST_SIGNAL] = { 0, }; G_LOCK_DEFINE_STATIC (construct_objects_lock); static GSList *construct_objects = NULL; +static GStaticRecMutex notify_mutex = G_STATIC_REC_MUTEX_INIT; /* --- functions --- */ #ifdef G_ENABLE_DEBUG @@ -533,7 +533,7 @@ g_object_real_dispose (GObject *object) /* yes, temporarily altering the ref_count is hackish, but that * enforces people not jerking around with weak_ref notifiers */ - ref_count = object->ref_count; + ref_count = g_atomic_int_get (&object->ref_count); object->ref_count = 0; g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL); object->ref_count = ref_count; @@ -556,38 +556,6 @@ g_object_finalize (GObject *object) #endif /* G_ENABLE_DEBUG */ } -static void -g_object_last_unref (GObject *object) -{ - g_return_if_fail (object->ref_count > 0); - - if (object->ref_count == 1) /* may have been re-referenced meanwhile */ - G_OBJECT_GET_CLASS (object)->dispose (object); - -#ifdef G_ENABLE_DEBUG - if (g_trap_object_ref == object) - G_BREAKPOINT (); -#endif /* G_ENABLE_DEBUG */ - - object->ref_count -= 1; - - if (object->ref_count == 0) /* may have been re-referenced meanwhile */ - { - g_signal_handlers_destroy (object); - g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL); - G_OBJECT_GET_CLASS (object)->finalize (object); -#ifdef G_ENABLE_DEBUG - IF_DEBUG (OBJECTS) - { - /* catch objects not chaining finalize handlers */ - G_LOCK (debug_objects); - g_assert (g_hash_table_lookup (debug_objects_ht, object) == NULL); - G_UNLOCK (debug_objects); - } -#endif /* G_ENABLE_DEBUG */ - g_type_free_instance ((GTypeInstance*) object); - } -} static void g_object_dispatch_properties_changed (GObject *object, @@ -615,12 +583,15 @@ void g_object_freeze_notify (GObject *object) { g_return_if_fail (G_IS_OBJECT (object)); - if (!object->ref_count) + + if (g_atomic_int_get (&object->ref_count) == 0) return; + g_static_rec_mutex_lock (¬ify_mutex); g_object_ref (object); g_object_notify_queue_freeze (object, &property_notify_context); g_object_unref (object); + g_static_rec_mutex_unlock (¬ify_mutex); } void @@ -631,7 +602,7 @@ g_object_notify (GObject *object, g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (property_name != NULL); - if (!object->ref_count) + if (g_atomic_int_get (&object->ref_count) == 0) return; g_object_ref (object); @@ -651,10 +622,13 @@ g_object_notify (GObject *object, property_name); else { - GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, &property_notify_context); - + GObjectNotifyQueue *nqueue; + + g_static_rec_mutex_lock (¬ify_mutex); + nqueue = g_object_notify_queue_freeze (object, &property_notify_context); g_object_notify_queue_add (object, nqueue, pspec); g_object_notify_queue_thaw (object, nqueue); + g_static_rec_mutex_unlock (¬ify_mutex); } g_object_unref (object); } @@ -665,16 +639,18 @@ g_object_thaw_notify (GObject *object) GObjectNotifyQueue *nqueue; g_return_if_fail (G_IS_OBJECT (object)); - if (!object->ref_count) + if (g_atomic_int_get (&object->ref_count) == 0) return; g_object_ref (object); + g_static_rec_mutex_lock (¬ify_mutex); nqueue = g_object_notify_queue_from_object (object, &property_notify_context); if (!nqueue || !nqueue->freeze_count) g_warning ("%s: property-changed notification for %s(%p) is not frozen", G_STRFUNC, G_OBJECT_TYPE_NAME (object), object); else g_object_notify_queue_thaw (object, nqueue); + g_static_rec_mutex_unlock (¬ify_mutex); g_object_unref (object); } @@ -730,7 +706,9 @@ object_set_property (GObject *object, else { class->set_property (object, param_id, &tmp_value, pspec); + g_static_rec_mutex_lock (¬ify_mutex); g_object_notify_queue_add (object, nqueue, pspec); + g_static_rec_mutex_unlock (¬ify_mutex); } g_value_unset (&tmp_value); } @@ -1085,6 +1063,7 @@ g_object_set_valist (GObject *object, g_return_if_fail (G_IS_OBJECT (object)); + g_static_rec_mutex_lock (¬ify_mutex); g_object_ref (object); nqueue = g_object_notify_queue_freeze (object, &property_notify_context); @@ -1141,6 +1120,7 @@ g_object_set_valist (GObject *object, g_object_notify_queue_thaw (object, nqueue); g_object_unref (object); + g_static_rec_mutex_unlock (¬ify_mutex); } void @@ -1246,6 +1226,7 @@ g_object_set_property (GObject *object, g_return_if_fail (property_name != NULL); g_return_if_fail (G_IS_VALUE (value)); + g_static_rec_mutex_lock (¬ify_mutex); g_object_ref (object); nqueue = g_object_notify_queue_freeze (object, &property_notify_context); @@ -1271,6 +1252,7 @@ g_object_set_property (GObject *object, g_object_notify_queue_thaw (object, nqueue); g_object_unref (object); + g_static_rec_mutex_unlock (¬ify_mutex); } void @@ -1663,6 +1645,7 @@ gpointer g_object_ref (gpointer _object) { GObject *object = _object; + gint old_val; g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (object->ref_count > 0, NULL); @@ -1672,8 +1655,10 @@ g_object_ref (gpointer _object) G_BREAKPOINT (); #endif /* G_ENABLE_DEBUG */ - object->ref_count += 1; - if (object->ref_count == 2 && OBJECT_HAS_TOGGLE_REF (object)) + + old_val = g_atomic_int_exchange_and_add (&object->ref_count, 1); + + if (old_val == 1 && OBJECT_HAS_TOGGLE_REF (object)) toggle_refs_notify (object, FALSE); return object; @@ -1683,6 +1668,8 @@ void g_object_unref (gpointer _object) { GObject *object = _object; + gint old_val; + gboolean is_zero; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (object->ref_count > 0); @@ -1692,14 +1679,60 @@ g_object_unref (gpointer _object) G_BREAKPOINT (); #endif /* G_ENABLE_DEBUG */ - if (object->ref_count > 1) +retry1: + old_val = g_atomic_int_get (&object->ref_count); + if (old_val > 1) { - object->ref_count -= 1; - if (object->ref_count == 1 && OBJECT_HAS_TOGGLE_REF (object)) + if (!g_atomic_int_compare_and_exchange (&object->ref_count, old_val, old_val-1)) + goto retry1; + + /* if we went from 2->1 we need to notify toggle refs if any */ + if (old_val == 2 && OBJECT_HAS_TOGGLE_REF (object)) toggle_refs_notify (object, TRUE); } else - g_object_last_unref (object); + { + /* removing the last ref */ + G_OBJECT_GET_CLASS (object)->dispose (object); + + /* dispose could have resurected the object */ +retry2: + old_val = g_atomic_int_get (&object->ref_count); + if (old_val > 1) + { + if (!g_atomic_int_compare_and_exchange (&object->ref_count, old_val, old_val-1)) + goto retry2; + + /* if we went from 2->1 we need to notify toggle refs if any */ + if (old_val == 2 && OBJECT_HAS_TOGGLE_REF (object)) + toggle_refs_notify (object, TRUE); + + return; + } + + /* we are still taking away the last ref */ + g_signal_handlers_destroy (object); + g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL); + + /* now we really take away the last ref */ + is_zero = g_atomic_int_dec_and_test (&object->ref_count); + + /* may have been re-referenced meanwhile */ + if (G_LIKELY (is_zero)) + { + G_OBJECT_GET_CLASS (object)->finalize (object); +#ifdef G_ENABLE_DEBUG + IF_DEBUG (OBJECTS) + { + /* catch objects not chaining finalize handlers */ + G_LOCK (debug_objects); + g_assert (g_hash_table_lookup (debug_objects_ht, object) == NULL); + G_UNLOCK (debug_objects); + } +#endif /* G_ENABLE_DEBUG */ + g_type_free_instance ((GTypeInstance*) object); + } + } } gpointer diff --git a/gobject/gparam.c b/gobject/gparam.c index 914bc123d..7147823a8 100644 --- a/gobject/gparam.c +++ b/gobject/gparam.c @@ -64,7 +64,6 @@ static gchar* value_param_lcopy_value (const GValue *value, /* --- variables --- */ static GQuark quark_floating = 0; -G_LOCK_DEFINE_STATIC (pspec_ref_count); /* --- functions --- */ @@ -169,43 +168,26 @@ GParamSpec* g_param_spec_ref (GParamSpec *pspec) { g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); + g_return_val_if_fail (pspec->ref_count > 0, NULL); + + g_atomic_int_inc (&pspec->ref_count); - G_LOCK (pspec_ref_count); - if (pspec->ref_count > 0) - { - pspec->ref_count += 1; - G_UNLOCK (pspec_ref_count); - } - else - { - G_UNLOCK (pspec_ref_count); - g_return_val_if_fail (pspec->ref_count > 0, NULL); - } - return pspec; } void g_param_spec_unref (GParamSpec *pspec) { + gboolean is_zero; + g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + g_return_if_fail (pspec->ref_count > 0); - G_LOCK (pspec_ref_count); - if (pspec->ref_count > 0) - { - gboolean need_finalize; - - /* sync with _sink */ - pspec->ref_count -= 1; - need_finalize = pspec->ref_count == 0; - G_UNLOCK (pspec_ref_count); - if (need_finalize) - G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec); - } - else + is_zero = g_atomic_int_dec_and_test (&pspec->ref_count); + + if (G_UNLIKELY (is_zero)) { - G_UNLOCK (pspec_ref_count); - g_return_if_fail (pspec->ref_count > 0); + G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec); } } @@ -213,29 +195,11 @@ void g_param_spec_sink (GParamSpec *pspec) { g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + g_return_if_fail (pspec->ref_count > 0); - G_LOCK (pspec_ref_count); - if (pspec->ref_count > 0) - { - if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating)) - { - /* sync with _unref */ - if (pspec->ref_count > 1) - pspec->ref_count -= 1; - else - { - G_UNLOCK (pspec_ref_count); - g_param_spec_unref (pspec); - - return; - } - } - G_UNLOCK (pspec_ref_count); - } - else + if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating)) { - G_UNLOCK (pspec_ref_count); - g_return_if_fail (pspec->ref_count > 0); + g_param_spec_unref (pspec); } } 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; |