g_object_unref racy condition can lead to crash
authorCody Russell <bratsche@gnome.org>
Tue, 6 Oct 2009 16:27:12 +0000 (12:27 -0400)
committerCody Russell <bratsche@gnome.org>
Tue, 6 Oct 2009 16:27:12 +0000 (12:27 -0400)
Store whether the object has a toggleref before decrementing the
refcount to prevent race condition when two threads simultaneously
try to unref an object with a refcount of 2.
Patch by Antoine Tremblay.

https://bugzilla.gnome.org/show_bug.cgi?id=551706

gobject/gobject.c

index 8432488..f906d31 100644 (file)
@@ -2425,11 +2425,14 @@ g_object_unref (gpointer _object)
   old_ref = g_atomic_int_get (&object->ref_count);
   if (old_ref > 1)
     {
+      /* valid if last 2 refs are owned by this call to unref and the toggle_ref */
+      gboolean has_toggle_ref = OBJECT_HAS_TOGGLE_REF (object);
+
       if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
        goto retry_atomic_decrement1;
 
       /* if we went from 2->1 we need to notify toggle refs if any */
-      if (old_ref == 2 && OBJECT_HAS_TOGGLE_REF (object))
+      if (old_ref == 2 && has_toggle_ref) /* The last ref being held in this case is owned by the toggle_ref */
        toggle_refs_notify (object, TRUE);
     }
   else
@@ -2442,13 +2445,16 @@ g_object_unref (gpointer _object)
       old_ref = g_atomic_int_get ((int *)&object->ref_count);
       if (old_ref > 1)
         {
+          /* valid if last 2 refs are owned by this call to unref and the toggle_ref */
+          gboolean has_toggle_ref = OBJECT_HAS_TOGGLE_REF (object);
+
           if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
            goto retry_atomic_decrement2;
 
           /* if we went from 2->1 we need to notify toggle refs if any */
-          if (old_ref == 2 && OBJECT_HAS_TOGGLE_REF (object))
+          if (old_ref == 2 && has_toggle_ref) /* The last ref being held in this case is owned by the toggle_ref */
            toggle_refs_notify (object, TRUE);
-          
+
          return;
        }