[utils] Add MonoRefCount to factor ref-counting mechanisms
authorLudovic Henry <ludovic@xamarin.com>
Thu, 17 Nov 2016 22:55:53 +0000 (17:55 -0500)
committerLudovic Henry <ludovic@xamarin.com>
Tue, 22 Nov 2016 15:23:57 +0000 (10:23 -0500)
mono/utils/Makefile.am
mono/utils/mono-threads.c
mono/utils/mono-threads.h
mono/utils/refcount.h [new file with mode: 0644]

index 35f218a8d0ac5cd4d0bd0ff650f69d1468ee6f14..20c9c45d01601f28f31cd352ec3be368b130d4ef 100644 (file)
@@ -173,7 +173,8 @@ monoutils_sources = \
        parse.h \
        checked-build.c \
        checked-build.h \
-       os-event.h
+       os-event.h \
+       refcount.h
 
 arch_sources = 
 
index 0b3f71b85d3587bb7650dcb4852100b6737951a4..cabfa0b3856281372e4c3c7083df0373edb0bb55 100644 (file)
@@ -338,6 +338,17 @@ mono_thread_info_register_small_id (void)
        return small_id;
 }
 
+static void
+thread_handle_destroy (gpointer data)
+{
+       MonoThreadHandle *thread_handle;
+
+       thread_handle = (MonoThreadHandle*) data;
+
+       mono_os_event_destroy (&thread_handle->event);
+       g_free (thread_handle);
+}
+
 static void*
 register_thread (MonoThreadInfo *info, gpointer baseptr)
 {
@@ -349,7 +360,7 @@ register_thread (MonoThreadInfo *info, gpointer baseptr)
        info->small_id = small_id;
 
        info->handle = g_new0 (MonoThreadHandle, 1);
-       info->handle->ref = 1;
+       mono_refcount_init (info->handle, thread_handle_destroy);
        mono_os_event_init (&info->handle->event, FALSE);
 
        mono_os_sem_init (&info->resume_semaphore, 0);
@@ -1090,14 +1101,24 @@ mono_thread_info_is_async_context (void)
 }
 
 typedef struct {
-       gint32 ref;
+       MonoRefCount ref;
        MonoThreadStart start_routine;
        gpointer start_routine_arg;
-       gint32 priority;
        MonoCoopSem registered;
        MonoThreadHandle *handle;
 } CreateThreadData;
 
+static void
+create_thread_data_destroy (gpointer data)
+{
+       CreateThreadData *thread_data;
+
+       thread_data = (CreateThreadData*) data;
+
+       mono_coop_sem_destroy (&thread_data->registered);
+       g_free (thread_data);
+}
+
 static gsize WINAPI
 inner_start_thread (gpointer data)
 {
@@ -1121,10 +1142,7 @@ inner_start_thread (gpointer data)
 
        mono_coop_sem_post (&thread_data->registered);
 
-       if (InterlockedDecrement (&thread_data->ref) == 0) {
-               mono_coop_sem_destroy (&thread_data->registered);
-               g_free (thread_data);
-       }
+       mono_refcount_dec (thread_data);
 
        /* thread_data is not valid anymore */
        thread_data = NULL;
@@ -1151,15 +1169,15 @@ mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const s
        MonoThreadHandle *ret;
 
        thread_data = g_new0 (CreateThreadData, 1);
-       thread_data->ref = 2;
+       mono_refcount_init (thread_data, create_thread_data_destroy);
        thread_data->start_routine = start;
        thread_data->start_routine_arg = arg;
        mono_coop_sem_init (&thread_data->registered, 0);
 
-       res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, stack_size, out_tid);
+       res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
        if (res != 0) {
                /* ref is not going to be decremented in inner_start_thread */
-               InterlockedDecrement (&thread_data->ref);
+               mono_refcount_dec (thread_data);
                ret = NULL;
                goto done;
        }
@@ -1171,10 +1189,7 @@ mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const s
        g_assert (ret);
 
 done:
-       if (InterlockedDecrement (&thread_data->ref) == 0) {
-               mono_coop_sem_destroy (&thread_data->registered);
-               g_free (thread_data);
-       }
+       mono_refcount_dec (thread_data);
 
        return ret;
 }
