[coop] Introduce new runtime handle API
authorLudovic Henry <ludovic@xamarin.com>
Fri, 13 Nov 2015 16:01:41 +0000 (16:01 +0000)
committerAleksey Kliger <aleksey@xamarin.com>
Fri, 18 Dec 2015 16:21:45 +0000 (11:21 -0500)
mono/metadata/Makefile.am
mono/metadata/handle-private.h [new file with mode: 0644]
mono/metadata/handle.c [new file with mode: 0644]
mono/metadata/handle.h [new file with mode: 0644]
mono/metadata/mono-gc.h
mono/metadata/object-internals.h
mono/metadata/threads.c
mono/unit-tests/.gitignore
mono/unit-tests/Makefile.am
mono/unit-tests/test-mono-handle.c [new file with mode: 0644]

index fde85f255d772e42c81b1dc581cac227f9255cd6..a0d7c20748e1601b1e384f22cb8a78f544beb16d 100644 (file)
@@ -214,7 +214,10 @@ common_sources = \
        abi-details.h   \
        metadata-cross-helpers.c        \
        seq-points-data.h       \
-       seq-points-data.c
+       seq-points-data.c       \
+       handle.c        \
+       handle.h        \
+       handle-private.h
 
 
 # These source files have compile time dependencies on GC code
