Merge pull request #1122 from PoppermostProductions/master
[mono.git] / mono / metadata / threads.c
index 16e3a086890446eb85c9634cd1868ed6e8407e29..0867b3dee8509793a824eb9bc44ed098c762db7e 100755 (executable)
@@ -242,6 +242,13 @@ mono_thread_get_tls_offset (void)
        return offset;
 }
 
+static inline MonoNativeThreadId
+thread_get_tid (MonoInternalThread *thread)
+{
+       /* We store the tid as a guint64 to keep the object layout constant between platforms */
+       return MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
+}
+
 /* handle_store() and handle_remove() manage the array of threads that
  * still need to be waited for when the main thread exits.
  *
@@ -385,10 +392,14 @@ static void thread_cleanup (MonoInternalThread *thread)
                        mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
        }
 
+       /*
+        * thread->synch_cs can be NULL if this was called after
+        * ves_icall_System_Threading_InternalThread_Thread_free_internal.
+        * This can happen only during shutdown.
+        * The shutting_down flag is not always set, so we can't assert on it.
+        */
        if (thread->synch_cs)
                LOCK_THREAD (thread);
-       else
-               g_assert (shutting_down);
 
        thread->state |= ThreadState_Stopped;
        thread->state &= ~ThreadState_Background;
@@ -638,6 +649,12 @@ static guint32 WINAPI start_wrapper_internal(void *data)
         */
        mono_profiler_thread_start (tid);
 
+       /* if the name was set before starting, we didn't invoke the profiler callback */
+       if (internal->name && (internal->flags & MONO_THREAD_FLAG_NAME_SET)) {
+               char *tname = g_utf16_to_utf8 (internal->name, -1, NULL, NULL, NULL);
+               mono_profiler_thread_name (internal->tid, tname);
+               g_free (tname);
+       }
        /* start_func is set only for unmanaged start functions */
        if (start_func) {
                start_func (start_arg);
@@ -665,6 +682,8 @@ static guint32 WINAPI start_wrapper_internal(void *data)
 
        thread_cleanup (internal);
 
+       internal->tid = 0;
+
        /* Remove the reference to the thread object in the TLS data,
         * so the thread object can be finalized.  This won't be
         * reached if the thread threw an uncaught exception, so those
@@ -702,6 +721,12 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *star
        MonoNativeThreadId tid;
        guint32 create_flags;
 
+       /*
+        * Join joinable threads to prevent running out of threads since the finalizer
+        * thread might be blocked/backlogged.
+        */
+       mono_threads_join_threads ();
+
        mono_threads_lock ();
        if (shutting_down) {
                g_free (start_info);
@@ -938,13 +963,13 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
 }
 
 void
-mono_thread_detach (MonoThread *thread)
+mono_thread_detach_internal (MonoInternalThread *thread)
 {
        g_return_if_fail (thread != NULL);
 
-       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->internal_thread->tid));
+       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
 
-       thread_cleanup (thread->internal_thread);
+       thread_cleanup (thread);
 
        SET_CURRENT_OBJECT (NULL);
        mono_domain_unset ();
@@ -955,6 +980,13 @@ mono_thread_detach (MonoThread *thread)
         */
 }
 
+void
+mono_thread_detach (MonoThread *thread)
+{
+       if (thread)
+               mono_thread_detach_internal (thread->internal_thread);
+}
+
 void
 mono_thread_exit ()
 {
@@ -1031,15 +1063,18 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this,
 }
 
 /*
- * This is called from the finalizer of the internal thread object. Since threads keep a reference to their
- * thread object while running, by the time this function is called, the thread has already exited/detached,
- * i.e. thread_cleanup () has ran.
+ * This is called from the finalizer of the internal thread object.
  */
 void
 ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this, HANDLE thread)
 {
        THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
 
+       /*
+        * Since threads keep a reference to their thread object while running, by the time this function is called,
+        * the thread has already exited/detached, i.e. thread_cleanup () has ran. The exception is during shutdown,
+        * when thread_cleanup () can be called after this.
+        */
        if (thread)
                CloseHandle (thread);
 
@@ -1173,9 +1208,10 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g
        
        UNLOCK_THREAD (this_obj);
 
-       if (this_obj->name) {
+       if (this_obj->name && this_obj->tid) {
                char *tname = mono_string_to_utf8 (name);
                mono_profiler_thread_name (this_obj->tid, tname);
+               mono_thread_info_set_name (thread_get_tid (this_obj), tname);
                mono_free (tname);
        }
 }
@@ -1200,7 +1236,7 @@ byte_array_to_domain (MonoArray *arr, MonoDomain *domain)
                return arr;
 
        copy = mono_array_new (domain, mono_defaults.byte_class, arr->max_length);
-       mono_gc_memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
+       memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length);
        return copy;
 }
 