@@ -1399,40 +1414,13 @@ mono_thread_info_exit (gsize exit_code)
 MonoThreadHandle*
 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
 {
-       guint32 oldref, newref;
-
-       g_assert (thread_handle);
-
-       do {
-               oldref = thread_handle->ref;
-               if (!(oldref >= 1))
-                       g_error ("%s: thread_handle %p has ref %u, it should be >= 1", __func__, thread_handle, oldref);
-
-               newref = oldref + 1;
-       } while (InterlockedCompareExchange ((gint32*) &thread_handle->ref, newref, oldref) != oldref);
-
-       return thread_handle;
+       return mono_refcount_inc (thread_handle);
 }
 
 void
 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
 {
-       guint32 oldref, newref;
-
-       g_assert (thread_handle);
-
-       do {
-               oldref = thread_handle->ref;
-               if (!(oldref >= 1))
-                       g_error ("%s: thread_handle %p has ref %u, it should be >= 1", __func__, thread_handle, oldref);
-
-               newref = oldref - 1;
-       } while (InterlockedCompareExchange ((gint32*) &thread_handle->ref, newref, oldref) != oldref);
-
-       if (newref == 0) {
-               mono_os_event_destroy (&thread_handle->event);
-               g_free (thread_handle);
-       }
+       mono_refcount_dec (thread_handle);
 }
 
 static void
index b076433999ba43c501b23a0d934abe798cd0f64a..b9ce4f7f43ad500d027678304341230db9218b57 100644 (file)
@@ -16,6 +16,7 @@
 #include <mono/utils/mono-tls.h>
 #include <mono/utils/mono-coop-semaphore.h>
 #include <mono/utils/os-event.h>
+#include <mono/utils/refcount.h>
 
 #include <mono/io-layer/io-layer.h>
 
@@ -66,7 +67,7 @@ typedef gsize (*MonoThreadStart)(gpointer);
 #endif /* #ifdef HOST_WIN32 */
 
 typedef struct {
-       guint32 ref;
+       MonoRefCount ref;
        MonoOSEvent event;
 } MonoThreadHandle;
 
diff --git a/mono/utils/refcount.h b/mono/utils/refcount.h
new file mode 100644 (file)
index 0000000..b3a1dcf
--- /dev/null
@@ -0,0 +1,70 @@
+
+#ifndef __MONO_UTILS_REFCOUNT_H__
+#define __MONO_UTILS_REFCOUNT_H__
+
+#include <glib.h>
+#include <config.h>
+
+#include "atomic.h"
+
+/*
+ * Mechanism for ref-counting which tries to be as user-friendly as possible. Instead of being a wrapper around
+ * user-provided data, it is embedded into the user data.
+ *
+ * This introduces some constraints on the MonoRefCount field:
+ *  - it needs to be called "ref"
+ *  - it cannot be a pointer
+ */
+
+typedef struct {
+       guint32 ref;
+       void (*destructor) (gpointer data);
+} MonoRefCount;
+
+#define mono_refcount_init(v,destructor) do { mono_refcount_initialize (&(v)->ref, (destructor)); } while (0)
+#define mono_refcount_inc(v) (mono_refcount_increment (&(v)->ref),(v))
+#define mono_refcount_dec(v) do { mono_refcount_decrement (&(v)->ref); } while (0)
+
+static inline void
+mono_refcount_initialize (MonoRefCount *refcount, void (*destructor) (gpointer data))
+{
+       refcount->ref = 1;
+       refcount->destructor = destructor;
+}
+
+static inline void
+mono_refcount_increment (MonoRefCount *refcount)
+{
+       guint32 oldref, newref;
+
+       g_assert (refcount);
+
+       do {
+               oldref = refcount->ref;
+               if (oldref == 0)
+                       g_error ("%s: cannot increment a ref with value 0", __func__);
+
+               newref = oldref + 1;
+       } while (InterlockedCompareExchange ((gint32*) &refcount->ref, newref, oldref) != oldref);
+}
+
+static inline void
+mono_refcount_decrement (MonoRefCount *refcount)
+{
+       guint32 oldref, newref;
+
+       g_assert (refcount);
+
+       do {
+               oldref = refcount->ref;
+               if (oldref == 0)
+                       g_error ("%s: cannot decrement a ref with value 0", __func__);
+
+               newref = oldref - 1;
+       } while (InterlockedCompareExchange ((gint32*) &refcount->ref, newref, oldref) != oldref);
+
+       if (newref == 0 && refcount->destructor)
+               refcount->destructor ((gpointer) refcount);
+}
+
+#endif /* __MONO_UTILS_REFCOUNT_H__ */