Merge pull request #2543 from ermshiperete/Xamarin-31021
[mono.git] / mono / sgen / sgen-gchandles.c
index 367c2c3260456a252b0351c467bc43fa8d98f335..9d00c397b9d6fb996f780a454aa56eb8647c65c2 100644 (file)
@@ -105,17 +105,17 @@ static void
 protocol_gchandle_update (int handle_type, gpointer link, gpointer old_value, gpointer new_value)
 {
        gboolean old = MONO_GC_HANDLE_IS_OBJECT_POINTER (old_value);
-       gboolean new = MONO_GC_HANDLE_IS_OBJECT_POINTER (new_value);
+       gboolean new_ = MONO_GC_HANDLE_IS_OBJECT_POINTER (new_value);
        gboolean track = handle_type == HANDLE_WEAK_TRACK;
 
        if (!MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type))
                return;
 
-       if (!old && new)
+       if (!old && new_)
                binary_protocol_dislink_add (link, MONO_GC_REVEAL_POINTER (new_value, TRUE), track);
-       else if (old && !new)
+       else if (old && !new_)
                binary_protocol_dislink_remove (link, track);
-       else if (old && new && old_value != new_value)
+       else if (old && new_ && old_value != new_value)
                binary_protocol_dislink_update (link, MONO_GC_REVEAL_POINTER (new_value, TRUE), track);
 }
 
