Re: gobject thread safety



On Fri, 2005-07-08 at 11:07 +0200, Andy Wingo wrote:
> Hi Alex,
> 
> On Fri, 2005-07-08 at 09:54 +0200, Alexander Larsson wrote:
> >
> > It would also be extremely sweet to get the atomic refcount patches in:
> > http://bugzilla.gnome.org/show_bug.cgi?id=166020
> > 
> > Whomever this is blocking on, please please try to move it forward.
> 
> It's blocking on Wim, who just now subscribed. He needs to update the
> closure refcounting, and claims he'll get to it today.

Wim has put a new atomic refcounting patch in bugzilla. 
I'll attach it here for your convenience, Tim. 

Matthias
diff -Naurp glib-tests/glib/giochannel.c glib-atomic-ni/glib/giochannel.c
--- glib-tests/glib/giochannel.c	2005-07-08 12:57:38.000000000 +0200
+++ glib-atomic-ni/glib/giochannel.c	2005-07-08 13:45:40.000000000 +0200
@@ -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 -Naurp glib-tests/gobject/gclosure.c glib-atomic-ni/gobject/gclosure.c
--- glib-tests/gobject/gclosure.c	2005-07-08 12:57:38.000000000 +0200
+++ glib-atomic-ni/gobject/gclosure.c	2005-07-12 16:51:10.000000000 +0200
@@ -36,6 +36,62 @@
 #define	CLOSURE_N_NOTIFIERS(cl)		(CLOSURE_N_MFUNCS (cl) + \
                                          (cl)->n_fnotifiers + \
                                          (cl)->n_inotifiers)
