Merge pull request #2453 from kumpera/fix_ji_free
[mono.git] / mono / metadata / threads.c
index 9e7e8ff34afb56be69e98da5a2b6e534c1fe04fc..9ea5120c3f975dc57a5f8e712394d369fdbf89ad 100644 (file)
@@ -149,6 +149,9 @@ static MonoGHashTable *threads=NULL;
  */
 static GHashTable *contexts = NULL;
 
+/* Cleanup queue for contexts. */
+static MonoReferenceQueue *context_queue;
+
 /*
  * Threads which are starting up and they are not in the 'threads' hash yet.
  * When handle_store is called for a thread, it will be removed from this hash table.
@@ -205,7 +208,7 @@ static void abort_thread_internal (MonoInternalThread *thread, gboolean can_rais
 static void suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt);
 static void self_suspend_internal (MonoInternalThread *thread);
 
-static MonoException* mono_thread_execute_interruption ();
+static MonoException* mono_thread_execute_interruption (void);
 static void ref_stack_destroy (gpointer rs);
 
 /* Spin lock for InterlockedXXX 64 bit functions */
@@ -476,6 +479,7 @@ static void thread_cleanup (MonoInternalThread *thread)
                MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
                thread->thread_pinning_ref = NULL;
        }
+
 }
 
 /*
@@ -566,8 +570,11 @@ set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, M
 static MonoThread*
 create_thread_object (MonoDomain *domain)
 {
+       MonoError error;
        MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
-       return (MonoThread*)mono_gc_alloc_mature (vt);
+       MonoThread *t = (MonoThread*)mono_object_new_mature (vt, &error);
+       mono_error_raise_exception (&error); /* FIXME don't raise here */
+       return t;
 }
 
 static MonoThread*
@@ -581,11 +588,13 @@ new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
 static MonoInternalThread*
 create_internal_thread (void)
 {
+       MonoError error;
        MonoInternalThread *thread;
        MonoVTable *vt;
 
        vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class);
-       thread = (MonoInternalThread*)mono_gc_alloc_mature (vt);
+       thread = (MonoInternalThread*)mono_object_new_mature (vt, &error);
+       mono_error_raise_exception (&error); /* FIXME don't raise here */
 
        thread->synch_cs = g_new0 (MonoCoopMutex, 1);
        mono_coop_mutex_init_recursive (thread->synch_cs);
@@ -1296,15 +1305,40 @@ ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj
        mono_thread_set_name_internal (this_obj, name, TRUE);
 }
 
+/*
+ * ves_icall_System_Threading_Thread_GetPriority_internal:
+ * @param this_obj: The MonoInternalThread on which to operate.
+ *
+ * Gets the priority of the given thread.
+ * @return: The priority of the given thread.
+ */
 int
 ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj)
 {
-       return ThreadPriority_Lowest;
+       gint32 priority;
+       MonoInternalThread *internal = this_obj->internal_thread;
+
+       LOCK_THREAD (internal);
+       priority = GetThreadPriority (internal->handle) + 2;
+       UNLOCK_THREAD (internal);
+       return priority;
 }
 
