Merge pull request #3932 from lambdageek/dev-handles-outargs
[mono.git] / mono / metadata / handle.c
index 56b9b9353cd706fc6bab6b125dc0112f9b6a4f92..2465527e158e846009daa0ded3890858e2b6e908 100644 (file)
@@ -45,6 +45,29 @@ Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and frie
        We could then generate neat type safe wrappers.
 */
 
+/*
+ * NOTE: Async suspend
+ * 
+ * If we are running with cooperative GC, all the handle stack
+ * manipulation will complete before a GC thread scans the handle
+ * stack. If we are using async suspend, however, a thread may be
+ * trying to allocate a new handle, or unwind the handle stack when
+ * the GC stops the world.
+ *
+ * In particular, we need to ensure that if the mutator thread is
+ * suspended while manipulating the handle stack, the stack is in a
+ * good enough state to be scanned.  In particular, the size of each
+ * chunk should be updated before an object is written into the
+ * handle, and chunks to be scanned (between bottom and top) should
+ * always be valid.
+ *
+ * Note that the handle stack is scanned PRECISELY (see
+ * sgen_client_scan_thread_data ()).  That means there should not be
+ * stale objects scanned.  So when we manipulate the size of a chunk,
+ * wemust ensure that the newly scannable slot is either null or
+ * points to a valid value.
+ */
+
 const MonoObjectHandle mono_null_value_handle = NULL;
 
 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
@@ -59,13 +82,28 @@ mono_handle_new (MonoObject *object)
 
 retry:
        if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
-               MonoObject **h = &top->objects [top->size++];
+               int idx = top->size;
+               /* can be interrupted anywhere here, so:
+                * 1. make sure the new slot is null
+                * 2. make the new slot scannable (increment size)
+                * 3. put a valid object in there
+                *
+                * (have to do 1 then 3 so that if we're interrupted
+                * between 1 and 2, the object is still live)
+                */
+               top->objects [idx] = NULL;
+               mono_memory_write_barrier ();
+               top->size++;
+               mono_memory_write_barrier ();
+               MonoObject **h = &top->objects [idx];
                *h = object;
                return h;
        }
        if (G_LIKELY (top->next)) {
+               top->next->size = 0;
+               /* make sure size == 0 is visible to a GC thread before it sees the new top */
+               mono_memory_write_barrier ();
                top = top->next;
-               top->size = 0;
                handles->top = top;
                goto retry;
        }
@@ -73,6 +111,8 @@ retry:
        new_chunk->size = 0;
        new_chunk->prev = top;
        new_chunk->next = NULL;
+       /* make sure size == 0 before new chunk is visible */
+       mono_memory_write_barrier ();
        top->next = new_chunk;
        handles->top = new_chunk;
        goto retry;
@@ -86,9 +126,10 @@ mono_handle_stack_alloc (void)
        HandleStack *stack = g_new (HandleStack, 1);
        HandleChunk *chunk = g_new (HandleChunk, 1);
 
-       stack->top = stack->bottom = chunk;
        chunk->size = 0;
        chunk->prev = chunk->next = NULL;
+       mono_memory_write_barrier ();
+       stack->top = stack->bottom = chunk;
        return stack;
 }
 
@@ -98,6 +139,8 @@ mono_handle_stack_free (HandleStack *stack)
        if (!stack)
                return;
        HandleChunk *c = stack->bottom;
+       stack->top = stack->bottom = NULL;
+       mono_memory_write_barrier ();
        while (c) {
                HandleChunk *next = c->next;
                g_free (c);
@@ -110,6 +153,8 @@ mono_handle_stack_free (HandleStack *stack)
 void
 mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data)
 {
+       /* if we're running, we know the world is stopped.
+        */
        HandleChunk *cur = stack->bottom;
        HandleChunk *last = stack->top;
 
@@ -118,8 +163,10 @@ mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data)
 
        while (cur) {
                int i;
-               for (i = 0; i < cur->size; ++i)
-                       func ((gpointer*)&cur->objects [i], gc_data);
+               for (i = 0; i < cur->size; ++i) {
+                       if (cur->objects [i] != NULL)
+                               func ((gpointer*)&cur->objects [i], gc_data);
+               }
                if (cur == last)
                        break;
                cur = cur->next;
@@ -140,7 +187,7 @@ mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, c
        }
 
        if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES)
-               printf ("%s USED %d handles\n", func_name, size);
+               g_warning ("%s USED %d handles\n", func_name, size);
 }
 
 /*
@@ -157,9 +204,9 @@ mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, Mon
 /* Temporary place for some of the handle enabled wrapper functions*/
 
 MonoStringHandle
-mono_string_new_handle (MonoDomain *domain, const char *data)
+mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error)
 {
-       return MONO_HANDLE_NEW (MonoString, mono_string_new (domain, data));
+       return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error));
 }
 
 MonoArrayHandle
@@ -176,3 +223,32 @@ mono_handle_verify (MonoRawHandle raw_handle)
        
 }
 #endif
+
+uintptr_t
+mono_array_handle_length (MonoArrayHandle arr)
+{
+       MONO_REQ_GC_UNSAFE_MODE;
+
+       return MONO_HANDLE_RAW (arr)->max_length;
+}
+
+uint32_t
+mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned)
+{
+       return mono_gchandle_new (MONO_HANDLE_RAW(handle), pinned);
+}
+
+MonoObjectHandle
+mono_gchandle_get_target_handle (uint32_t gchandle)
+{
+       return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target);
+}
+
+gpointer
+mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, uint32_t *gchandle)
+{
+       g_assert (gchandle != NULL);
+       *gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE);
+       MonoArray *raw = MONO_HANDLE_RAW (handle);
+       return mono_array_addr_with_size (raw, size, idx);
+}