+
+/* copy of first 32 bits we need to make atomic */
+
+typedef struct
+{
+  guint    ref_count : 15;
+  guint    meta_marshal : 1;
+  guint    n_guards : 1;
+  guint    n_fnotifiers : 2;
+  guint    n_inotifiers : 8;
+  guint    in_inotify : 1;
+  guint    floating : 1;
+  guint    derivative_flag : 1;
+  guint    in_marshal : 1;
+  guint    is_invalid : 1;
+} GClosureBits;
+
+#define BITS_AS_INT(b)	(((GAtomicClosureBits*)(b))->data.atomic)
+
+typedef struct
+{
+  union {
+    GClosureBits bits;
+    gint atomic;
+  } data;
+} GAtomicClosureBits;
+
+#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 {									\
+  GClosureBits 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 {									\
+  GClosureBits 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 +132,8 @@ static inline void
 closure_invoke_notifiers (GClosure *closure,
 			  guint     notify_type)
 {
+  GClosureBits bits, new;
+
   /* notifier layout:
    *     meta_marshal  n_guards    n_guards     n_fnotif.  n_inotifiers
    * ->[[meta_marshal][pre_guards][post_guards][fnotifiers][inotifiers]]
@@ -99,11 +157,12 @@ closure_invoke_notifiers (GClosure *clos
       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 +171,34 @@ closure_invoke_notifiers (GClosure *clos
       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 +206,9 @@ closure_invoke_notifiers (GClosure *clos
 	}
       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 +223,48 @@ g_closure_set_meta_marshal (GClosure    
 			    gpointer        marshal_data,
 			    GClosureMarshal meta_marshal)
 {
-  GClosureNotifyData *notifiers;
+  GClosureNotifyData *old_notifiers, *new_notifiers;
   guint n;
+  GClosureBits 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 +275,56 @@ g_closure_add_marshal_guards (GClosure  
 			      GClosureNotify post_marshal_notify)
 {
   guint i;
+  GClosureBits 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 +333,35 @@ g_closure_add_finalize_notifier (GClosur
 				 GClosureNotify notify_func)
 {
   guint i;
+  GClosureBits 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 +370,31 @@ g_closure_add_invalidate_notifier (GClos
 				   GClosureNotify notify_func)
 {
   guint i;
+  GClosureBits 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 +403,19 @@ closure_try_remove_inotify (GClosure    
 			    GClosureNotify notify_func)
 {
   GClosureNotifyData *ndata, *nlast;
+  GClosureBits 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 +430,27 @@ closure_try_remove_fnotify (GClosure    
 			    GClosureNotify notify_func)
 {
   GClosureNotifyData *ndata, *nlast;
+  GClosureBits 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 +463,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 +471,21 @@ g_closure_ref (GClosure *closure)
 void
 g_closure_invalidate (GClosure *closure)
 {
+  GClosureBits 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 +494,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 +512,8 @@ g_closure_unref (GClosure *closure)
 void
 g_closure_sink (GClosure *closure)
 {
+  GClosureBits old, new;
+
   g_return_if_fail (closure != NULL);
   g_return_if_fail (closure->ref_count > 0);
 
@@ -361,13 +522,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 +541,14 @@ g_closure_remove_invalidate_notifier (GC
 				      gpointer       notify_data,
 				      GClosureNotify notify_func)
 {
+  GClosureBits 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 +561,14 @@ g_closure_remove_finalize_notifier (GClo
 				    gpointer       notify_data,
 				    GClosureNotify notify_func)
 {
+  GClosureBits 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 +583,29 @@ g_closure_invoke (GClosure       *closur
 		  const GValue   *param_values,
 		  gpointer        invocation_hint)
 {
+  GClosureBits 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 +617,22 @@ g_closure_invoke (GClosure       *closur
 	}
       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 -Naurp glib-tests/gobject/gobject.c glib-atomic-ni/gobject/gobject.c
--- glib-tests/gobject/gobject.c	2005-07-08 12:57:38.000000000 +0200
+++ glib-atomic-ni/gobject/gobject.c	2005-07-08 16:09:27.000000000 +0200
@@ -66,7 +66,6 @@ static void	g_object_init				(GObject	*o
 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_signal
 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 (&notify_mutex);
   g_object_ref (object);
   g_object_notify_queue_freeze (object, &property_notify_context);
   g_object_unref (object);
+  g_static_rec_mutex_unlock (&notify_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 (&notify_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 (&notify_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 (&notify_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 (&notify_mutex);
   g_object_unref (object);
 }
 
@@ -730,7 +706,9 @@ object_set_property (GObject            
   else
     {
       class->set_property (object, param_id, &tmp_value, pspec);
+      g_static_rec_mutex_lock (&notify_mutex);
       g_object_notify_queue_add (object, nqueue, pspec);
+      g_static_rec_mutex_unlock (&notify_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 (&notify_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 (&notify_mutex);
 }
 
 void
@@ -1246,6 +1226,7 @@ g_object_set_property (GObject	    *obje
   g_return_if_fail (property_name != NULL);
   g_return_if_fail (G_IS_VALUE (value));
   
+  g_static_rec_mutex_lock (&notify_mutex);
   g_object_ref (object);
   nqueue = g_object_notify_queue_freeze (object, &property_notify_context);
   
@@ -1271,6 +1252,7 @@ g_object_set_property (GObject	    *obje
   
   g_object_notify_queue_thaw (object, nqueue);
   g_object_unref (object);
+  g_static_rec_mutex_unlock (&notify_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 -Naurp glib-tests/gobject/gparam.c glib-atomic-ni/gobject/gparam.c
--- glib-tests/gobject/gparam.c	2005-07-08 12:57:38.000000000 +0200
+++ glib-atomic-ni/gobject/gparam.c	2005-07-08 14:02:25.000000000 +0200
@@ -64,7 +64,6 @@ static gchar*	value_param_lcopy_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;
+  is_zero = g_atomic_int_dec_and_test (&pspec->ref_count);
 
-      /* 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
+  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 -Naurp glib-tests/gobject/gsignal.c glib-atomic-ni/gobject/gsignal.c
--- glib-tests/gobject/gsignal.c	2005-07-08 12:57:39.000000000 +0200
+++ glib-atomic-ni/gobject/gsignal.c	2005-07-08 14:04:10.000000000 +0200
@@ -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;
@@ -567,12 +566,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 +574,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;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]