diff --git a/mono/metadata/handle-private.h b/mono/metadata/handle-private.h
new file mode 100644 (file)
index 0000000..de52a33
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __MONO_HANDLE_PRIVATE_H__
+#define __MONO_HANDLE_PRIVATE_H__
+
+#include <mono/metadata/handle.h>
+
+MonoHandle
+mono_handle_arena_new (MonoHandleArena *arena, MonoObject *obj);
+
+MonoHandle
+mono_handle_arena_elevate (MonoHandleArena *arena, MonoHandle handle);
+
+void
+mono_handle_arena_stack_push (MonoHandleArena **arena_stack, MonoHandleArena *arena, gsize nb_handles);
+
+void
+mono_handle_arena_stack_pop (MonoHandleArena **arena_stack, MonoHandleArena *arena, gsize nb_handles);
+
+void
+mono_handle_arena_initialize (MonoHandleArena **arena_stack);
+
+void
+mono_handle_arena_deinitialize (MonoHandleArena **arena_stack);
+
+#endif/*__MONO_HANDLE_PRIVATE_H__*/
diff --git a/mono/metadata/handle.c b/mono/metadata/handle.c
new file mode 100644 (file)
index 0000000..10c573a
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * handle.c: Handle to object in native code
+ *
+ * Authors:
+ *  - Ludovic Henry <ludovic@xamarin.com>
+ *
+ * Copyright 2015 Xamarin, Inc. (www.xamarin.com)
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include <mono/metadata/handle.h>
+#include <mono/metadata/handle-private.h>
+#include <mono/metadata/object-internals.h>
+#include <mono/metadata/gc-internals.h>
+#include <mono/utils/atomic.h>
+#include <mono/utils/mono-lazy-init.h>
+
+#define HANDLES_PER_CHUNK (16 - 3)
+
+typedef struct _MonoHandleArenaChunk MonoHandleArenaChunk;
+struct _MonoHandleArenaChunk {
+       MonoHandleArenaChunk *next;
+       gsize handles_capacity;
+       gsize handles_size;
+       MonoHandleStorage handles [MONO_ZERO_LEN_ARRAY];
+};
+
+struct _MonoHandleArena {
+       MonoHandleArenaChunk *chunk;
+       MonoHandleArenaChunk *chunk_last;
+       MonoHandleArena *prev;
+};
+
+static mono_lazy_init_t arena_status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
+
+#ifdef HAVE_SGEN_GC
+static MonoGCDescriptor arena_desc = MONO_GC_DESCRIPTOR_NULL;
+#endif
+
+static MonoHandleArenaChunk *chunk_free_list = NULL;
+
+static inline MonoHandleArenaChunk*
+chunk_alloc (void)
+{
+       MonoHandleArenaChunk *old, *new;
+
+       do {
+               old = chunk_free_list;
+               if (!old) {
+                       MonoHandleArenaChunk *chunk;
+
+                       chunk = g_malloc0 (sizeof (MonoHandleArenaChunk) + sizeof (MonoHandleStorage) * (HANDLES_PER_CHUNK - MONO_ZERO_LEN_ARRAY));
+                       chunk->handles_capacity = HANDLES_PER_CHUNK;
+
+                       return chunk;
+               }
+
+               new = old->next;
+       } while (InterlockedCompareExchangePointer ((gpointer*) &chunk_free_list, new, old) != old);
+
+       memset (old, 0, sizeof (MonoHandleArenaChunk));
+       return old;
+}
+
+static inline void
+chunk_free (MonoHandleArenaChunk *chunk)
+{
+       do {
+               chunk->next = chunk_free_list;
+       } while (InterlockedCompareExchangePointer ((gpointer*) &chunk_free_list, chunk, chunk->next) != chunk->next);
+}
+
+static MonoHandle
+handle_new (MonoHandleArena *arena, MonoObject *obj)
+{
+       MonoHandleArenaChunk *chunk;
+
+       g_assert (arena->chunk);
+       g_assert (arena->chunk_last);
+
+       chunk = arena->chunk_last;
+
+       if (chunk->handles_size < chunk->handles_capacity) {
+               chunk->handles [chunk->handles_size].obj = obj;
+               chunk->handles_size += 1;
+
+               return &chunk->handles [chunk->handles_size - 1];
+       }
+
+       chunk = chunk->next = chunk_alloc ();
+
+       chunk->handles [0].obj = obj;
+       chunk->handles_size = 1;
+
+       arena->chunk_last = chunk;
+
+       return &chunk->handles [0];
+}
+
+MonoHandle
+mono_handle_arena_new (MonoHandleArena *arena, MonoObject *obj)
+{
+       g_assert (arena);
+       return handle_new (arena, obj);
+}
+
+MonoHandle
+mono_handle_new (MonoObject *obj)
+{
+       /* TODO: finish implementation by placing an arena somewhere
+        * in the current thread */
+       g_assert_not_reached ();
+       MonoHandleArena *arena = NULL;
+       return mono_handle_arena_new (arena, obj);
+}
+
+/*
+ * Elevate the handle to the parent arena
+ */
+MonoHandle
+mono_handle_arena_elevate (MonoHandleArena *arena, MonoHandle handle)
+{
+       g_assert (handle);
+       g_assert (arena);
+       g_assert (arena->prev);
+
+       return handle_new (arena->prev, handle->obj);
+}
+
+MonoHandle
+mono_handle_elevate (MonoHandle handle)
+{
+       /* TODO: finish implementation by placing an arena somewhere
+        * in the current thread */
+       g_assert_not_reached ();
+       MonoHandleArena *arena = NULL;
+       return mono_handle_arena_elevate (arena, handle);
+}
+
+gsize
+mono_handle_arena_size (gsize nb_handles)
+{
+       g_assert (nb_handles > 0);
+       return sizeof (MonoHandleArena) + sizeof (MonoHandleArenaChunk) + sizeof (MonoHandle) * (MAX (nb_handles, HANDLES_PER_CHUNK) - MONO_ZERO_LEN_ARRAY);
+}
+
+void
+mono_handle_arena_stack_push(MonoHandleArena **arena_stack, MonoHandleArena *arena, gsize nb_handles)
+{
+       g_assert (arena_stack);
+       g_assert (arena);
+
+       arena->prev = *arena_stack;
+       arena->chunk = arena->chunk_last = (MonoHandleArenaChunk*) (((char*) arena) + sizeof (MonoHandleArena));
+
+       arena->chunk->next = NULL;
+       arena->chunk->handles_capacity = MAX (nb_handles, HANDLES_PER_CHUNK);
+       arena->chunk->handles_size = 0;
+       memset (&arena->chunk->handles [0], 0, sizeof (MonoHandle) * arena->chunk->handles_capacity);
+
+       *arena_stack = arena;
+}
+
+void
+mono_handle_arena_stack_pop(MonoHandleArena **arena_stack, MonoHandleArena *arena, gsize nb_handles)
+{
+       MonoHandleArenaChunk *chunk, *next;
+
+       g_assert (arena);
+       g_assert (arena->chunk);
+       g_assert (arena->chunk_last);
+       g_assert (arena_stack);
+
+       *arena_stack = arena->prev;
+
+       for (chunk = arena->chunk; chunk; chunk = next) {
+               next = chunk->next;
+               memset (&chunk->handles [0], 0, sizeof (MonoHandle) * chunk->handles_capacity);
+               if (chunk != arena->chunk)
+                       chunk_free (chunk);
+       }
+}
+
+static void
+arena_scan (gpointer addr, MonoGCMarkFunc mark_func, gpointer gc_data)
+{
+       MonoHandleArena *arena;
+       MonoHandleArenaChunk *chunk;
+       int i;
+
+       for (arena = *(MonoHandleArena**) addr; arena; arena = arena->prev) {
+               for (chunk = arena->chunk; chunk; chunk = chunk->next) {
+                       for (i = 0; i < chunk->handles_size; ++i) {
+                               if (chunk->handles [i].obj != NULL)
+                                       mark_func (&chunk->handles [i].obj, gc_data);
+                       }
+               }
+       }
+}
+
+static void
+initialize (void)
+{
+#ifdef HAVE_SGEN_GC
+       arena_desc = mono_gc_make_root_descr_user (arena_scan);
+#endif
+}
+
+void
+mono_handle_arena_initialize (MonoHandleArena **arena_stack)
+{
+#ifdef HAVE_SGEN_GC
+       mono_lazy_initialize (&arena_status, initialize);
+       mono_gc_register_root ((char*) arena_stack, sizeof (MonoHandleArena*), arena_desc, MONO_ROOT_SOURCE_HANDLE, "runtime threads handle arena");
+#endif
+}
+
+void
+mono_handle_arena_deinitialize (MonoHandleArena **arena_stack)
+{
+#ifdef HAVE_SGEN_GC
+       mono_gc_deregister_root ((char*) arena_stack);
+#endif
+}
+
diff --git a/mono/metadata/handle.h b/mono/metadata/handle.h
new file mode 100644 (file)
index 0000000..7f80397
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * handle.h: Handle to object in native code
+ *
+ * Authors:
+ *  - Ludovic Henry <ludovic@xamarin.com>
+ *
+ * Copyright 2015 Xamarin, Inc. (www.xamarin.com)
+ */
+
+#ifndef __MONO_HANDLE_H__
+#define __MONO_HANDLE_H__
+
+#include <config.h>
+#include <glib.h>
+
+#include "object.h"
+#include "class.h"
+#include "class-internals.h"
+#include "threads-types.h"
+
+#include "mono/utils/mono-threads-coop.h"
+
+G_BEGIN_DECLS
+
+typedef struct _MonoHandleStorage MonoHandleStorage;
+typedef MonoHandleStorage* MonoHandle;
+
+/*
+ * DO NOT ACCESS DIRECTLY
+ * USE mono_handle_obj BELOW TO ACCESS OBJ
+ *
+ * The field obj is not private as there is no way to do that
+ * in C, but using a C++ template would simplify that a lot
+ */
+struct _MonoHandleStorage {
+       MonoObject *obj;
+};
+
+#ifndef CHECKED_BUILD
+
+#define mono_handle_obj(handle) ((handle)->obj)
+
+#define mono_handle_assign(handle,rawptr) do { (handle)->obj = (rawptr); } while(0)
+
+#else
+
+static inline void
+mono_handle_check_in_critical_section (const gchar* file, const gint lineno)
+{
+       MonoThreadInfo *info = (MonoThreadInfo*) mono_thread_info_current_unchecked ();
+       if (info && !info->inside_critical_region)
+               g_error ("Assertion at %s:%d: mono_handle_check_in_critical_section failed", file, lineno);
+}
+
+#define mono_handle_obj(handle) (mono_handle_check_in_critical_section (__FILE__, __LINE__), (handle)->obj)
+
+#define mono_handle_assign(handle,rawptr) do { mono_handle_check_in_critical_section (__FILE__, __LINE__); (handle)->obj = (rawptr); } while (0)
+
+#endif
+
+static inline MonoClass*
+mono_handle_class (MonoHandle handle)
+{
+       return handle->obj->vtable->klass;
+}
+
+static inline MonoDomain*
+mono_handle_domain (MonoHandle handle)
+{
+       return handle->obj->vtable->domain;
+}
+
+#define MONO_HANDLE_TYPE_DECL(type)      typedef struct { type *obj; } type ## HandleStorage ; \
+       typedef type ## HandleStorage * type ## Handle
+#define MONO_HANDLE_TYPE(type)           type ## Handle
+#define MONO_HANDLE_NEW(type,obj)        ((type ## Handle) mono_handle_new ((MonoObject*) (obj)))
+#define MONO_HANDLE_ELEVATE(type,handle) ((type ## Handle) mono_handle_elevate ((MonoObject*) (handle)->obj))
+
+#define MONO_HANDLE_ASSIGN(handle,rawptr)      \
+       do {    \
+               mono_handle_assign ((handle), (rawptr));        \
+       } while (0)
+
+#define MONO_HANDLE_SETREF(handle,fieldname,value)                     \
+       do {                                                            \
+               MonoHandle __value = (MonoHandle) (value);              \
+               MONO_PREPARE_CRITICAL;                                  \
+               MONO_OBJECT_SETREF (mono_handle_obj ((handle)), fieldname, mono_handle_obj (__value)); \
+               MONO_FINISH_CRITICAL;                                   \
+       } while (0)
+
+#define MONO_HANDLE_SET(handle,fieldname,value)        \
+       do {    \
+               MONO_PREPARE_CRITICAL;  \
+               mono_handle_obj ((handle))->fieldname = (value);        \
+               MONO_FINISH_CRITICAL;   \
+       } while (0)
+
+#define MONO_HANDLE_ARRAY_SETREF(handle,index,value)                   \
+       do {                                                            \
+               MonoHandle __value = (MonoHandle) (value);              \
+               MONO_PREPARE_CRITICAL;                                  \
+               mono_array_setref (mono_handle_obj ((handle)), (index), mono_handle_obj (__value)); \
+               MONO_FINISH_CRITICAL;                                   \
+       } while (0)
+
+#define MONO_HANDLE_ARRAY_SET(handle,type,index,value) \
+       do {    \
+               MONO_PREPARE_CRITICAL;  \
+               mono_array_set (mono_handle_obj ((handle)), (type), (index), (value));  \
+               MONO_FINISH_CRITICAL;   \
+       } while (0)
+
+/* handle arena specific functions */
+
+typedef struct _MonoHandleArena MonoHandleArena;
+
+gsize
+mono_handle_arena_size (gsize nb_handles);
+
+MonoHandle
+mono_handle_new (MonoObject *rawptr);
+
+MonoHandle
+mono_handle_elevate (MonoHandle handle);
+
+/* Some common handle types */
+
+MONO_HANDLE_TYPE_DECL (MonoArray);
+MONO_HANDLE_TYPE_DECL (MonoString);
+
+G_END_DECLS
+
+#endif /* __MONO_HANDLE_H__ */
index 24ffda7730a2ecb67c1a158f542a768e6eead89d..e1455148696b130e5e21dffd5c0d4eaa56ad9815 100644 (file)
@@ -40,6 +40,8 @@ typedef enum {
        MONO_ROOT_SOURCE_THREAD_POOL = 12,
        // Roots in the debugger agent.
        MONO_ROOT_SOURCE_DEBUGGER = 13,
+       // Handle structures, used for object passed to internal functions
+       MONO_ROOT_SOURCE_HANDLE = 14,
 } MonoGCRootSource;
 
 MONO_API void   mono_gc_collect         (int generation);
