[coop] Switch to GC safe around pthread_join
[mono.git] / mono / metadata / threads.c
index d00b01f812933a860cf15b16c87f58d57ca87714..8309ddbb540da1ad067185f315898d8e89a77cd6 100644 (file)
@@ -37,6 +37,7 @@
 #include <mono/utils/mono-membar.h>
 #include <mono/utils/mono-time.h>
 #include <mono/utils/mono-threads.h>
+#include <mono/utils/mono-threads-coop.h>
 #include <mono/utils/hazard-pointer.h>
 #include <mono/utils/mono-tls.h>
 #include <mono/utils/atomic.h>
@@ -574,21 +575,22 @@ set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, M
 }
 
 static MonoThread*
-create_thread_object (MonoDomain *domain, MonoError *error)
+create_thread_object (MonoDomain *domain)
 {
+       MonoError error;
        MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
-       MonoThread *t = (MonoThread*)mono_object_new_mature (vt, error);
+       MonoThread *t = (MonoThread*)mono_object_new_mature (vt, &error);
+       /* only possible failure mode is OOM, from which we don't expect to recover. */
+       mono_error_assert_ok (&error);
        return t;
 }
 
 static MonoThread*
-new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, MonoError *error)
+new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
 {
        MonoThread *thread;
 
-       thread = create_thread_object (domain, error);
-       if (!mono_error_ok (error))
-               return NULL;
+       thread = create_thread_object (domain);
 
        MONO_OBJECT_SETREF (thread, internal_thread, internal);
 
@@ -596,15 +598,16 @@ new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, Mono
 }
 
 static MonoInternalThread*
-create_internal_thread (MonoError *error)
+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_object_new_mature (vt, error);
-       if (!mono_error_ok (error))
-               return NULL;
+       thread = (MonoInternalThread*) mono_object_new_mature (vt, &error);
+       /* only possible failure mode is OOM, from which we don't exect to recover */
+       mono_error_assert_ok (&error);
 
        thread->synch_cs = g_new0 (MonoCoopMutex, 1);
        mono_coop_mutex_init_recursive (thread->synch_cs);
@@ -620,14 +623,12 @@ create_internal_thread (MonoError *error)
 }
 
 static gboolean
