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 | |
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)
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 21 | ||||
-rw-r--r-- | ChangeLog.pre-2-12 | 21 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 21 | ||||
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | glib/giochannel.c | 9 | ||||
-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 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/refcount/Makefile.am | 34 | ||||
-rw-r--r-- | tests/refcount/objects.c | 155 | ||||
-rw-r--r-- | tests/refcount/objects2.c | 111 | ||||
-rw-r--r-- | tests/refcount/properties.c | 254 | ||||
-rw-r--r-- | tests/refcount/properties2.c | 195 | ||||
-rw-r--r-- | tests/refcount/signals.c | 281 |
18 files changed, 1579 insertions, 245 deletions
@@ -1,5 +1,26 @@ 2005-07-15 Matthias Clasen <mclasen@redhat.com> + * 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) + * NEWS: Updates 2005-07-14 Matthias Clasen <mclasen@redhat.com> diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index e248c300c..0e916a05d 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,5 +1,26 @@ 2005-07-15 Matthias Clasen <mclasen@redhat.com> + * 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) + * NEWS: Updates 2005-07-14 Matthias Clasen <mclasen@redhat.com> diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index e248c300c..0e916a05d 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,5 +1,26 @@ 2005-07-15 Matthias Clasen <mclasen@redhat.com> + * 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) + * NEWS: Updates 2005-07-14 Matthias Clasen <mclasen@redhat.com> diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index e248c300c..0e916a05d 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,5 +1,26 @@ 2005-07-15 Matthias Clasen <mclasen@redhat.com> + * 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) + * NEWS: Updates 2005-07-14 Matthias Clasen <mclasen@redhat.com> diff --git a/configure.in b/configure.in index c419f2d75..78fa843f3 100644 --- a/configure.in +++ b/configure.in @@ -2723,6 +2723,7 @@ docs/reference/gobject/Makefile docs/reference/gobject/version.xml tests/Makefile tests/gobject/Makefile +tests/refcount/Makefile m4macros/Makefile ]) diff --git a/glib/giochannel.c b/glib/giochannel.c index 0e898bacf..0a3be6d37 100644 --- a/glib/giochannel.c +++ b/glib/giochannel.c @@ -97,7 +97,7 @@ g_io_channel_ref (GIOChannel *channel) { g_return_val_if_fail (channel != NULL, NULL); - channel->ref_count++; + g_atomic_int_inc (&channel->ref_count); return channel; } @@ -105,10 +105,13 @@ g_io_channel_ref (GIOChannel *channel) void g_io_channel_unref (GIOChannel *channel) { + gboolean is_zero; + g_return_if_fail (channel != NULL); - channel->ref_count--; - if (channel->ref_count == 0) + is_zero = g_atomic_int_dec_and_test (&channel->ref_count); + + if (G_UNLIKELY (is_zero)) { if (channel->close_on_unref) g_io_channel_shutdown (channel, TRUE, NULL); 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; diff --git a/tests/Makefile.am b/tests/Makefile.am index 2cc3e7bc8..11b110c36 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=gobject +SUBDIRS=gobject refcount INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/glib -I$(top_srcdir)/gmodule $(GLIB_DEBUG_FLAGS) diff --git a/tests/refcount/Makefile.am b/tests/refcount/Makefile.am new file mode 100644 index 000000000..5d98c3262 --- /dev/null +++ b/tests/refcount/Makefile.am @@ -0,0 +1,34 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/glib \ + -I$(top_srcdir)/gmodule \ + $(GLIB_DEBUG_FLAGS) + +libglib = $(top_builddir)/glib/libglib-2.0.la +libgthread = $(top_builddir)/gthread/libgthread-2.0.la +libgmodule = $(top_builddir)/gmodule/libgmodule-2.0.la +libgobject = $(top_builddir)/gobject/libgobject-2.0.la + +LDADD = $(libgobject) $(libgthread) + +test_programs = \ + signal1 \ + signal2 \ + signal3 \ + objects \ + objects2 \ + properties \ + properties2 + +signal1_SOURCES = signals.c +signal1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS) +signal2_SOURCES = signals.c +signal2_CFLAGS = -DTESTNUM=2 $(AM_CFLAGS) +signal3_SOURCES = signals.c +signal3_CFLAGS = -DTESTNUM=3 $(AM_CFLAGS) + +check_PROGRAMS = $(test_programs) + +TESTS = $(test_programs) +TESTS_ENVIRONMENT = srcdir=$(srcdir) \ + LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset diff --git a/tests/refcount/objects.c b/tests/refcount/objects.c new file mode 100644 index 000000000..2fb1a6c97 --- /dev/null +++ b/tests/refcount/objects.c @@ -0,0 +1,155 @@ +#include <unistd.h> +#include <glib.h> +#include <glib-object.h> + +#define G_TYPE_TEST (g_test_get_type ()) +#define G_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest)) +#define G_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST)) +#define G_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass)) +#define G_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST)) +#define G_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass)) + +typedef struct _GTest GTest; +typedef struct _GTestClass GTestClass; + +struct _GTest +{ + GObject object; +}; + +struct _GTestClass +{ + GObjectClass parent_class; +}; + +static GType g_test_get_type (void); +static gboolean stopping; + +static void g_test_class_init (GTestClass * klass); +static void g_test_init (GTest * test); +static void g_test_dispose (GObject * object); + +static GObjectClass *parent_class = NULL; + +static GType +g_test_get_type (void) +{ + static GType test_type = 0; + + if (!test_type) { + static const GTypeInfo test_info = { + sizeof (GTestClass), + NULL, + NULL, + (GClassInitFunc) g_test_class_init, + NULL, + NULL, + sizeof (GTest), + 0, + (GInstanceInitFunc) g_test_init, + NULL + }; + + test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", + &test_info, 0); + } + return test_type; +} + +static void +g_test_class_init (GTestClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->dispose = g_test_dispose; +} + +static void +g_test_init (GTest * test) +{ + g_print ("init %p\n", test); +} + +static void +g_test_dispose (GObject * object) +{ + GTest *test; + + test = G_TEST (object); + + g_print ("dispose %p!\n", object); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +g_test_do_refcount (GTest * test) +{ + g_object_ref (test); + g_object_unref (test); +} + +static gpointer +run_thread (GTest * test) +{ + gint i = 0; + + while (!stopping) { + g_test_do_refcount (test); + if ((i++ % 100000) == 0) { + g_print ("."); + g_usleep (1); /* context switch */ + } + } + + return NULL; +} + +int +main (int argc, char **argv) +{ + gint i; + GTest *test1, *test2; + GArray *test_threads; + + g_thread_init (NULL); + g_type_init (); + + test1 = g_object_new (G_TYPE_TEST, NULL); + test2 = g_object_new (G_TYPE_TEST, NULL); + + test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *)); + + stopping = FALSE; + + for (i = 0; i < 20; i++) { + GThread *thread; + + thread = g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL); + g_array_append_val (test_threads, thread); + + thread = g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL); + g_array_append_val (test_threads, thread); + } + sleep (5); + + stopping = TRUE; + + g_print ("\nstopping\n"); + + /* join all threads */ + for (i = 0; i < 40; i++) { + GThread *thread; + + thread = g_array_index (test_threads, GThread *, i); + g_thread_join (thread); + } + + g_print ("stopped\n"); + + return 0; +} diff --git a/tests/refcount/objects2.c b/tests/refcount/objects2.c new file mode 100644 index 000000000..539e89c23 --- /dev/null +++ b/tests/refcount/objects2.c @@ -0,0 +1,111 @@ +#include <unistd.h> +#include <glib.h> +#include <glib-object.h> + +#define G_TYPE_TEST (g_test_get_type ()) +#define G_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest)) +#define G_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST)) +#define G_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass)) +#define G_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST)) +#define G_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass)) + +typedef struct _GTest GTest; +typedef struct _GTestClass GTestClass; + +struct _GTest +{ + GObject object; +}; + +struct _GTestClass +{ + GObjectClass parent_class; +}; + +static GType g_test_get_type (void); + +static void g_test_class_init (GTestClass * klass); +static void g_test_init (GTest * test); +static void g_test_dispose (GObject * object); + +static GObjectClass *parent_class = NULL; + +static GType +g_test_get_type (void) +{ + static GType test_type = 0; + + if (!test_type) { + static const GTypeInfo test_info = { + sizeof (GTestClass), + NULL, + NULL, + (GClassInitFunc) g_test_class_init, + NULL, + NULL, + sizeof (GTest), + 0, + (GInstanceInitFunc) g_test_init, + NULL + }; + + test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", + &test_info, 0); + } + return test_type; +} + +static void +g_test_class_init (GTestClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->dispose = g_test_dispose; +} + +static void +g_test_init (GTest * test) +{ + g_print ("init %p\n", test); +} + +static void +g_test_dispose (GObject * object) +{ + GTest *test; + + test = G_TEST (object); + + g_print ("dispose %p!\n", object); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +g_test_do_refcount (GTest * test) +{ + g_object_ref (test); + g_object_unref (test); +} + +int +main (int argc, char **argv) +{ + gint i; + GTest *test; + + g_thread_init (NULL); + g_type_init (); + + test = g_object_new (G_TYPE_TEST, NULL); + + for (i=0; i<100000000; i++) { + g_test_do_refcount (test); + } + + return 0; +} diff --git a/tests/refcount/properties.c b/tests/refcount/properties.c new file mode 100644 index 000000000..f6514146d --- /dev/null +++ b/tests/refcount/properties.c @@ -0,0 +1,254 @@ +#include <unistd.h> +#include <glib.h> +#include <glib-object.h> + +#define G_TYPE_TEST (g_test_get_type ()) +#define G_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest)) +#define G_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST)) +#define G_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass)) +#define G_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST)) +#define G_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass)) + +enum { + PROP_0, + PROP_DUMMY +}; + +typedef struct _GTest GTest; +typedef struct _GTestClass GTestClass; + +struct _GTest +{ + GObject object; + + gint dummy; + + gint count; +}; + +struct _GTestClass +{ + GObjectClass parent_class; +}; + +static GType g_test_get_type (void); +static gboolean stopping; + +static void g_test_class_init (GTestClass * klass); +static void g_test_init (GTest * test); +static void g_test_dispose (GObject * object); +static void g_test_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void g_test_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static GObjectClass *parent_class = NULL; + +static GType +g_test_get_type (void) +{ + static GType test_type = 0; + + if (!test_type) { + static const GTypeInfo test_info = { + sizeof (GTestClass), + NULL, + NULL, + (GClassInitFunc) g_test_class_init, + NULL, + NULL, + sizeof (GTest), + 0, + (GInstanceInitFunc) g_test_init, + NULL + }; + + test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", + &test_info, 0); + } + return test_type; +} + +static void +g_test_class_init (GTestClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->dispose = g_test_dispose; + gobject_class->get_property = g_test_get_property; + gobject_class->set_property = g_test_set_property; + + g_object_class_install_property (gobject_class, + PROP_DUMMY, + g_param_spec_int ("dummy", + NULL, + NULL, + 0, G_MAXINT, 0, + G_PARAM_READWRITE)); +} + +static void +g_test_init (GTest * test) +{ + g_print ("init %p\n", test); +} + +static void +g_test_dispose (GObject * object) +{ + GTest *test; + + test = G_TEST (object); + + g_print ("dispose %p!\n", object); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +g_test_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GTest *test; + + test = G_TEST (object); + + switch (prop_id) + { + case PROP_DUMMY: + g_value_set_int (value, test->dummy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_test_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTest *test; + + test = G_TEST (object); + + switch (prop_id) + { + case PROP_DUMMY: + test->dummy = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dummy_notify (GObject *object, + GParamSpec *pspec) +{ + GTest *test; + + test = G_TEST (object); + + test->count++; +} + +static void +g_test_do_property (GTest * test) +{ + gint dummy; + + g_object_get (test, "dummy", &dummy, NULL); + g_object_set (test, "dummy", dummy + 1, NULL); +} + +static gpointer +run_thread (GTest * test) +{ + gint i = 0; + + while (!stopping) { + g_test_do_property (test); + if ((i++ % 100000) == 0) { + g_print ("."); + g_usleep (1); /* context switch */ + } + } + + return NULL; +} + +int +main (int argc, char **argv) +{ + gint i; + GArray *test_objects; + GArray *test_threads; + + g_thread_init (NULL); + g_type_init (); + + test_objects = g_array_new (FALSE, FALSE, sizeof (GTest *)); + + for (i = 0; i < 20; i++) { + GTest *test; + + test = g_object_new (G_TYPE_TEST, NULL); + g_array_append_val (test_objects, test); + + g_assert (test->count == test->dummy); + g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL); + } + + test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *)); + + stopping = FALSE; + + for (i = 0; i < 20; i++) { + GThread *thread; + GTest *test; + + test = g_array_index (test_objects, GTest *, i); + + thread = g_thread_create ((GThreadFunc) run_thread, test, TRUE, NULL); + g_array_append_val (test_threads, thread); + } + sleep (5); + + stopping = TRUE; + + g_print ("\nstopping\n"); + + /* join all threads */ + for (i = 0; i < 20; i++) { + GThread *thread; + + thread = g_array_index (test_threads, GThread *, i); + g_thread_join (thread); + } + + g_print ("stopped\n"); + + for (i = 0; i < 20; i++) { + GTest *test; + + test = g_array_index (test_objects, GTest *, i); + + g_assert (test->count == test->dummy); + } + + return 0; +} diff --git a/tests/refcount/properties2.c b/tests/refcount/properties2.c new file mode 100644 index 000000000..94bbe71ce --- /dev/null +++ b/tests/refcount/properties2.c @@ -0,0 +1,195 @@ +#include <unistd.h> +#include <glib.h> +#include <glib-object.h> + +#define G_TYPE_TEST (g_test_get_type ()) +#define G_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest)) +#define G_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST)) +#define G_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass)) +#define G_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST)) +#define G_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass)) + +enum { + PROP_0, + PROP_DUMMY +}; + +typedef struct _GTest GTest; +typedef struct _GTestClass GTestClass; + +struct _GTest +{ + GObject object; + + gint dummy; +}; + +struct _GTestClass +{ + GObjectClass parent_class; +}; + +static GType g_test_get_type (void); + +static void g_test_class_init (GTestClass * klass); +static void g_test_init (GTest * test); +static void g_test_dispose (GObject * object); +static void g_test_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void g_test_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static GObjectClass *parent_class = NULL; + +static GType +g_test_get_type (void) +{ + static GType test_type = 0; + + if (!test_type) { + static const GTypeInfo test_info = { + sizeof (GTestClass), + NULL, + NULL, + (GClassInitFunc) g_test_class_init, + NULL, + NULL, + sizeof (GTest), + 0, + (GInstanceInitFunc) g_test_init, + NULL + }; + + test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", + &test_info, 0); + } + return test_type; +} + +static void +g_test_class_init (GTestClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + gobject_class->dispose = g_test_dispose; + gobject_class->get_property = g_test_get_property; + gobject_class->set_property = g_test_set_property; + + g_object_class_install_property (gobject_class, + PROP_DUMMY, + g_param_spec_int ("dummy", + NULL, + NULL, + 0, G_MAXINT, 0, + G_PARAM_READWRITE)); +} + +static void +g_test_init (GTest * test) +{ + g_print ("init %p\n", test); +} + +static void +g_test_dispose (GObject * object) +{ + GTest *test; + + test = G_TEST (object); + + g_print ("dispose %p!\n", object); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +g_test_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GTest *test; + + test = G_TEST (object); + + switch (prop_id) + { + case PROP_DUMMY: + g_value_set_int (value, test->dummy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_test_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTest *test; + + test = G_TEST (object); + + switch (prop_id) + { + case PROP_DUMMY: + test->dummy = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gint count = 0; + +static void +dummy_notify (GObject *object, + GParamSpec *pspec) +{ + count++; +} + +static void +g_test_do_property (GTest * test) +{ + gint dummy; + + g_object_get (test, "dummy", &dummy, NULL); + g_object_set (test, "dummy", dummy + 1, NULL); +} + +int +main (int argc, char **argv) +{ + gint i; + GTest *test; + + g_thread_init (NULL); + g_type_init (); + + test = g_object_new (G_TYPE_TEST, NULL); + + g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL); + + g_assert (count == test->dummy); + + for (i=0; i<1000000; i++) { + g_test_do_property (test); + } + + g_assert (count == test->dummy); + + return 0; +} diff --git a/tests/refcount/signals.c b/tests/refcount/signals.c new file mode 100644 index 000000000..a0ed09acf --- /dev/null +++ b/tests/refcount/signals.c @@ -0,0 +1,281 @@ +#include <unistd.h> +#include <glib.h> +#include <glib-object.h> + +#define G_TYPE_TEST (g_test_get_type ()) +#define G_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest)) +#define G_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST)) +#define G_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass)) +#define G_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST)) +#define G_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass)) + +static GRand *rand; + +typedef struct _GTest GTest; +typedef struct _GTestClass GTestClass; + +struct _GTest +{ + GObject object; + + gint value; +}; + +struct _GTestClass +{ + GObjectClass parent_class; + + void (*test_signal1) (GTest * test, gint an_int); + void (*test_signal2) (GTest * test, gint an_int); +}; + +static GType g_test_get_type (void); +static gboolean stopping; + +/* Element signals and args */ +enum +{ + TEST_SIGNAL1, + TEST_SIGNAL2, + /* add more above */ + LAST_SIGNAL +}; + +enum +{ + ARG_0, + ARG_TEST_PROP +}; + +static void g_test_class_init (GTestClass * klass); +static void g_test_init (GTest * test); +static void g_test_dispose (GObject * object); + +static void signal2_handler (GTest * test, gint anint); + +static void g_test_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void g_test_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GObjectClass *parent_class = NULL; + +static guint g_test_signals[LAST_SIGNAL] = { 0 }; + +static GType +g_test_get_type (void) +{ + static GType test_type = 0; + + if (!test_type) { + static const GTypeInfo test_info = { + sizeof (GTestClass), + NULL, + NULL, + (GClassInitFunc) g_test_class_init, + NULL, + NULL, + sizeof (GTest), + 0, + (GInstanceInitFunc) g_test_init, + NULL + }; + + rand = g_rand_new(); + + test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", + &test_info, 0); + } + return test_type; +} + +static void +g_test_class_init (GTestClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + if (!g_thread_supported ()) + g_thread_init (NULL); + + gobject_class->dispose = g_test_dispose; + gobject_class->set_property = g_test_set_property; + gobject_class->get_property = g_test_get_property; + + g_test_signals[TEST_SIGNAL1] = + g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal1), NULL, + NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + g_test_signals[TEST_SIGNAL2] = + g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal2), NULL, + NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEST_PROP, + g_param_spec_int ("test-prop", "Test Prop", "Test property", + 0, 1, 0, G_PARAM_READWRITE)); + + klass->test_signal2 = signal2_handler; +} + +static void +g_test_init (GTest * test) +{ + g_print ("init %p\n", test); + + test->value = 0; +} + +static void +g_test_dispose (GObject * object) +{ + GTest *test; + + test = G_TEST (object); + + g_print ("dispose %p!\n", object); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +g_test_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GTest *test; + + test = G_TEST (object); + + switch (prop_id) { + case ARG_TEST_PROP: + test->value = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_test_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GTest *test; + + test = G_TEST (object); + + switch (prop_id) { + case ARG_TEST_PROP: + g_value_set_int (value, test->value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_test_do_signal1 (GTest * test) +{ + g_signal_emit (G_OBJECT (test), g_test_signals[TEST_SIGNAL1], 0, 0); +} + +static void +signal2_handler (GTest * test, gint anint) +{ +} + +static void +g_test_do_signal2 (GTest * test) +{ + g_signal_emit (G_OBJECT (test), g_test_signals[TEST_SIGNAL2], 0, 0); +} + +static void +g_test_do_prop (GTest * test) +{ + test->value = g_rand_int (rand); + g_object_notify (G_OBJECT (test), "test-prop"); +} + +static gpointer +run_thread (GTest * test) +{ + gint i = 0; + + while (!stopping) { + if (TESTNUM == 1) + g_test_do_signal1 (test); + if (TESTNUM == 2) + g_test_do_signal2 (test); + if (TESTNUM == 3) + g_test_do_prop (test); + if ((i++ % 100000) == 0) { + g_print ("."); + g_usleep (1); /* context switch */ + } + } + + return NULL; +} + +static void +notify (GObject *object, GParamSpec *spec, gpointer user_data) +{ + gint value; + + g_object_get (object, "test-prop", &value, NULL); + //g_print ("+ %d", value); +} + +int +main (int argc, char **argv) +{ + gint i; + GTest *test1, *test2; + GArray *test_threads; + + g_thread_init (NULL); + g_type_init (); + + test1 = g_object_new (G_TYPE_TEST, NULL); + test2 = g_object_new (G_TYPE_TEST, NULL); + + g_signal_connect (test1, "notify::test-prop", notify, NULL); + g_signal_connect (test1, "test-signal1", notify, NULL); + g_signal_connect (test1, "test-signal2", notify, NULL); + + test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *)); + + stopping = FALSE; + + for (i = 0; i < 20; i++) { + GThread *thread; + + thread = g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL); + g_array_append_val (test_threads, thread); + + thread = g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL); + g_array_append_val (test_threads, thread); + } + sleep (5); + + stopping = TRUE; + + g_print ("\nstopping\n"); + + /* join all threads */ + for (i = 0; i < 40; i++) { + GThread *thread; + + thread = g_array_index (test_threads, GThread *, i); + g_thread_join (thread); + } + + g_print ("stopped\n"); + + return 0; +} |