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
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;
}
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;
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;
}
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);
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;