#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/w32handle.h>
+#include <mono/metadata/w32event.h>
+#include <mono/metadata/w32mutex.h>
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/reflection-internals.h>
+#include <mono/metadata/abi-details.h>
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
+#if defined(HOST_WIN32)
+#include <objbase.h>
+#endif
+
#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
#define USE_TKILL_ON_ANDROID 1
#endif
#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;
-} StartInfo;
-
typedef union {
gint32 ival;
gfloat fval;
/*
* 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;
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);
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;
*/
static void thread_cleanup (MonoInternalThread *thread)
{
+ gboolean ret;
+
g_assert (thread != NULL);
if (thread->abort_state_handle) {
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;
MonoThread *thread;
thread = create_thread_object (domain);
- thread->priority = MONO_THREAD_PRIORITY_NORMAL;
MONO_OBJECT_SETREF (thread, internal_thread, internal);
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));
+
+ SET_CURRENT_OBJECT (internal);
+
+ domain = mono_object_domain (thread);
- if (!candidate || candidate->obj.vtable->domain != domain) {
- candidate = new_thread_with_internal (domain, thread);
+ mono_thread_push_appdomain_ref (domain);
+ if (!mono_domain_set (domain, force_domain)) {
+ mono_thread_pop_appdomain_ref ();
+ return FALSE;
}
- set_current_thread_for_domain (domain, thread, candidate);
- g_assert (!thread->root_domain_thread);
- MONO_OBJECT_SETREF (thread, root_domain_thread, candidate);
+
+ 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);
+ }
+
+ 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.
*/
- MonoInternalThread *internal = start_info->obj->internal_thread;
- MonoObject *start_delegate = start_info->delegate;
- MonoDomain *domain = start_info->obj->obj.vtable->domain;
+ MonoThread *thread;
+ MonoInternalThread *internal;
+ MonoObject *start_delegate;
+ MonoObject *start_delegate_arg;
+ MonoDomain *domain;
- THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
+ thread = start_info->thread;
+ internal = thread->internal_thread;
+ domain = mono_object_domain (start_info->thread);
- /* 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
- */
+ THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
- info = mono_thread_info_current ();
- g_assert (info);
- internal->thread_info = info;
- internal->small_id = info->small_id;
+ if (!mono_thread_attach_internal (thread, FALSE, FALSE, stack_ptr)) {
+ start_info->failed = TRUE;
- tid = internal->tid;
+ mono_coop_sem_post (&start_info->registered);
- 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);
+
+ tid = internal->tid;
- /* 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);
+ 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 */
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
/* 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)) {
* 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);
}
/*
* 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;
+
+ 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
mono_threads_lock ();
if (shutting_down) {
- g_free (start_info);
mono_threads_unlock ();
return FALSE;
}
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 = CREATE_SUSPENDED;
-
- thread_handle = mono_threads_create_thread (start_wrapper, start_info, &tp, &tid);
+ thread_handle = mono_threads_create_thread (start_wrapper, start_info, stack_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 ();
- 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);
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))
- return FALSE;
-
- mono_thread_info_resume (tid);
-
/*
* 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)
{
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->unused2 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
-#endif
+ UNLOCK_THREAD (internal);
return internal;
}
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 */
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 ());
}
- 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;
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
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);
mono_domain_unset ();
- /* Don't need to CloseHandle this thread, even though we took a
+ /* Don't need to close the handle to this thread, even though we took a
* reference in mono_thread_attach (), because the GC will do it
* when the Thread object is finalised.
*/
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)
{
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 ();
}
MonoObject *start)
{
MonoError error;
- StartInfo *start_info;
MonoInternalThread *internal;
gboolean res;
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);
* when thread_cleanup () can be called after this.
*/
if (thread)
- CloseHandle (thread);
+ mono_threads_close_thread_handle (thread);
if (this_obj->synch_cs) {
MonoCoopMutex *synch_cs = this_obj->synch_cs;
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;
}
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);
}
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)
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);
* be notified, since it has to rebuild the list of threads to
* wait for.
*/
- SetEvent (background_change_event);
+ mono_w32event_set (background_change_event);
}
}
* be notified, since it has to rebuild the list of threads to
* wait for.
*/
- SetEvent (background_change_event);
+ mono_w32event_set (background_change_event);
}
}
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);
guint32 num;
};
-static void wait_for_tids (struct wait_data *wait, guint32 timeout)
+static void
+wait_for_tids (struct wait_data *wait, guint32 timeout)
{
guint32 i, ret;
}
for(i=0; i<wait->num; i++)
- CloseHandle (wait->handles[i]);
+ mono_threads_close_thread_handle (wait->handles [i]);
if (ret == WAIT_TIMEOUT)
return;
for(i=0; i<wait->num; 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 ();
}
}
}
for(i=0; i<wait->num; i++)
- CloseHandle (wait->handles[i]);
+ mono_threads_close_thread_handle (wait->handles [i]);
if (ret == WAIT_TIMEOUT)
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 ();
}
}
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 ();
* 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 ();
}
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);
|| mono_gc_is_finalizer_internal_thread (thread)
|| (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
) {
- //CloseHandle (wait->handles [i]);
+ //mono_threads_close_thread_handle (wait->handles [i]);
wait->threads [i] = NULL; /* ignore this thread in next loop */
continue;
}
(thread->state & ThreadState_StopRequested) != 0 ||
(thread->state & ThreadState_Stopped) != 0) {
UNLOCK_THREAD (thread);
- CloseHandle (wait->handles [i]);
+ mono_threads_close_thread_handle (wait->handles [i]);
wait->threads [i] = NULL; /* ignore this thread in next loop */
continue;
}
#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
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
*/
thread->state & ThreadState_Background)
ExitThread (1);
#endif
-
if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
return NULL;
InterlockedIncrement (&thread_interruption_requested);
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)
{
if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
return MonoResumeThread;
+ /*
+ The target thread is running at least one protected block, which must not be interrupted, so we give up.
+ The protected block code will give them a chance when appropriate.
+ */
+ if (thread->abort_protected_block_count)
+ return MonoResumeThread;
+
/*someone is already interrupting it*/
if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
return MonoResumeThread;
/* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
* since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
+ /*
+ Self aborts ignore the protected block logic and raise the TAE regardless. This is verified by one of the tests in mono/tests/abort-cctor.cs.
+ */
exc = mono_thread_request_interruption (TRUE);
if (exc)
mono_error_set_exception_instance (error, exc);
}
}
}
+
+void
+mono_threads_begin_abort_protected_block (void)
+{
+ MonoInternalThread *thread;
+
+ thread = mono_thread_internal_current ();
+ ++thread->abort_protected_block_count;
+ mono_memory_barrier ();
+}
+
+void
+mono_threads_end_abort_protected_block (void)
+{
+ MonoInternalThread *thread;
+
+ thread = mono_thread_internal_current ();
+
+ mono_memory_barrier ();
+ --thread->abort_protected_block_count;
+}
+
+MonoException*
+mono_thread_try_resume_interruption (void)
+{
+ MonoInternalThread *thread;
+
+ thread = mono_thread_internal_current ();
+ if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
+ return NULL;
+
+ return mono_thread_resume_interruption ();
+}
+
+/* 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;
+}
+
+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));
+}