-init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate, MonoError *error)
+init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate)
 {
        MonoDomain *domain = mono_get_root_domain ();
 
-       mono_error_init (error);
        if (!candidate || candidate->obj.vtable->domain != domain) {
-               candidate = new_thread_with_internal (domain, thread, error);
-               return_val_if_nok (error, FALSE);
+               candidate = new_thread_with_internal (domain, thread);
        }
        set_current_thread_for_domain (domain, thread, candidate);
        g_assert (!thread->root_domain_thread);
@@ -686,8 +687,7 @@ static guint32 WINAPI start_wrapper_internal(void *data)
        /* We have to do this here because mono_thread_new_init()
           requires that root_domain_thread is set up. */
        thread_adjust_static_data (internal);
-       init_root_domain_thread (internal, start_info->obj, &error);
-       mono_error_raise_exception (&error); /* FIXME don't raise here */
+       init_root_domain_thread (internal, start_info->obj);
 
        /* This MUST be called before any managed code can be
         * executed, as it calls the callback function that (for the
@@ -927,11 +927,9 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
 
        mono_error_init (error);
 
-       thread = create_thread_object (domain, error);
-       return_val_if_nok (error, NULL);
+       thread = create_thread_object (domain);
 
-       internal = create_internal_thread (error);
-       return_val_if_nok (error, NULL);
+       internal = create_internal_thread ();
 
        MONO_OBJECT_SETREF (thread, internal_thread, internal);
 
@@ -969,15 +967,13 @@ mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, Mon
 MonoThread *
 mono_thread_attach (MonoDomain *domain)
 {
-       MonoError error;
-       MonoThread *thread = mono_thread_attach_full (domain, FALSE, &error);
-       mono_error_raise_exception (&error);
+       MonoThread *thread = mono_thread_attach_full (domain, FALSE);
 
        return thread;
 }
 
 MonoThread *
-mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *error)
+mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
 {
        MonoThreadInfo *info;
        MonoInternalThread *thread;
@@ -985,8 +981,6 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e
        HANDLE thread_handle;
        MonoNativeThreadId tid;
 
-       mono_error_init (error);
-
        if ((thread = mono_thread_internal_current ())) {
                if (domain != mono_domain_get ())
                        mono_domain_set (domain, TRUE);
@@ -998,9 +992,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e
                g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
        }
 
-       thread = create_internal_thread (error);
-       if (!mono_error_ok (error))
-               return NULL;
+       thread = create_internal_thread ();
 
        thread_handle = mono_thread_info_open_handle ();
        g_assert (thread_handle);
@@ -1018,9 +1010,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e
        thread->thread_info = info;
        thread->small_id = info->small_id;
 
-       current_thread = new_thread_with_internal (domain, thread, error);
-       if (!mono_error_ok (error))
-               return NULL;
+       current_thread = new_thread_with_internal (domain, thread);
 
        if (!handle_store (current_thread, force_attach)) {
                /* Mono is shutting down, so just wait for the end */
@@ -1035,8 +1025,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e
 
        thread_adjust_static_data (thread);
 
-       init_root_domain_thread (thread, current_thread, error);
-       return_val_if_nok (error, NULL);
+       init_root_domain_thread (thread, current_thread);
 
        if (domain != mono_get_root_domain ())
                set_current_thread_for_domain (domain, thread, current_thread);
@@ -1128,12 +1117,9 @@ mono_thread_exit ()
 void
 ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
 {
-       MonoError error;
        MonoInternalThread *internal;
 
-       internal = create_internal_thread (&error);
-       if (mono_error_set_pending_exception (&error))
-               return;
+       internal = create_internal_thread ();
 
        internal->state = ThreadState_Unstarted;
 
@@ -1401,7 +1387,8 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g
        UNLOCK_THREAD (this_obj);
 
        if (this_obj->name && this_obj->tid) {
-               char *tname = mono_string_to_utf8 (name);
+               char *tname = mono_string_to_utf8_checked (name, error);
+               return_if_nok (error);
                mono_profiler_thread_name (this_obj->tid, tname);
                mono_native_thread_set_name (thread_get_tid (this_obj), tname);
                mono_free (tname);
@@ -1492,7 +1479,6 @@ ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
 MonoThread *
 mono_thread_current (void)
 {
-       MonoError error;
        MonoDomain *domain = mono_domain_get ();
        MonoInternalThread *internal = mono_thread_internal_current ();
        MonoThread **current_thread_ptr;
@@ -1502,8 +1488,7 @@ mono_thread_current (void)
 
        if (!*current_thread_ptr) {
                g_assert (domain != mono_get_root_domain ());
-               *current_thread_ptr = new_thread_with_internal (domain, internal, &error);
-               mono_error_raise_exception (&error); /* FIXME don't raise here */
+               *current_thread_ptr = new_thread_with_internal (domain, internal);
        }
        return *current_thread_ptr;
 }
@@ -1512,7 +1497,6 @@ mono_thread_current (void)
 static MonoThread *
 mono_thread_current_for_thread (MonoInternalThread *internal)
 {
-       MonoError error;
        MonoDomain *domain = mono_domain_get ();
        MonoThread **current_thread_ptr;
 
@@ -1521,8 +1505,7 @@ mono_thread_current_for_thread (MonoInternalThread *internal)
 
        if (!*current_thread_ptr) {
                g_assert (domain != mono_get_root_domain ());
-               *current_thread_ptr = new_thread_with_internal (domain, internal, &error);
-               mono_error_raise_exception (&error); /* FIXME don't raise here */
+               *current_thread_ptr = new_thread_with_internal (domain, internal);
        }
        return *current_thread_ptr;
 }
@@ -2553,7 +2536,7 @@ void mono_thread_stop (MonoThread *thread)
                This function is part of the embeding API and has no way to return the exception
                to be thrown. So what we do is keep the old behavior and raise the exception.
                */
-               mono_error_raise_exception (&error);
+               mono_error_raise_exception (&error); /* OK to throw, see note */
        } else {
                async_abort_internal (internal, TRUE);
        }
@@ -3615,10 +3598,9 @@ mono_threads_perform_thread_dump (void)
 }
 
 /* Obtain the thread dump of all threads */
-static void
-mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames)
+static gboolean
+mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_frames, MonoError *error)
 {
-       MonoError error;
 
        ThreadDumpUserData ud;
        MonoInternalThread *thread_array [128];
@@ -3626,7 +3608,7 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
        MonoDebugSourceLocation *location;
        int tindex, nthreads;
 
-       mono_error_init (&error);
+       mono_error_init (error);
        
        *out_threads = NULL;
        *out_stack_frames = NULL;
@@ -3638,11 +3620,11 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
        ud.frames = g_new0 (MonoStackFrameInfo, 256);
        ud.max_frames = 256;
 
-       *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, &error);
-       if (!is_ok (&error))
+       *out_threads = mono_array_new_checked (domain, mono_defaults.thread_class, nthreads, error);
+       if (!is_ok (error))
                goto leave;