+/* 
+ * ves_icall_System_Threading_Thread_SetPriority_internal:
+ * @param this_obj: The MonoInternalThread on which to operate.
+ * @param priority: The priority to set.
+ *
+ * Sets the priority of the given thread.
+ */
 void
 ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
 {
+       MonoInternalThread *internal = this_obj->internal_thread;
+
+       LOCK_THREAD (internal);
+       SetThreadPriority (internal->handle, priority - 2);
+       UNLOCK_THREAD (internal);
 }
 
 /* If the array is already in the requested domain, we just return it,
@@ -2317,64 +2351,64 @@ void mono_thread_stop (MonoThread *thread)
 gint8
 ves_icall_System_Threading_Thread_VolatileRead1 (void *ptr)
 {
-       gint8 tmp;
-       mono_atomic_load_acquire (tmp, gint8, (volatile gint8 *) ptr);
+       gint8 tmp = *(volatile gint8 *)ptr;
+       mono_memory_barrier ();
        return tmp;
 }
 
 gint16
 ves_icall_System_Threading_Thread_VolatileRead2 (void *ptr)
 {
-       gint16 tmp;
-       mono_atomic_load_acquire (tmp, gint16, (volatile gint16 *) ptr);
+       gint16 tmp = *(volatile gint16 *)ptr;
+       mono_memory_barrier ();
        return tmp;
 }
 
 gint32
 ves_icall_System_Threading_Thread_VolatileRead4 (void *ptr)
 {
-       gint32 tmp;
-       mono_atomic_load_acquire (tmp, gint32, (volatile gint32 *) ptr);
+       gint32 tmp = *(volatile gint32 *)ptr;
+       mono_memory_barrier ();
        return tmp;
 }
 
 gint64
 ves_icall_System_Threading_Thread_VolatileRead8 (void *ptr)
 {
-       gint64 tmp;
-       mono_atomic_load_acquire (tmp, gint64, (volatile gint64 *) ptr);
+       gint64 tmp = *(volatile gint64 *)ptr;
+       mono_memory_barrier ();
        return tmp;
 }
 
 void *
 ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr)
 {
-       volatile void *tmp;
-       mono_atomic_load_acquire (tmp, volatile void *, (volatile void **) ptr);
+       volatile void *tmp = *(volatile void **)ptr;
+       mono_memory_barrier ();
        return (void *) tmp;
 }
 
 void *
 ves_icall_System_Threading_Thread_VolatileReadObject (void *ptr)
 {
-       volatile MonoObject *tmp;
-       mono_atomic_load_acquire (tmp, volatile MonoObject *, (volatile MonoObject **) ptr);
+       volatile MonoObject *tmp = *(volatile MonoObject **)ptr;
+       mono_memory_barrier ();
        return (MonoObject *) tmp;
 }
 
 double
 ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr)
 {
-       double tmp;
-       mono_atomic_load_acquire (tmp, double, (volatile double *) ptr);
+       double tmp = *(volatile double *)ptr;
+       mono_memory_barrier ();
        return tmp;
 }
 
 float
 ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr)
 {
-       float tmp;
-       mono_atomic_load_acquire (tmp, float, (volatile float *) ptr);
+       float tmp = *(volatile float *)ptr;
+       mono_memory_barrier ();
        return tmp;
 }
 
@@ -2456,49 +2490,57 @@ ves_icall_System_Threading_Volatile_Read_T (void *ptr)
 void
 ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value)
 {
-       mono_atomic_store_release ((volatile gint8 *) ptr, value);
+       mono_memory_barrier ();
+       *(volatile gint8 *)ptr = value;
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWrite2 (void *ptr, gint16 value)
 {
-       mono_atomic_store_release ((volatile gint16 *) ptr, value);
+       mono_memory_barrier ();
+       *(volatile gint16 *)ptr = value;
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWrite4 (void *ptr, gint32 value)
 {
-       mono_atomic_store_release ((volatile gint32 *) ptr, value);
+       mono_memory_barrier ();
+       *(volatile gint32 *)ptr = value;
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWrite8 (void *ptr, gint64 value)
 {
-       mono_atomic_store_release ((volatile gint64 *) ptr, value);
+       mono_memory_barrier ();
+       *(volatile gint64 *)ptr = value;
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWriteIntPtr (void *ptr, void *value)
 {
-       mono_atomic_store_release ((volatile void **) ptr, value);
+       mono_memory_barrier ();
+       *(volatile void **)ptr = value;
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, MonoObject *value)
 {
-       mono_gc_wbarrier_generic_store_atomic (ptr, value);
+       mono_memory_barrier ();
+       mono_gc_wbarrier_generic_store (ptr, value);
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value)
 {
-       mono_atomic_store_release ((volatile double *) ptr, value);
+       mono_memory_barrier ();
+       *(volatile double *)ptr = value;
 }
 
 void
 ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value)
 {
-       mono_atomic_store_release ((volatile float *) ptr, value);
+       mono_memory_barrier ();
+       *(volatile float *)ptr = value;
 }
 
 void
@@ -2575,6 +2617,31 @@ ves_icall_System_Threading_Volatile_Write_T (void *ptr, MonoObject *value)
        mono_gc_wbarrier_generic_store_atomic (ptr, value);
 }
 
+static void
+free_context (void *user_data)
+{
+       ContextStaticData *data = user_data;
+
+       mono_threads_lock ();
+
+       /*
+        * There is no guarantee that, by the point this reference queue callback
+        * has been invoked, the GC handle associated with the object will fail to
+        * resolve as one might expect. So if we don't free and remove the GC
+        * handle here, free_context_static_data_helper () could end up resolving
+        * a GC handle to an actually-dead context which would contain a pointer
+        * to an already-freed static data segment, resulting in a crash when
+        * accessing it.
+        */
+       g_hash_table_remove (contexts, GUINT_TO_POINTER (data->gc_handle));
+
+       mono_threads_unlock ();
+
+       mono_gchandle_free (data->gc_handle);
+       mono_free_static_data (data->static_data);
+       g_free (data);
+}
+
 void
 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
 {
@@ -2585,10 +2652,24 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppConte
        if (!contexts)
                contexts = g_hash_table_new (NULL, NULL);
 
-       context_adjust_static_data (ctx);
+       if (!context_queue)
+               context_queue = mono_gc_reference_queue_new (free_context);
+
        gpointer gch = GUINT_TO_POINTER (mono_gchandle_new_weakref (&ctx->obj, FALSE));
        g_hash_table_insert (contexts, gch, gch);
 
+       /*
+        * We use this intermediate structure to contain a duplicate pointer to
+        * the static data because we can't rely on being able to resolve the GC
+        * handle in the reference queue callback.
+        */
+       ContextStaticData *data = g_new0 (ContextStaticData, 1);
+       data->gc_handle = GPOINTER_TO_UINT (gch);
+       ctx->data = data;
+
+       context_adjust_static_data (ctx);
+       mono_gc_reference_queue_add (context_queue, &ctx->obj, data);
+
        mono_threads_unlock ();
 
        mono_profiler_context_loaded (ctx);
@@ -2600,9 +2681,7 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContex
        /*
         * NOTE: Since finalizers are unreliable for the purposes of ensuring
         * cleanup in exceptional circumstances, we don't actually do any
-        * cleanup work here. We instead do this when we iterate the `contexts`
-        * hash table. The only purpose of this finalizer, at the moment, is to
-        * notify the profiler.
+        * cleanup work here. We instead do this via a reference queue.
         */
 
        //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
@@ -3868,6 +3947,7 @@ context_adjust_static_data (MonoAppContext *ctx)
        if (context_static_info.offset || context_static_info.idx > 0) {
                guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
                mono_alloc_static_data (&ctx->static_data, offset, FALSE);
+               ctx->data->static_data = ctx->static_data;
        }
 }
 
