X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=2ae43aab0ae3be675b979e4cb5756fc1d6ed169a;hb=6d37ffdf534eae04a5d06b2e097762bd64fc0591;hp=239d6ffb8990375ef093a2ba293ac7aeb0875173;hpb=f4d9546f3c7fda64c767e0966be69b2da4ca8d37;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 239d6ffb899..2ae43aab0ae 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -45,14 +45,21 @@ #include #include #include +#include +#include #include #include +#include #ifdef HAVE_SIGNAL_H #include #endif +#if defined(HOST_WIN32) +#include +#endif + #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) #define USE_TKILL_ON_ANDROID 1 #endif @@ -83,15 +90,6 @@ extern int tkill (pid_t tid, int signal); #define LOCK_THREAD(thread) lock_thread((thread)) #define UNLOCK_THREAD(thread) unlock_thread((thread)) -typedef struct -{ - guint32 (*func)(void *); - MonoThread *obj; - MonoObject *delegate; - void *start_arg; - MonoCoopSem registered; -} StartInfo; - typedef union { gint32 ival; gfloat fval; @@ -152,7 +150,7 @@ 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. + * When mono_thread_attach_internal is called for a thread, it will be removed from this hash table. * Protected by mono_threads_lock (). */ static MonoGHashTable *threads_starting_up = NULL; @@ -193,7 +191,6 @@ static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL; static guint32 default_stacksize = 0; #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize) -static void thread_adjust_static_data (MonoInternalThread *thread); static void context_adjust_static_data (MonoAppContext *ctx); static void mono_free_static_data (gpointer* static_data); static void mono_init_static_data_info (StaticDataInfo *static_data); @@ -271,87 +268,6 @@ thread_get_tid (MonoInternalThread *thread) 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. - * - * If handle_store() returns FALSE the thread must not be started - * because Mono is shutting down. - */ -static gboolean handle_store(MonoThread *thread, gboolean force_attach) -{ - mono_threads_lock (); - - THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->internal_thread->tid)); - - if (threads_starting_up) - mono_g_hash_table_remove (threads_starting_up, thread); - - if (shutting_down && !force_attach) { - mono_threads_unlock (); - return FALSE; - } - - if(threads==NULL) { - MONO_GC_REGISTER_ROOT_FIXED (threads, MONO_ROOT_SOURCE_THREADING, "threads table"); - threads=mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table"); - } - - /* We don't need to duplicate thread->handle, because it is - * only closed when the thread object is finalized by the GC. - */ - g_assert (thread->internal_thread); - mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->internal_thread->tid), - thread->internal_thread); - - mono_threads_unlock (); - - return TRUE; -} - -static gboolean handle_remove(MonoInternalThread *thread) -{ - gboolean ret; - gsize tid = thread->tid; - - THREAD_DEBUG (g_message ("%s: thread ID %"G_GSIZE_FORMAT, __func__, tid)); - - mono_threads_lock (); - - if (threads) { - /* We have to check whether the thread object for the - * tid is still the same in the table because the - * thread might have been destroyed and the tid reused - * in the meantime, in which case the tid would be in - * the table, but with another thread object. - */ - if (mono_g_hash_table_lookup (threads, (gpointer)tid) == thread) { - mono_g_hash_table_remove (threads, (gpointer)tid); - ret = TRUE; - } else { - ret = FALSE; - } - } - else - ret = FALSE; - - mono_threads_unlock (); - - /* Don't close the handle here, wait for the object finalizer - * to do it. Otherwise, the following race condition applies: - * - * 1) Thread exits (and handle_remove() closes the handle) - * - * 2) Some other handle is reassigned the same slot - * - * 3) Another thread tries to join the first thread, and - * blocks waiting for the reassigned handle to be signalled - * (which might never happen). This is possible, because the - * thread calling Join() still has a reference to the first - * thread's object. - */ - return ret; -} - static void ensure_synch_cs_set (MonoInternalThread *thread) { MonoCoopMutex *synch_cs; @@ -407,6 +323,8 @@ is_threadabort_exception (MonoClass *klass) */ static void thread_cleanup (MonoInternalThread *thread) { + gboolean ret; + g_assert (thread != NULL); if (thread->abort_state_handle) { @@ -455,13 +373,45 @@ static void thread_cleanup (MonoInternalThread *thread) if (InterlockedExchange (&thread->interruption_requested, 0)) InterlockedDecrement (&thread_interruption_requested); + mono_threads_lock (); + + if (!threads) { + ret = FALSE; + } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) { + /* We have to check whether the thread object for the + * tid is still the same in the table because the + * thread might have been destroyed and the tid reused + * in the meantime, in which case the tid would be in + * the table, but with another thread object. + */ + ret = FALSE; + } else { + mono_g_hash_table_remove (threads, (gpointer)thread->tid); + ret = TRUE; + } + + mono_threads_unlock (); + + /* Don't close the handle here, wait for the object finalizer + * to do it. Otherwise, the following race condition applies: + * + * 1) Thread exits (and thread_cleanup() closes the handle) + * + * 2) Some other handle is reassigned the same slot + * + * 3) Another thread tries to join the first thread, and + * blocks waiting for the reassigned handle to be signalled + * (which might never happen). This is possible, because the + * thread calling Join() still has a reference to the first + * thread's object. + */ + /* if the thread is not in the hash it has been removed already */ - if (!handle_remove (thread)) { + if (!ret) { if (thread == mono_thread_internal_current ()) { mono_domain_unset (); mono_memory_barrier (); } - /* This needs to be called even if handle_remove () fails */ if (mono_thread_cleanup_fn) mono_thread_cleanup_fn (thread_get_tid (thread)); return; @@ -605,7 +555,6 @@ new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal) MonoThread *thread; thread = create_thread_object (domain); - thread->priority = MONO_THREAD_PRIORITY_NORMAL; MONO_OBJECT_SETREF (thread, internal_thread, internal); @@ -634,94 +583,226 @@ create_internal_thread (void) MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref, MONO_ROOT_SOURCE_THREADING, "thread pinning reference"); } + thread->priority = MONO_THREAD_PRIORITY_NORMAL; + return thread; } +static void +mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority) +{ + g_assert (internal); + g_assert (internal->handle); + + g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST); + g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST); + g_assert (MONO_THREAD_PRIORITY_LOWEST < MONO_THREAD_PRIORITY_HIGHEST); + +#ifdef HOST_WIN32 + BOOL res; + + res = SetThreadPriority (internal->handle, priority - 2); + if (!res) + g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ()); +#else /* HOST_WIN32 */ + pthread_t tid; + int policy; + struct sched_param param; + gint res; + + tid = thread_get_tid (internal); + + res = pthread_getschedparam (tid, &policy, ¶m); + if (res != 0) + g_error ("%s: pthread_getschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); + +#ifdef _POSIX_PRIORITY_SCHEDULING + int max, min; + + /* Necessary to get valid priority range */ + + min = sched_get_priority_min (policy); + max = sched_get_priority_max (policy); + + if (max > 0 && min >= 0 && max > min) { + double srange, drange, sposition, dposition; + srange = MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST; + drange = max - min; + sposition = priority - MONO_THREAD_PRIORITY_LOWEST; + dposition = (sposition / srange) * drange; + param.sched_priority = (int)(dposition + min); + } else +#endif + { + switch (policy) { + case SCHED_FIFO: + case SCHED_RR: + param.sched_priority = 50; + break; +#ifdef SCHED_BATCH + case SCHED_BATCH: +#endif + case SCHED_OTHER: + param.sched_priority = 0; + break; + default: + g_error ("%s: unknown policy %d", __func__, policy); + } + } + + res = pthread_setschedparam (tid, policy, ¶m); + if (res != 0) { + if (res == EPERM) { + g_warning ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); + return; + } + g_error ("%s: pthread_setschedparam failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); + } +#endif /* HOST_WIN32 */ +} + +static void +mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal); + static gboolean -init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate) +mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain, gsize *stack_ptr) { - MonoDomain *domain = mono_get_root_domain (); + MonoThreadInfo *info; + MonoInternalThread *internal; + MonoDomain *domain, *root_domain; + + g_assert (thread); + + info = mono_thread_info_current (); + + internal = thread->internal_thread; + internal->handle = mono_thread_info_duplicate_handle (info); + internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()); + internal->thread_info = info; + internal->small_id = info->small_id; + internal->stack_ptr = stack_ptr; + + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal)); - if (!candidate || candidate->obj.vtable->domain != domain) { - candidate = new_thread_with_internal (domain, thread); + SET_CURRENT_OBJECT (internal); + + domain = mono_object_domain (thread); + + mono_thread_push_appdomain_ref (domain); + if (!mono_domain_set (domain, force_domain)) { + mono_thread_pop_appdomain_ref (); + return FALSE; + } + + mono_threads_lock (); + + if (threads_starting_up) + mono_g_hash_table_remove (threads_starting_up, thread); + + if (shutting_down && !force_attach) { + mono_threads_unlock (); + return FALSE; + } + + if (!threads) { + MONO_GC_REGISTER_ROOT_FIXED (threads, MONO_ROOT_SOURCE_THREADING, "threads table"); + threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table"); + } + + /* We don't need to duplicate thread->handle, because it is + * only closed when the thread object is finalized by the GC. */ + mono_g_hash_table_insert (threads, (gpointer)(gsize)(internal->tid), internal); + + /* We have to do this here because mono_thread_start_cb + * requires that root_domain_thread is set up. */ + if (thread_static_info.offset || thread_static_info.idx > 0) { + /* get the current allocated size */ + guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0); + mono_alloc_static_data (&internal->static_data, offset, TRUE); } - set_current_thread_for_domain (domain, thread, candidate); - g_assert (!thread->root_domain_thread); - MONO_OBJECT_SETREF (thread, root_domain_thread, candidate); + + mono_threads_unlock (); + + root_domain = mono_get_root_domain (); + + g_assert (!internal->root_domain_thread); + if (domain != root_domain) + MONO_OBJECT_SETREF (internal, root_domain_thread, new_thread_with_internal (root_domain, internal)); + else + MONO_OBJECT_SETREF (internal, root_domain_thread, thread); + + if (domain != root_domain) + set_current_thread_for_domain (root_domain, internal, internal->root_domain_thread); + + set_current_thread_for_domain (domain, internal, thread); + + THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, internal->tid, internal->handle)); + return TRUE; } -static guint32 WINAPI start_wrapper_internal(void *data) +typedef struct { + gint32 ref; + MonoThread *thread; + MonoObject *start_delegate; + MonoObject *start_delegate_arg; + MonoThreadStart start_func; + gpointer start_func_arg; + gboolean failed; + MonoCoopSem registered; +} StartInfo; + +static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack_ptr) { MonoError error; - MonoThreadInfo *info; - StartInfo *start_info = (StartInfo *)data; - guint32 (*start_func)(void *); - void *start_arg; + MonoThreadStart start_func; + void *start_func_arg; gsize tid; /* - * We don't create a local to hold start_info->obj, so hopefully it won't get pinned during a + * We don't create a local to hold start_info->thread, so hopefully it won't get pinned during a * GC stack walk. */ + MonoThread *thread; MonoInternalThread *internal; MonoObject *start_delegate; + MonoObject *start_delegate_arg; MonoDomain *domain; - mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE); - - internal = start_info->obj->internal_thread; - start_delegate = start_info->delegate; - domain = start_info->obj->obj.vtable->domain; + thread = start_info->thread; + internal = thread->internal_thread; + domain = mono_object_domain (start_info->thread); THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ())); - /* We can be sure start_info->obj->tid and - * start_info->obj->handle have been set, because the thread - * was created suspended, and these values were set before the - * thread resumed - */ + if (!mono_thread_attach_internal (thread, FALSE, FALSE, stack_ptr)) { + start_info->failed = TRUE; - info = mono_thread_info_current (); - g_assert (info); - internal->thread_info = info; - internal->small_id = info->small_id; + mono_coop_sem_post (&start_info->registered); - tid = internal->tid; - - SET_CURRENT_OBJECT (internal); + if (InterlockedDecrement (&start_info->ref) == 0) { + mono_coop_sem_destroy (&start_info->registered); + g_free (start_info); + } - /* Every thread references the appdomain which created it */ - mono_thread_push_appdomain_ref (domain); - - if (!mono_domain_set (domain, FALSE)) { - /* No point in raising an appdomain_unloaded exception here */ - /* FIXME: Cleanup here */ - mono_thread_pop_appdomain_ref (); return 0; } - start_func = start_info->func; - start_arg = start_info->obj->start_obj; - if (!start_arg) - start_arg = start_info->start_arg; + mono_thread_internal_set_priority (internal, internal->priority); - /* 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); + tid = internal->tid; + + start_delegate = start_info->start_delegate; + start_delegate_arg = start_info->start_delegate_arg; + start_func = start_info->start_func; + start_func_arg = start_info->start_func_arg; /* This MUST be called before any managed code can be * executed, as it calls the callback function that (for the * jit) sets the lmf marker. */ - mono_thread_new_init (tid, &tid, start_func); - internal->stack_ptr = &tid; - if (domain != mono_get_root_domain ()) - set_current_thread_for_domain (domain, internal, start_info->obj); - LIBGC_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, mono_native_thread_id_get (), getpid (), thread->stack_ptr)); - - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal)); + if (mono_thread_start_cb) + mono_thread_start_cb (tid, stack_ptr, start_func); /* On 2.0 profile (and higher), set explicitly since state might have been Unknown */ @@ -730,21 +811,16 @@ static guint32 WINAPI start_wrapper_internal(void *data) mono_thread_init_apartment_state (); - if (internal->start_notify) - /* Let the thread that called Start() know we're - * ready - */ - mono_coop_sem_post (internal->start_notify); + /* Let the thread that called Start() know we're ready */ + mono_coop_sem_post (&start_info->registered); - if (InterlockedDecrement ((gint32*)&internal->start_notify_refcount) == 0) { - mono_coop_sem_destroy (internal->start_notify); - g_free (internal->start_notify); - internal->start_notify = NULL; + if (InterlockedDecrement (&start_info->ref) == 0) { + mono_coop_sem_destroy (&start_info->registered); + g_free (start_info); } - g_free (start_info); - THREAD_DEBUG (g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__, - internal->tid)); + /* start_info is not valid anymore */ + start_info = NULL; /* * Call this after calling start_notify, since the profiler callback might want @@ -763,12 +839,14 @@ static guint32 WINAPI start_wrapper_internal(void *data) /* start_func is set only for unmanaged start functions */ if (start_func) { - start_func (start_arg); + start_func (start_func_arg); } else { void *args [1]; + g_assert (start_delegate != NULL); - args [0] = start_arg; + /* we may want to handle the exception here. See comment below on unhandled exceptions */ + args [0] = (gpointer) start_delegate_arg; mono_runtime_delegate_invoke_checked (start_delegate, args, &error); if (!mono_error_ok (&error)) { @@ -801,31 +879,21 @@ static guint32 WINAPI start_wrapper_internal(void *data) * for the current thead */ mono_thread_cleanup_apartment_state (); - thread_cleanup (internal); + mono_thread_detach_internal (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 - * thread handles will stay referenced :-( (This is due to - * missing support for scanning thread-specific data in the - * Boehm GC - the io-layer keeps a GC-visible hash of pointers - * to TLS data.) - */ - SET_CURRENT_OBJECT (NULL); - return(0); } static gsize WINAPI start_wrapper(void *data) { - volatile int dummy; + volatile gsize dummy; /* Avoid scanning the frames above this frame during a GC */ mono_gc_set_stack_end ((void*)&dummy); - return start_wrapper_internal (data); + return start_wrapper_internal ((StartInfo*) data, (gsize*) &dummy); } /* @@ -835,12 +903,19 @@ static gsize WINAPI start_wrapper(void *data) * LOCKING: Acquires the threads lock. */ static gboolean -create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *start_info, gboolean threadpool_thread, guint32 stack_size, - MonoError *error) +create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg, + gboolean threadpool_thread, guint32 stack_size, MonoError *error) { + StartInfo *start_info = NULL; HANDLE thread_handle; MonoNativeThreadId tid; - MonoThreadParm tp; + gboolean ret; + gsize stack_set_size; + + if (start_delegate) + g_assert (!start_func && !start_func_arg); + if (start_func) + g_assert (!start_delegate); /* * Join joinable threads to prevent running out of threads since the finalizer @@ -852,7 +927,6 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *star mono_threads_lock (); if (shutting_down) { - g_free (start_info); mono_threads_unlock (); return FALSE; } @@ -863,74 +937,65 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *star mono_g_hash_table_insert (threads_starting_up, thread, thread); mono_threads_unlock (); - internal->start_notify = g_new0 (MonoCoopSem, 1); - mono_coop_sem_init (internal->start_notify, 0); - internal->start_notify_refcount = 2; + internal->threadpool_thread = threadpool_thread; + if (threadpool_thread) + mono_thread_set_state (internal, ThreadState_Background); + start_info = g_new0 (StartInfo, 1); + start_info->ref = 2; + start_info->thread = thread; + start_info->start_delegate = start_delegate; + start_info->start_delegate_arg = thread->start_obj; + start_info->start_func = start_func; + start_info->start_func_arg = start_func_arg; + start_info->failed = FALSE; mono_coop_sem_init (&start_info->registered, 0); if (stack_size == 0) - stack_size = default_stacksize_for_thread (internal); - - /* Create suspended, so we can do some housekeeping before the thread - * starts - */ - tp.priority = thread->priority; - tp.stack_size = stack_size; - tp.creation_flags = 0; + stack_set_size = default_stacksize_for_thread (internal); + else + stack_set_size = 0; - thread_handle = mono_threads_create_thread (start_wrapper, start_info, &tp, &tid); + thread_handle = mono_threads_create_thread (start_wrapper, start_info, &stack_set_size, &tid); if (thread_handle == NULL) { /* The thread couldn't be created, so set an exception */ mono_threads_lock (); mono_g_hash_table_remove (threads_starting_up, thread); mono_threads_unlock (); - mono_coop_sem_destroy (&start_info->registered); - g_free (start_info); mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", GetLastError()); - return FALSE; + /* ref is not going to be decremented in start_wrapper_internal */ + InterlockedDecrement (&start_info->ref); + ret = FALSE; + goto done; } - THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle)); - - internal->handle = thread_handle; - internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (tid); - internal->threadpool_thread = threadpool_thread; - if (threadpool_thread) - mono_thread_set_state (internal, ThreadState_Background); + internal->stack_size = (int) stack_set_size; THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid)); - /* Only store the handle when the thread is about to be - * launched, to avoid the main thread deadlocking while trying - * to clean up a thread that will never be signalled. - */ - if (!handle_store (thread, FALSE)) { - mono_coop_sem_destroy (&start_info->registered); - return FALSE; - } - - mono_coop_sem_post (&start_info->registered); - /* * Wait for the thread to set up its TLS data etc, so * theres no potential race condition if someone tries * to look up the data believing the thread has * started */ - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid)); - mono_coop_sem_wait (internal->start_notify, MONO_SEM_FLAGS_NONE); - if (InterlockedDecrement ((gint32*)&internal->start_notify_refcount) == 0) { - mono_coop_sem_destroy (internal->start_notify); - g_free (internal->start_notify); - internal->start_notify = NULL; - } + mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE); + + mono_threads_close_thread_handle (thread_handle); THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid)); - return TRUE; + ret = !start_info->failed; + +done: + if (InterlockedDecrement (&start_info->ref) == 0) { + mono_coop_sem_destroy (&start_info->registered); + g_free (start_info); + } + + return ret; } void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func) @@ -960,31 +1025,22 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb { MonoThread *thread; MonoInternalThread *internal; - StartInfo *start_info; gboolean res; mono_error_init (error); thread = create_thread_object (domain); - thread->priority = MONO_THREAD_PRIORITY_NORMAL; internal = create_internal_thread (); MONO_OBJECT_SETREF (thread, internal_thread, internal); - start_info = g_new0 (StartInfo, 1); - start_info->func = (guint32 (*)(void *))func; - start_info->obj = thread; - start_info->start_arg = arg; + LOCK_THREAD (internal); - res = create_thread (thread, internal, start_info, threadpool_thread, stack_size, error); + res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, threadpool_thread, stack_size, error); return_val_if_nok (error, NULL); - /* Check that the managed and unmanaged layout of MonoInternalThread matches */ -#ifndef MONO_CROSS_COMPILE - if (mono_check_corlib_version () == NULL) - g_assert (((char*)&internal->abort_protected_block_count - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset); -#endif + UNLOCK_THREAD (internal); return internal; } @@ -1014,12 +1070,12 @@ mono_thread_attach (MonoDomain *domain) MonoThread * mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) { - MonoThreadInfo *info; - MonoInternalThread *thread; - MonoThread *current_thread; + MonoInternalThread *internal; + MonoThread *thread; MonoNativeThreadId tid; + gsize stack_ptr; - if ((thread = mono_thread_internal_current ())) { + if (mono_thread_internal_current_is_attached ()) { if (domain != mono_domain_get ()) mono_domain_set (domain, TRUE); /* Already attached */ @@ -1030,40 +1086,19 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) 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 before in the file containing the thread creation code.", mono_native_thread_id_get ()); } - info = mono_thread_info_current (); - tid=mono_native_thread_id_get (); - thread = create_internal_thread (); - thread->handle = mono_thread_info_get_handle (info); - thread->tid = MONO_NATIVE_THREAD_ID_TO_UINT (tid); - thread->stack_ptr = &tid; - - THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread->handle)); - - thread->thread_info = info; - thread->small_id = info->small_id; + internal = create_internal_thread (); - current_thread = new_thread_with_internal (domain, thread); + thread = new_thread_with_internal (domain, internal); - if (!handle_store (current_thread, force_attach)) { + if (!mono_thread_attach_internal (thread, force_attach, TRUE, &stack_ptr)) { /* Mono is shutting down, so just wait for the end */ for (;;) mono_thread_info_sleep (10000, NULL); } - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), thread)); - - SET_CURRENT_OBJECT (thread); - mono_domain_set (domain, TRUE); - - thread_adjust_static_data (thread); - - init_root_domain_thread (thread, current_thread); - - if (domain != mono_get_root_domain ()) - set_current_thread_for_domain (domain, thread, current_thread); - + THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle)); if (mono_thread_attach_cb) { guint8 *staddr; @@ -1072,17 +1107,17 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) mono_thread_info_get_stack_bounds (&staddr, &stsize); if (staddr == NULL) - mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &tid); + mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &stack_ptr); else mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), staddr + stsize); } /* Can happen when we attach the profiler helper thread in order to heapshot. */ - if (!info->tools_thread) + if (!mono_thread_info_current ()->tools_thread) // FIXME: Need a separate callback mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid)); - return current_thread; + return thread; } void @@ -1092,6 +1127,10 @@ mono_thread_detach_internal (MonoInternalThread *thread) THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); +#ifndef HOST_WIN32 + mono_w32mutex_abandon (); +#endif + thread_cleanup (thread); SET_CURRENT_OBJECT (NULL); @@ -1133,6 +1172,18 @@ mono_thread_detach_if_exiting (void) return FALSE; } +gboolean +mono_thread_internal_current_is_attached (void) +{ + MonoInternalThread *internal; + + internal = GET_CURRENT_OBJECT (); + if (!internal) + return FALSE; + + return TRUE; +} + void mono_thread_exit (void) { @@ -1140,13 +1191,12 @@ mono_thread_exit (void) THREAD_DEBUG (g_message ("%s: mono_thread_exit for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); - thread_cleanup (thread); - SET_CURRENT_OBJECT (NULL); - mono_domain_unset (); + mono_thread_detach_internal (thread); /* we could add a callback here for embedders to use. */ if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread)) exit (mono_environment_exitcode_get ()); + mono_thread_info_exit (); } @@ -1167,7 +1217,6 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj, MonoObject *start) { MonoError error; - StartInfo *start_info; MonoInternalThread *internal; gboolean res; @@ -1189,15 +1238,8 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj, UNLOCK_THREAD (internal); return this_obj; } - /* This is freed in start_wrapper */ - start_info = g_new0 (StartInfo, 1); - start_info->func = NULL; - start_info->start_arg = NULL; - start_info->delegate = start; - start_info->obj = this_obj; - g_assert (this_obj->obj.vtable->domain == mono_domain_get ()); - res = create_thread (this_obj, internal, start_info, FALSE, 0, &error); + res = create_thread (this_obj, internal, start, NULL, NULL, FALSE, 0, &error); if (!res) { mono_error_cleanup (&error); UNLOCK_THREAD (internal); @@ -1453,11 +1495,9 @@ ves_icall_System_Threading_Thread_GetPriority (MonoThread *this_obj) MonoInternalThread *internal = this_obj->internal_thread; LOCK_THREAD (internal); - if (internal->handle != NULL) - priority = mono_thread_info_get_priority ((MonoThreadInfo*) internal->thread_info); - else - priority = this_obj->priority; + priority = internal->priority; UNLOCK_THREAD (internal); + return priority; } @@ -1474,9 +1514,9 @@ ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priorit MonoInternalThread *internal = this_obj->internal_thread; LOCK_THREAD (internal); - this_obj->priority = priority; + internal->priority = priority; if (internal->handle != NULL) - mono_thread_info_set_priority ((MonoThreadInfo*) internal->thread_info, this_obj->priority); + mono_thread_internal_set_priority (internal, priority); UNLOCK_THREAD (internal); } @@ -1735,19 +1775,9 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret)); mono_error_set_pending_exception (&error); - /* - * These need to be here. See MSDN dos on WaitForMultipleObjects. - */ - if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) { - return map_native_wait_result_to_managed (ret - WAIT_OBJECT_0); - } - else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) { - return map_native_wait_result_to_managed (ret - WAIT_ABANDONED_0); - } - else { - /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ - return map_native_wait_result_to_managed (ret); - } + + /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ + return map_native_wait_result_to_managed (ret); } gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms) @@ -1798,123 +1828,6 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, H return map_native_wait_result_to_managed (ret); } -HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created) -{ - HANDLE mutex; - - *created = TRUE; - - if (name == NULL) { - mutex = CreateMutex (NULL, owned, NULL); - } else { - mutex = CreateMutex (NULL, owned, mono_string_chars (name)); - - if (GetLastError () == ERROR_ALREADY_EXISTS) { - *created = FALSE; - } - } - - return(mutex); -} - -MonoBoolean ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { - return(ReleaseMutex (handle)); -} - -HANDLE ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, - gint32 rights, - gint32 *error) -{ - HANDLE ret; - - *error = ERROR_SUCCESS; - - ret = OpenMutex (rights, FALSE, mono_string_chars (name)); - if (ret == NULL) { - *error = GetLastError (); - } - - return(ret); -} - - -HANDLE ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error) -{ - HANDLE sem; - - if (name == NULL) { - sem = CreateSemaphore (NULL, initialCount, maximumCount, NULL); - } else { - sem = CreateSemaphore (NULL, initialCount, maximumCount, - mono_string_chars (name)); - } - - *error = GetLastError (); - - return(sem); -} - -MonoBoolean ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (HANDLE handle, gint32 releaseCount, gint32 *prevcount) -{ - return ReleaseSemaphore (handle, releaseCount, prevcount); -} - -HANDLE ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error) -{ - HANDLE sem; - - sem = OpenSemaphore (rights, FALSE, mono_string_chars (name)); - - *error = GetLastError (); - - return(sem); -} - -HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, gint32 *error) -{ - HANDLE event; - - if (name == NULL) { - event = CreateEvent (NULL, manual, initial, NULL); - } else { - event = CreateEvent (NULL, manual, initial, - mono_string_chars (name)); - } - - *error = GetLastError (); - - return(event); -} - -gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) { - return (SetEvent(handle)); -} - -gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) { - return (ResetEvent(handle)); -} - -void -ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) { - CloseHandle (handle); -} - -HANDLE ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name, - gint32 rights, - gint32 *error) -{ - HANDLE ret; - - ret = OpenEvent (rights, FALSE, mono_string_chars (name)); - if (ret == NULL) { - *error = GetLastError (); - } else { - *error = ERROR_SUCCESS; - } - - return(ret); -} - gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location) { return InterlockedIncrement (location); @@ -2159,7 +2072,7 @@ ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint3 * be notified, since it has to rebuild the list of threads to * wait for. */ - SetEvent (background_change_event); + mono_w32event_set (background_change_event); } } @@ -2173,7 +2086,7 @@ ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint3 * be notified, since it has to rebuild the list of threads to * wait for. */ - SetEvent (background_change_event); + mono_w32event_set (background_change_event); } } @@ -2924,7 +2837,7 @@ void mono_thread_init (MonoThreadStartCB start_cb, mono_os_mutex_init_recursive(&interlocked_mutex); mono_os_mutex_init_recursive(&joinable_threads_mutex); - background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL); + background_change_event = mono_w32event_create (TRUE, FALSE); g_assert(background_change_event != NULL); mono_init_static_data_info (&thread_static_info); @@ -3025,34 +2938,14 @@ wait_for_tids (struct wait_data *wait, guint32 timeout) return; for(i=0; inum; i++) { - gsize tid = wait->threads[i]->tid; + MonoInternalThread *internal; - /* - * 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); + internal = wait->threads [i]; mono_threads_lock (); - if(mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) { - /* This thread must have been killed, because - * it hasn't cleaned itself up. (It's just - * possible that the thread exited before the - * parent thread had a chance to store the - * handle, and now there is another pointer to - * the already-exited thread stored. In this - * case, we'll just get two - * mono_profiler_thread_end() calls for the - * same thread.) - */ - - mono_threads_unlock (); - THREAD_DEBUG (g_message ("%s: cleaning up after thread %p (%"G_GSIZE_FORMAT")", __func__, wait->threads[i], tid)); - thread_cleanup (wait->threads[i]); - } else { - mono_threads_unlock (); - } + if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal) + g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal); + mono_threads_unlock (); } } @@ -3088,15 +2981,14 @@ static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeo return; if (ret < wait->num) { - gsize tid = wait->threads[ret]->tid; + MonoInternalThread *internal; + + internal = wait->threads [ret]; + mono_threads_lock (); - if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) { - /* See comment in wait_for_tids about thread cleanup */ - mono_threads_unlock (); - THREAD_DEBUG (g_message ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, tid)); - thread_cleanup (wait->threads [ret]); - } else - mono_threads_unlock (); + if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal) + g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal); + mono_threads_unlock (); } } @@ -3224,8 +3116,8 @@ mono_threads_set_shutting_down (void) UNLOCK_THREAD (current_thread); } - /*since we're killing the thread, unset the current domain.*/ - mono_domain_unset (); + /*since we're killing the thread, detach it.*/ + mono_thread_detach_internal (current_thread); /* Wake up other threads potentially waiting for us */ mono_thread_info_exit (); @@ -3236,7 +3128,7 @@ mono_threads_set_shutting_down (void) * interrupt the main thread if it is waiting for all * the other threads. */ - SetEvent (background_change_event); + mono_w32event_set (background_change_event); mono_threads_unlock (); } @@ -3269,7 +3161,7 @@ void mono_thread_manage (void) THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads)); mono_g_hash_table_foreach (threads, print_tids, NULL)); - ResetEvent (background_change_event); + mono_w32event_reset (background_change_event); wait->num=0; /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/ memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); @@ -3485,7 +3377,7 @@ get_thread_dump (MonoThreadInfo *info, gpointer ud) #if 0 /* This no longer works with remote unwinding */ g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread); - mono_thread_info_describe (info, text); + mono_thread_internal_describe (thread, text); g_string_append (text, "\n"); #endif @@ -4146,22 +4038,6 @@ mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 return offset; } -/* - * ensure thread static fields already allocated are valid for thread - * This function is called when a thread is created or on thread attach. - */ -static void -thread_adjust_static_data (MonoInternalThread *thread) -{ - mono_threads_lock (); - if (thread_static_info.offset || thread_static_info.idx > 0) { - /* get the current allocated size */ - guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (thread_static_info.idx, thread_static_info.offset, 0); - mono_alloc_static_data (&thread->static_data, offset, TRUE); - } - mono_threads_unlock (); -} - /* * LOCKING: requires that threads_mutex is held */ @@ -4704,6 +4580,29 @@ mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state) UNLOCK_THREAD (thread); } +/** + * mono_thread_test_and_set_state: + * + * Test if current state of @thread include @test. If it does not, OR @set into the state. + * + * Returns TRUE is @set was OR'd in. + */ +gboolean +mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set) +{ + LOCK_THREAD (thread); + + if ((thread->state & test) != 0) { + UNLOCK_THREAD (thread); + return FALSE; + } + + thread->state |= set; + UNLOCK_THREAD (thread); + + return TRUE; +} + void mono_thread_clr_state (MonoInternalThread *thread, MonoThreadState state) { @@ -5027,7 +4926,7 @@ mono_threads_join_threads (void) if (thread != pthread_self ()) { MONO_ENTER_GC_SAFE; /* This shouldn't block */ - pthread_join (thread, NULL); + mono_native_thread_join (thread); MONO_EXIT_GC_SAFE; } } else { @@ -5063,7 +4962,7 @@ mono_thread_join (gpointer tid) return; thread = (pthread_t)tid; MONO_ENTER_GC_SAFE; - pthread_join (thread, NULL); + mono_native_thread_join (thread); MONO_EXIT_GC_SAFE; #endif } @@ -5229,4 +5128,55 @@ mono_thread_try_resume_interruption (void) return NULL; return mono_thread_resume_interruption (); -} \ No newline at end of file +} + +#if 0 +/* Returns TRUE if the current thread is ready to be interrupted. */ +gboolean +mono_threads_is_ready_to_be_interrupted (void) +{ + MonoInternalThread *thread; + + thread = mono_thread_internal_current (); + LOCK_THREAD (thread); + if (thread->state & (MonoThreadState)(ThreadState_StopRequested | ThreadState_SuspendRequested | ThreadState_AbortRequested)) { + UNLOCK_THREAD (thread); + return FALSE; + } + + if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) { + UNLOCK_THREAD (thread); + return FALSE; + } + + UNLOCK_THREAD (thread); + return TRUE; +} +#endif + +void +mono_thread_internal_describe (MonoInternalThread *internal, GString *text) +{ + g_string_append_printf (text, ", thread handle : %p", internal->handle); + + if (internal->thread_info) { + g_string_append (text, ", state : "); + mono_thread_info_describe_interrupt_token ((MonoThreadInfo*) internal->thread_info, text); + } + + if (internal->owned_mutexes) { + int i; + + g_string_append (text, ", owns : ["); + for (i = 0; i < internal->owned_mutexes->len; i++) + g_string_append_printf (text, i == 0 ? "%p" : ", %p", g_ptr_array_index (internal->owned_mutexes, i)); + g_string_append (text, "]"); + } +} + +gboolean +mono_thread_internal_is_current (MonoInternalThread *internal) +{ + g_assert (internal); + return mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid)); +}