index d36dfc7207252e4e3651a05b4eab4f09b1165523..a2d384eebf49a5e80f0314f4abe42e5e3f0a35f9 100644 (file)
@@ -7,6 +7,7 @@
 #include <mono/metadata/mempool.h>
 #include <mono/metadata/class-internals.h>
 #include <mono/metadata/threads-types.h>
+#include <mono/metadata/handle.h>
 #include <mono/io-layer/io-layer.h>
 #include "mono/utils/mono-compiler.h"
 #include "mono/utils/mono-error.h"
index 9e7e8ff34afb56be69e98da5a2b6e534c1fe04fc..75bdcb3d0e5dae3e3e04b65baa9f08964d7a1b6e 100644 (file)
@@ -476,6 +476,7 @@ static void thread_cleanup (MonoInternalThread *thread)
                MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
                thread->thread_pinning_ref = NULL;
        }
+
 }
 
 /*
index ce9958e5946a0b393e375257ecea4a24e9596c25..1f5427ed6980b31a474bddf927d3e934817b748a 100644 (file)
@@ -9,3 +9,4 @@
 /test-mono-linked-list-set
 /test-sgen-qsort
 /test-conc-hashtable
+/test-mono-handle
\ No newline at end of file
index 10e0da01b2e9a24119c0b45f6eef17afe506e58d..9db0e0c98230823e073ea57b46454c647c01272f 100644 (file)
@@ -35,9 +35,14 @@ test_conc_hashtable_CFLAGS = $(test_cflags)
 test_conc_hashtable_LDADD = $(test_ldadd)
 test_conc_hashtable_LDFLAGS = $(test_ldflags)
 
-noinst_PROGRAMS = test-sgen-qsort test-memfuncs test-mono-linked-list-set test-conc-hashtable
+test_mono_handle_SOURCES = test-mono-handle.c
+test_mono_handle_CFLAGS = $(test_cflags)
+test_mono_handle_LDADD = $(test_ldadd)
+test_mono_handle_LDFLAGS = $(test_ldflags)
 
-TESTS = test-sgen-qsort test-memfuncs test-mono-linked-list-set test-conc-hashtable
+noinst_PROGRAMS = test-sgen-qsort test-memfuncs test-mono-linked-list-set test-conc-hashtable test-mono-handle
+
+TESTS = test-sgen-qsort test-memfuncs test-mono-linked-list-set test-conc-hashtable test-mono-handle
 
 .NOTPARALLEL:
 
diff --git a/mono/unit-tests/test-mono-handle.c b/mono/unit-tests/test-mono-handle.c
new file mode 100644 (file)
index 0000000..db620f1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * test-mono-handle: tests for MonoHandle and MonoHandleArena
+ *
+ * Authors:
+ *   Aleksey Kliger <aleksey@xamarin.com>
+ *
+ * Copyright 2015 Xamarin, Inc. (www.xamarin.com)
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <mono/metadata/handle.h>
+#include <mono/metadata/handle-private.h>
+
+static void
+test1_arena_size ()
+{
+       for (gsize i = 1; i < 10; ++i) {
+               gsize sz = mono_handle_arena_size(i);
+               g_assert(sz >= i*sizeof(MonoHandle));
+       }
+}
+
+static void
+test2_arena_push_pop ()
+{
+       MonoHandleArena *top = NULL;
+
+       const int n_handles = 3;
+       MonoHandleArena *new_arena1 = g_malloc0 (mono_handle_arena_size (n_handles));
+       mono_handle_arena_stack_push (&top, new_arena1, n_handles);
+
+       MonoHandleArena *new_arena2 = g_malloc0 (mono_handle_arena_size (n_handles));
+
+       mono_handle_arena_stack_push (&top, new_arena2, n_handles);
+
+       g_assert (top == new_arena2);
+
+       mono_handle_arena_stack_pop (&top, new_arena2, n_handles);
+
+       g_free (new_arena2);
+
+       g_assert (top == new_arena1);
+
+       mono_handle_arena_stack_pop (&top, new_arena1, n_handles);
+
+       g_assert (top == NULL);
+       
+       g_free (new_arena1);
+}
+
+
+
+int
+main (int argc, const char* argv[])
+{
+       test1_arena_size ();
+       
+       test2_arena_push_pop ();
+
+       return 0;
+}