@@ -1309,7 +1345,7 @@ mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32
                        continue;
 
                /* Re-calculate ms according to the time passed */
-               diff_ms = (mono_100ns_ticks () - start) / 10000;
+               diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
                if (diff_ms >= ms) {
                        ret = WAIT_TIMEOUT;
                        break;
@@ -1333,7 +1369,8 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_
        /* Do this WaitSleepJoin check before creating objects */
        mono_thread_current_check_pending_interrupt ();
 
-       numhandles = mono_array_length(mono_handles);
+       /* We fail in managed if the array has more than 64 elements */
+       numhandles = (guint32)mono_array_length(mono_handles);
        handles = g_new0(HANDLE, numhandles);
 
        for(i = 0; i < numhandles; i++) {       
@@ -1368,7 +1405,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_
 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext)
 {
        HANDLE handles [MAXIMUM_WAIT_OBJECTS];
-       guint32 numhandles;
+       uintptr_t numhandles;
        guint32 ret;
        guint32 i;
        MonoObject *waitHandle;
@@ -1949,6 +1986,10 @@ static void CALLBACK interruption_request_apc (ULONG_PTR param)
  */
 static void signal_thread_state_change (MonoInternalThread *thread)
 {
+#ifndef HOST_WIN32
+       gpointer wait_handle;
+#endif
+
        if (thread == mono_thread_internal_current ()) {
                /* Do it synchronously */
                MonoException *exc = mono_thread_request_interruption (FALSE); 
@@ -1957,11 +1998,8 @@ static void signal_thread_state_change (MonoInternalThread *thread)
        }
 
 #ifdef HOST_WIN32
-       QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL);
+       QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, (ULONG_PTR)NULL);
 #else
-       /* fixme: store the state somewhere */
-       mono_thread_kill (thread, mono_thread_get_abort_signal ());
-
        /* 
         * This will cause waits to be broken.
         * It will also prevent the thread from entering a wait, so if the thread returns
@@ -1969,7 +2007,12 @@ static void signal_thread_state_change (MonoInternalThread *thread)
         * functions in the io-layer until the signal handler calls QueueUserAPC which will
         * make it return.
         */
-       wapi_interrupt_thread (thread->handle);
+       wait_handle = wapi_prepare_interrupt_thread (thread->handle);
+
+       /* fixme: store the state somewhere */
+       mono_thread_kill (thread, mono_thread_get_abort_signal ());
+
+       wapi_finish_interrupt_thread (wait_handle);
 #endif /* HOST_WIN32 */
 }
 
@@ -2520,17 +2563,16 @@ void mono_thread_init (MonoThreadStartCB start_cb,
 void mono_thread_cleanup (void)
 {
 #if !defined(HOST_WIN32) && !defined(RUN_IN_SUBTHREAD)
+       MonoThreadInfo *info;
+
        /* The main thread must abandon any held mutexes (particularly
         * important for named mutexes as they are shared across
         * processes, see bug 74680.)  This will happen when the
         * thread exits, but if it's not running in a subthread it
         * won't exit in time.
         */
-       /* Using non-w32 API is a nasty kludge, but I couldn't find
-        * anything in the documentation that would let me do this
-        * here yet still be safe to call on windows.
-        */
-       _wapi_thread_signal_self (mono_environment_exitcode_get ());
+       info = mono_thread_info_current ();
+       wapi_thread_handle_set_exited (info->handle, mono_environment_exitcode_get ());
 #endif
 
 #if 0
@@ -2611,6 +2653,7 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout)
                /*
                 * On !win32, when the thread handle becomes signalled, it just means the thread has exited user code,
                 * it can still run io-layer etc. code. So wait for it to really exit.
+                * FIXME: This won't join threads which are not in the joinable_hash yet.
                 */
                mono_thread_join ((gpointer)tid);
 
@@ -2713,7 +2756,7 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user)
                        return;
                }
 