-       *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, &error);
-       if (!is_ok (&error))
+       *out_stack_frames = mono_array_new_checked (domain, mono_defaults.array_class, nthreads, error);
+       if (!is_ok (error))
                goto leave;
 
        for (tindex = 0; tindex < nthreads; ++tindex) {
@@ -3662,16 +3644,16 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
 
                mono_array_setref_fast (*out_threads, tindex, mono_thread_current_for_thread (thread));
 
-               thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, &error);
-               if (!is_ok (&error))
+               thread_frames = mono_array_new_checked (domain, mono_defaults.stack_frame_class, ud.nframes, error);
+               if (!is_ok (error))
                        goto leave;
                mono_array_setref_fast (*out_stack_frames, tindex, thread_frames);
 
                for (i = 0; i < ud.nframes; ++i) {
                        MonoStackFrameInfo *frame = &ud.frames [i];
                        MonoMethod *method = NULL;
-                       MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, &error);
-                       if (!mono_error_ok (&error))
+                       MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
+                       if (!is_ok (error))
                                goto leave;
 
                        sf->native_offset = frame->native_offset;
@@ -3682,8 +3664,9 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
                        if (method) {
                                sf->method_address = (gsize) frame->ji->code_start;
 
-                               MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, &error);
-                               mono_error_raise_exception (&error); /* FIXME don't raise here */
+                               MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
+                               if (!is_ok (error))
+                                       goto leave;
                                MONO_OBJECT_SETREF (sf, method, rm);
 
                                location = mono_debug_lookup_source_location (method, frame->native_offset, domain);
@@ -3706,7 +3689,7 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
 
 leave:
        g_free (ud.frames);
-       mono_error_raise_exception (&error); /* FIXME don't raise here */
+       return is_ok (error);
 }
 
 /**
@@ -4422,17 +4405,6 @@ mono_alloc_special_static_data_free (GHashTable *special_static_fields)
        mono_threads_unlock ();
 }
 
-static void
-mono_special_static_data_free_slot (guint32 offset, guint32 size)
-{
-       /* Only ever called for ThreadLocal instances */
-       g_assert (ACCESS_SPECIAL_STATIC_OFFSET (offset, type) == SPECIAL_STATIC_OFFSET_TYPE_THREAD);
-
-       mono_threads_lock ();
-       do_free_special_slot (offset, size);
-       mono_threads_unlock ();
-}
-
 #ifdef HOST_WIN32
 static void CALLBACK dummy_apc (ULONG_PTR param)
 {
@@ -5021,9 +4993,12 @@ mono_threads_join_threads (void)
                }
                joinable_threads_unlock ();
                if (found) {
-                       if (thread != pthread_self ())
+                       if (thread != pthread_self ()) {
+                               MONO_ENTER_GC_SAFE;
                                /* This shouldn't block */
                                pthread_join (thread, NULL);
+                               MONO_EXIT_GC_SAFE;
+                       }
                } else {
                        break;
                }
@@ -5056,7 +5031,9 @@ mono_thread_join (gpointer tid)
        if (!found)
                return;
        thread = (pthread_t)tid;
+       MONO_ENTER_GC_SAFE;
        pthread_join (thread, NULL);
+       MONO_EXIT_GC_SAFE;
 #endif
 }
 
@@ -5097,7 +5074,9 @@ mono_thread_internal_unhandled_exception (MonoObject* exc)
 void
 ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoArray **out_stack_traces)
 {
-       mono_threads_get_thread_dump (out_threads, out_stack_traces);
+       MonoError error;
+       mono_threads_get_thread_dump (out_threads, out_stack_traces, &error);
+       mono_error_set_pending_exception (&error);
 }
 
 /*
@@ -5114,7 +5093,6 @@ ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoA
 gpointer
 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
 {
-       MonoError error;
        MonoDomain *orig;
        gboolean fresh_thread;
 
@@ -5135,8 +5113,7 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
        }
 
        if (!mono_thread_internal_current ()) {
-               mono_thread_attach_full (domain, FALSE, &error);
-               mono_error_assert_ok (&error);
+               mono_thread_attach_full (domain, FALSE);
 
                // #678164
                mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
@@ -5153,7 +5130,7 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
                *dummy = NULL;
                /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
                 * return the right cookie. */
-               return mono_threads_enter_gc_unsafe_region_cookie (mono_thread_info_current ());
+               return mono_threads_enter_gc_unsafe_region_cookie ();
        } else {
                *dummy = orig;
                /* thread state (BLOCKING|RUNNING) -> RUNNING */