@@ -123,15 +123,15 @@ protocol_gchandle_update (int handle_type, gpointer link, gpointer old_value, gp
 static inline gpointer
 try_set_slot (volatile gpointer *slot, GCObject *obj, gpointer old, GCHandleType type)
 {
-       gpointer new;
+       gpointer new_;
        if (obj)
-               new = MONO_GC_HANDLE_OBJECT_POINTER (obj, GC_HANDLE_TYPE_IS_WEAK (type));
+               new_ = MONO_GC_HANDLE_OBJECT_POINTER (obj, GC_HANDLE_TYPE_IS_WEAK (type));
        else
-               new = MONO_GC_HANDLE_METADATA_POINTER (sgen_client_default_metadata (), GC_HANDLE_TYPE_IS_WEAK (type));
-       SGEN_ASSERT (0, new, "Why is the occupied bit not set?");
-       if (InterlockedCompareExchangePointer (slot, new, old) == old) {
-               protocol_gchandle_update (type, (gpointer)slot, old, new);
-               return new;
+               new_ = MONO_GC_HANDLE_METADATA_POINTER (sgen_client_default_metadata (), GC_HANDLE_TYPE_IS_WEAK (type));
+       SGEN_ASSERT (0, new_, "Why is the occupied bit not set?");
+       if (InterlockedCompareExchangePointer (slot, new_, old) == old) {
+               protocol_gchandle_update (type, (gpointer)slot, old, new_);
+               return new_;
        }
        return NULL;
 }
@@ -143,7 +143,7 @@ try_occupy_slot (HandleData *handles, guint bucket, guint offset, GCObject *obj,
        volatile gpointer *link_addr = &(handles->entries [bucket] [offset]);
        if (MONO_GC_HANDLE_OCCUPIED (*link_addr))
                return FALSE;
-       return try_set_slot (link_addr, obj, NULL, handles->type) != NULL;
+       return try_set_slot (link_addr, obj, NULL, (GCHandleType)handles->type) != NULL;
 }
 
 static HandleData gc_handles [] = {
@@ -156,8 +156,7 @@ static HandleData gc_handles [] = {
 static HandleData *
 gc_handles_for_type (GCHandleType type)
 {
-       g_assert (type < HANDLE_TYPE_MAX);
-       return &gc_handles [type];
+       return type < HANDLE_TYPE_MAX ? &gc_handles [type] : NULL;
 }
 
 /* This assumes that the world is stopped. */
@@ -217,7 +216,7 @@ handle_data_grow (HandleData *handles, guint32 old_capacity)
        const size_t new_bucket_size = sizeof (**handles->entries) * growth;
        if (handles->capacity >= new_capacity)
                return;
-       entries = g_malloc0 (new_bucket_size);
+       entries = (gpointer *)g_malloc0 (new_bucket_size);
        if (handles->type == HANDLE_PINNED)
                sgen_register_root ((char *)entries, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, "pinned gc handles");
        /* The zeroing of the newly allocated bucket must be complete before storing
@@ -261,18 +260,28 @@ retry:
                goto retry;
        }
        handles->slot_hint = index;
-       bucketize (index, &bucket, &offset);
-       if (!try_occupy_slot (handles, bucket, offset, obj, track))
-               goto retry;
-       /* If a GC happens shortly after a new bucket is allocated, the entire
-        * bucket could be scanned even though it's mostly empty. To avoid this, we
-        * track the maximum index seen so far, so that we can skip the empty slots.
+
+       /*
+        * If a GC happens shortly after a new bucket is allocated, the entire
+        * bucket could be scanned even though it's mostly empty. To avoid this,
+        * we track the maximum index seen so far, so that we can skip the empty
+        * slots.
+        *
+        * Note that we update `max_index` before we even try occupying the
+        * slot.  If we did it the other way around and a GC happened in
+        * between, the GC wouldn't know that the slot was occupied.  This is
+        * not a huge deal since `obj` is on the stack and thus pinned anyway,
+        * but hopefully some day it won't be anymore.
         */
        do {
                max_index = handles->max_index;
                if (index <= max_index)
                        break;
-       } while (!InterlockedCompareExchange ((volatile gint32 *)&handles->max_index, index, max_index));
+       } while (InterlockedCompareExchange ((volatile gint32 *)&handles->max_index, index, max_index) != max_index);
+
+       bucketize (index, &bucket, &offset);
+       if (!try_occupy_slot (handles, bucket, offset, obj, track))
+               goto retry;
 #ifdef HEAVY_STATISTICS
        InterlockedIncrement ((volatile gint32 *)&stat_gc_handles_allocated);
        if (stat_gc_handles_allocated > stat_gc_handles_max_allocated)
@@ -359,7 +368,7 @@ mono_gchandle_new (GCObject *obj, gboolean pinned)
 /**
  * mono_gchandle_new_weakref:
  * @obj: managed object to get a handle for
- * @pinned: whether the object should be pinned
+ * @track_resurrection: Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
  *
  * This returns a weak handle that wraps the object, this is used to
  * keep a reference to a managed object from the unmanaged world.
@@ -367,10 +376,12 @@ mono_gchandle_new (GCObject *obj, gboolean pinned)
  * garbage collector.  In this case the value of the GCHandle will be
  * set to zero.
  * 
- * If @pinned is false the address of the object can not be obtained, if it is
- * true the address of the object can be obtained.  This will also pin the
- * object so it will not be possible by a moving garbage collector to move the
- * object. 
+ * If @track_resurrection is TRUE the object will be tracked through
+ * finalization and if the object is resurrected during the execution
+ * of the finalizer, then the returned weakref will continue to hold
+ * a reference to the object.   If @track_resurrection is FALSE, then
+ * the weak reference's target will become NULL as soon as the object
+ * is passed on to the finalizer.
  * 
  * Returns: a handle that can be used to access the object from
  * unmanaged code.
@@ -427,18 +438,21 @@ retry:
  * mono_gchandle_get_target:
  * @gchandle: a GCHandle's handle.
  *
- * The handle was previously created by calling mono_gchandle_new or
- * mono_gchandle_new_weakref
+ * The handle was previously created by calling `mono_gchandle_new` or
+ * `mono_gchandle_new_weakref`
  *
- * Returns a pointer to the MonoObject represented by the handle or
+ * Returns a pointer to the `MonoObject*` represented by the handle or
  * NULL for a collected object if using a weakref handle.
  */
 GCObject*
 mono_gchandle_get_target (guint32 gchandle)
 {
        guint index = MONO_GC_HANDLE_SLOT (gchandle);
-       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
        HandleData *handles = gc_handles_for_type (type);
+       /* Invalid handles are possible; accessing one should produce NULL. (#34276) */
+       if (!handles)
+               return NULL;
        guint bucket, offset;
        g_assert (index < handles->capacity);
        bucketize (index, &bucket, &offset);
@@ -449,8 +463,10 @@ void
 sgen_gchandle_set_target (guint32 gchandle, GCObject *obj)
 {
        guint index = MONO_GC_HANDLE_SLOT (gchandle);
-       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
        HandleData *handles = gc_handles_for_type (type);
+       if (!handles)
+               return;
        guint bucket, offset;
        gpointer slot;
 
@@ -460,7 +476,7 @@ sgen_gchandle_set_target (guint32 gchandle, GCObject *obj)
        do {
                slot = handles->entries [bucket] [offset];
                SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (slot), "Why are we setting the target on an unoccupied slot?");
-       } while (!try_set_slot (&handles->entries [bucket] [offset], obj, slot, handles->type));
+       } while (!try_set_slot (&handles->entries [bucket] [offset], obj, slot, (GCHandleType)handles->type));
 }
 
 static gpointer
@@ -473,7 +489,7 @@ retry:
        if (!MONO_GC_HANDLE_OCCUPIED (slot))
                return NULL;
        if (MONO_GC_HANDLE_IS_OBJECT_POINTER (slot)) {
-               GCObject *obj = MONO_GC_REVEAL_POINTER (slot, is_weak);
+               GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (slot, is_weak);
                /* See note [dummy use]. */
                sgen_dummy_use (obj);
                /*
@@ -497,8 +513,10 @@ gpointer
 sgen_gchandle_get_metadata (guint32 gchandle)
 {
        guint index = MONO_GC_HANDLE_SLOT (gchandle);
-       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
        HandleData *handles = gc_handles_for_type (type);
+       if (!handles)
+               return NULL;
        guint bucket, offset;
        if (index >= handles->capacity)
                return NULL;
@@ -518,8 +536,10 @@ void
 mono_gchandle_free (guint32 gchandle)
 {
        guint index = MONO_GC_HANDLE_SLOT (gchandle);
-       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
        HandleData *handles = gc_handles_for_type (type);
+       if (!handles)
+               return;
        guint bucket, offset;
        gpointer slot;
        bucketize (index, &bucket, &offset);
@@ -548,7 +568,7 @@ null_link_if_necessary (gpointer hidden, GCHandleType handle_type, int max_gener
        if (!MONO_GC_HANDLE_VALID (hidden))
                return hidden;
 
-       obj = MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
+       obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
        SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
 
        if (object_older_than (obj, max_generation))
@@ -589,7 +609,7 @@ null_link_if (gpointer hidden, GCHandleType handle_type, int max_generation, gpo
        if (!MONO_GC_HANDLE_VALID (hidden))
                return hidden;
 
-       obj = MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
+       obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
        SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
 
        if (object_older_than (obj, max_generation))