-               handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+               handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
                if (handle == NULL) {
                        THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
                        return;
@@ -2753,7 +2796,7 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
        if (thread->tid != self && (thread->state & ThreadState_Background) != 0 &&
                !(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
        
-               handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+               handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
                if (handle == NULL)
                        return FALSE;
 
@@ -2937,7 +2980,7 @@ collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
                return;
 
        if (wait->num<MAXIMUM_WAIT_OBJECTS) {
-               handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+               handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
                if (handle == NULL)
                        return;
 
@@ -3099,7 +3142,7 @@ collect_threads (gpointer key, gpointer value, gpointer user_data)
        HANDLE handle;
 
        if (wait->num<MAXIMUM_WAIT_OBJECTS) {
-               handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+               handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
                if (handle == NULL)
                        return;
 
@@ -3157,7 +3200,7 @@ print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info)
 #endif
 
        mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text);
-       mono_thread_info_resume (mono_thread_info_get_tid (info));
+       mono_thread_info_finish_suspend_and_resume (info);
 
        fprintf (stdout, "%s", text->str);
 
@@ -3393,7 +3436,7 @@ collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
                /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
 
                if(data->wait.num<MAXIMUM_WAIT_OBJECTS) {
-                       HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid);
+                       HANDLE handle = mono_threads_open_thread_handle (thread->handle, (MonoNativeThreadId)thread->tid);
                        if (handle == NULL)
                                return;
                        data->wait.handles [data->wait.num] = handle;
@@ -3534,7 +3577,7 @@ static const int static_data_size [NUM_STATIC_DATA_IDX] = {
 static uintptr_t* static_reference_bitmaps [NUM_STATIC_DATA_IDX];
 
 static void
-mark_tls_slots (void *addr, MonoGCMarkFunc mark_func)
+mark_tls_slots (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
 {
        int i;
        gpointer *static_data = addr;
@@ -3550,7 +3593,7 @@ mark_tls_slots (void *addr, MonoGCMarkFunc mark_func)
                        void ** p = ptr;
                        while (bmap) {
                                if ((bmap & 1) && *p) {
-                                       mark_func (p);
+                                       mark_func (p, gc_data);
                                }
                                p++;
                                bmap >>= 1;
@@ -3704,6 +3747,12 @@ search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32
        return NULL;
 }
 
+#if SIZEOF_VOID_P == 4
+#define ONE_P 1
+#else
+#define ONE_P 1ll
+#endif
+
 static void
 update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int numbits)
 {
@@ -3717,8 +3766,8 @@ update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int numbits)
        offset /= sizeof (gpointer);
        /* offset is now the bitmap offset */
        for (i = 0; i < numbits; ++i) {
-               if (bitmap [i / sizeof (uintptr_t)] & (1L << (i & (sizeof (uintptr_t) * 8 -1))))
-                       rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (1L << ((offset + i) & (sizeof (uintptr_t) * 8 -1)));
+               if (bitmap [i / sizeof (uintptr_t)] & (ONE_P << (i & (sizeof (uintptr_t) * 8 -1))))
+                       rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (ONE_P << ((offset + i) & (sizeof (uintptr_t) * 8 -1)));
        }
 }
 
@@ -3824,7 +3873,7 @@ free_thread_static_data_helper (gpointer key, gpointer value, gpointer user)
        if (!thread->static_data || !thread->static_data [idx])
                return;
        ptr = ((char*) thread->static_data [idx]) + (data->offset & 0xffffff);
-       mono_gc_bzero (ptr, data->size);
+       mono_gc_bzero_atomic (ptr, data->size);
 }
 
 static void
@@ -4131,9 +4180,9 @@ mono_thread_request_interruption (gboolean running_managed)
                   or similar */
                /* Our implementation of this function ignores the func argument */
 #ifdef HOST_WIN32
-               QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL);
+               QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, (ULONG_PTR)NULL);
 #else
-               wapi_thread_interrupt_self ();
+               wapi_self_interrupt ();
 #endif
                return NULL;
        }