@@ -3886,21 +3966,17 @@ alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
 /*
  * LOCKING: requires that threads_mutex is held
  */
-static gboolean
+static void
 alloc_context_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
-       uint32_t gch = GPOINTER_TO_INT (key);
-       MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
+       MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
 
-       if (!ctx) {
-               mono_gchandle_free (gch);
-               return TRUE; // Remove this key/value pair
-       }
+       if (!ctx)
+               return;
 
        guint32 offset = GPOINTER_TO_UINT (user);
        mono_alloc_static_data (&ctx->static_data, offset, FALSE);
-
-       return FALSE; // Don't remove it
+       ctx->data->static_data = ctx->static_data;
 }
 
 static StaticDataFreeList*
@@ -3992,7 +4068,7 @@ mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align
                        mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset));
        } else {
                if (contexts != NULL)
-                       g_hash_table_foreach_remove (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
+                       g_hash_table_foreach (contexts, alloc_context_static_data_helper, GUINT_TO_POINTER (offset));
 
                ACCESS_SPECIAL_STATIC_OFFSET (offset, type) = SPECIAL_STATIC_OFFSET_TYPE_CONTEXT;
        }
@@ -4046,16 +4122,13 @@ free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
 /*
  * LOCKING: requires that threads_mutex is held
  */
-static gboolean
+static void
 free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
 {
-       uint32_t gch = GPOINTER_TO_INT (key);
-       MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (gch);
+       MonoAppContext *ctx = (MonoAppContext *) mono_gchandle_get_target (GPOINTER_TO_INT (key));
 
-       if (!ctx) {
-               mono_gchandle_free (gch);
-               return TRUE; // Remove this key/value pair
-       }
+       if (!ctx)
+               return;
 
        OffsetSize *data = (OffsetSize *)user;
        int idx = ACCESS_SPECIAL_STATIC_OFFSET (data->offset, index);
@@ -4063,12 +4136,10 @@ free_context_static_data_helper (gpointer key, gpointer value, gpointer user)
        char *ptr;
 
        if (!ctx->static_data || !ctx->static_data [idx])
-               return FALSE; // Don't remove this key/value pair
+               return;
 
        ptr = ((char*) ctx->static_data [idx]) + off;
        mono_gc_bzero_atomic (ptr, data->size);
-
-       return FALSE; // Don't remove this key/value pair
 }
 
 static void
@@ -4097,7 +4168,7 @@ do_free_special_slot (guint32 offset, guint32 size)
                        mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data);
        } else {
                if (contexts != NULL)
-                       g_hash_table_foreach_remove (contexts, free_context_static_data_helper, &data);
+                       g_hash_table_foreach (contexts, free_context_static_data_helper, &data);
        }
 
        if (!mono_runtime_is_shutting_down ()) {