@@ -4497,13 +4546,13 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
        }
 
        if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (&info->suspend_state)) {
-               mono_thread_info_resume (mono_thread_info_get_tid (info));
+               mono_thread_info_finish_suspend_and_resume (info);
                return;
        }
 
        /*someone is already interrupting it*/
        if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) {
-               mono_thread_info_resume (mono_thread_info_get_tid (info));
+               mono_thread_info_finish_suspend_and_resume (info);
                return;
        }
        InterlockedIncrement (&thread_interruption_requested);
@@ -4517,9 +4566,8 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                /*Set the thread to call */
                if (install_async_abort)
                        mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
-               mono_thread_info_resume (mono_thread_info_get_tid (info));
+               mono_thread_info_finish_suspend_and_resume (info);
        } else {
-               gpointer interrupt_handle;
                /* 
                 * This will cause waits to be broken.
                 * It will also prevent the thread from entering a wait, so if the thread returns
@@ -4528,9 +4576,10 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                 * make it return.
                 */
 #ifndef HOST_WIN32
+               gpointer interrupt_handle;
                interrupt_handle = wapi_prepare_interrupt_thread (thread->handle);
 #endif
-               mono_thread_info_resume (mono_thread_info_get_tid (info));
+               mono_thread_info_finish_suspend_and_resume (info);
 #ifndef HOST_WIN32
                wapi_finish_interrupt_thread (interrupt_handle);
 #endif
@@ -4539,16 +4588,18 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
 }
 
 static void
-transition_to_suspended (MonoInternalThread *thread)
+transition_to_suspended (MonoInternalThread *thread, MonoThreadInfo *info)
 {
        if ((thread->state & ThreadState_SuspendRequested) == 0) {
                g_assert (0); /*FIXME we should not reach this */
                /*Make sure we balance the suspend count.*/
-               mono_thread_info_resume ((MonoNativeThreadId)(gpointer)(gsize)thread->tid);
+               if (info)
+                       mono_thread_info_finish_suspend_and_resume (info);
        } else {
                thread->state &= ~ThreadState_SuspendRequested;
                thread->state |= ThreadState_Suspended;
-               mono_thread_info_finish_suspend ();
+               if (info)
+                       mono_thread_info_finish_suspend (info);
        }
        UNLOCK_THREAD (thread);
 }
@@ -4563,7 +4614,7 @@ suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
 
        LOCK_THREAD (thread);
        if (thread == mono_thread_internal_current ()) {
-               transition_to_suspended (thread);
+               transition_to_suspended (thread, NULL);
                mono_thread_info_self_suspend ();
        } else {
                MonoThreadInfo *info;
@@ -4582,9 +4633,11 @@ suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
                running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
 
                if (running_managed && !protected_wrapper) {
-                       transition_to_suspended (thread);
+                       transition_to_suspended (thread, info);
                } else {
+#ifndef HOST_WIN32
                        gpointer interrupt_handle;
+#endif
 
                        if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
                                InterlockedIncrement (&thread_interruption_requested);
@@ -4592,7 +4645,7 @@ suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
                        if (interrupt)
                                interrupt_handle = wapi_prepare_interrupt_thread (thread->handle);
 #endif
-                       mono_thread_info_resume (mono_thread_info_get_tid (info));
+                       mono_thread_info_finish_suspend_and_resume (info);
 #ifndef HOST_WIN32
                        if (interrupt)
                                wapi_finish_interrupt_thread (interrupt_handle);
@@ -4642,7 +4695,7 @@ self_suspend_internal (MonoInternalThread *thread)
                return;
        }
 
-       transition_to_suspended (thread);
+       transition_to_suspended (thread, NULL);
        mono_thread_info_self_suspend ();
 }
 
@@ -4776,6 +4829,7 @@ mono_thread_join (gpointer tid)
 {
 #ifndef HOST_WIN32
        pthread_t thread;
+       gboolean found = FALSE;
 
        joinable_threads_lock ();
        if (!joinable_threads)
@@ -4783,8 +4837,11 @@ mono_thread_join (gpointer tid)
        if (g_hash_table_lookup (joinable_threads, tid)) {
                g_hash_table_remove (joinable_threads, tid);
                joinable_thread_count --;
+               found = TRUE;
        }
        joinable_threads_unlock ();
+       if (!found)
+               return;
        thread = (pthread_t)tid;
        pthread_join (thread, NULL);
 #endif