X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=256b96ce470d86f7aa4130a15c66dc0b5844f767;hb=089d5fed1917f774dfd602b2e3e4f5a60285a349;hp=9a617a433cba0cad2a0233f729763868ab5eb837;hpb=3cfee96d060b1b0098598145ab70076725ad399d;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 9a617a433cb..256b96ce470 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -31,11 +31,16 @@ #include #include #include +#include #include -#undef THREAD_DEBUG -#undef THREAD_WAIT_DEBUG +/*#define THREAD_DEBUG(a) do { a; } while (0)*/ +#define THREAD_DEBUG(a) +/*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/ +#define THREAD_WAIT_DEBUG(a) +/*#define LIBGC_DEBUG(a) do { a; } while (0)*/ +#define LIBGC_DEBUG(a) struct StartInfo { @@ -50,6 +55,11 @@ typedef union { gint32 ival; gfloat fval; } IntFloatUnion; + +typedef union { + gint64 ival; + gdouble fval; +} LongDoubleUnion; typedef struct { int idx; @@ -86,7 +96,7 @@ static guint32 current_object_key = -1; /* we need to use both the Tls* functions and __thread because * the gc needs to see all the threads */ -static __thread MonoThread * tls_current_object; +static __thread MonoThread * tls_current_object MONO_TLS_FAST; #define SET_CURRENT_OBJECT(x) do { \ tls_current_object = x; \ TlsSetValue (current_object_key, x); \ @@ -109,9 +119,6 @@ static MonoThreadCleanupFunc mono_thread_cleanup = NULL; /* function called when a new thread has been created */ static MonoThreadCallbacks *mono_thread_callbacks = NULL; -/* The TLS key that holds the LocalDataStoreSlot hash in each thread */ -static guint32 slothash_key = -1; - /* The default stack size for each thread */ static guint32 default_stacksize = 0; #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize) @@ -119,22 +126,32 @@ static guint32 default_stacksize = 0; static void thread_adjust_static_data (MonoThread *thread); static void mono_init_static_data_info (StaticDataInfo *static_data); static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align); +static gboolean mono_thread_resume (MonoThread* thread); +static void mono_thread_start (MonoThread *thread); /* Spin lock for InterlockedXXX 64 bit functions */ static CRITICAL_SECTION interlocked_mutex; -/* Controls access to interruption flag */ -static CRITICAL_SECTION interruption_mutex; - /* global count of thread interruptions requested */ static gint32 thread_interruption_requested = 0; +/* Event signaled when a thread changes its background mode */ +static HANDLE background_change_event; + guint32 mono_thread_get_tls_key (void) { return current_object_key; } +gint32 +mono_thread_get_tls_offset (void) +{ + int offset; + MONO_THREAD_VAR_OFFSET (tls_current_object,offset); + return offset; +} + /* handle_store() and handle_remove() manage the array of threads that * still need to be waited for when the main thread exits. */ @@ -142,10 +159,7 @@ static void handle_store(MonoThread *thread) { EnterCriticalSection(&threads_mutex); -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread, - thread->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": thread %p ID %"G_GSIZE_FORMAT, thread, (gsize)thread->tid)); if(threads==NULL) { MONO_GC_REGISTER_ROOT (threads); @@ -155,20 +169,19 @@ static void handle_store(MonoThread *thread) /* 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, GUINT_TO_POINTER(thread->tid), thread); + mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->tid), + thread); LeaveCriticalSection(&threads_mutex); } -static void handle_remove(guint32 tid) +static void handle_remove(gsize tid) { -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread ID %d", tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": thread ID %"G_GSIZE_FORMAT, tid)); EnterCriticalSection(&threads_mutex); if (threads) - mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid)); + mono_g_hash_table_remove (threads, (gpointer)tid); LeaveCriticalSection(&threads_mutex); @@ -189,6 +202,8 @@ static void handle_remove(guint32 tid) static void thread_cleanup (MonoThread *thread) { + g_assert (thread != NULL); + mono_release_type_locks (thread); if (!mono_monitor_enter (thread->synch_lock)) @@ -212,7 +227,7 @@ static void thread_cleanup (MonoThread *thread) mono_thread_cleanup (thread); } -static guint32 start_wrapper(void *data) +static guint32 WINAPI start_wrapper(void *data) { struct StartInfo *start_info=(struct StartInfo *)data; guint32 (*start_func)(void *); @@ -220,11 +235,8 @@ static guint32 start_wrapper(void *data) guint32 tid; MonoThread *thread=start_info->obj; MonoObject *start_delegate = start_info->delegate; - -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper", - GetCurrentThreadId ()); -#endif + + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Start wrapper", GetCurrentThreadId ())); /* We can be sure start_info->obj->tid and * start_info->obj->handle have been set, because the thread @@ -252,17 +264,13 @@ static guint32 start_wrapper(void *data) mono_thread_new_init (tid, &tid, start_func); thread->stack_ptr = &tid; -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": (%d,%d) Setting thread stack to %p", - GetCurrentThreadId (), getpid (), thread->stack_ptr); -#endif + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ": (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", + GetCurrentThreadId (), getpid (), thread->stack_ptr)); -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": (%d) Setting current_object_key to %p", - GetCurrentThreadId (), thread); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ": (%"G_GSIZE_FORMAT") Setting current_object_key to %p", + GetCurrentThreadId (), thread)); mono_profiler_thread_start (tid); @@ -280,7 +288,8 @@ static guint32 start_wrapper(void *data) thread_adjust_static_data (thread); #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION "start_wrapper for %d\n", thread->tid); + g_message (G_GNUC_PRETTY_FUNCTION "start_wrapper for %"G_GSIZE_FORMAT, + thread->tid); #endif /* start_func is set only for unamanged start functions */ @@ -293,16 +302,13 @@ static guint32 start_wrapper(void *data) /* we may want to handle the exception here. See comment below on unhandled exceptions */ mono_runtime_delegate_invoke (start_delegate, args, NULL); } -#ifdef PLATFORM_WIN32 + /* If the thread calls ExitThread at all, this remaining code * will not be executed, but the main thread will eventually * call thread_cleanup() on this thread's behalf. */ -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper terminating", - GetCurrentThreadId ()); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Start wrapper terminating", GetCurrentThreadId ())); /* Remove the reference to the thread object in the TLS data, * so the thread object can be finalized. This won't be @@ -313,14 +319,13 @@ static guint32 start_wrapper(void *data) * to TLS data.) */ SET_CURRENT_OBJECT (NULL); -#endif - + thread_cleanup (thread); return(0); } -void mono_thread_new_init (guint32 tid, gpointer stack_start, gpointer func) +void mono_thread_new_init (gsize tid, gpointer stack_start, gpointer func) { if (mono_thread_start_cb) { mono_thread_start_cb (tid, stack_start, func); @@ -345,7 +350,7 @@ void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg) MonoThread *thread; HANDLE thread_handle; struct StartInfo *start_info; - guint32 tid; + gsize tid; thread=(MonoThread *)mono_object_new (domain, mono_defaults.thread_class); @@ -359,18 +364,15 @@ void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg) /* Create suspended, so we can do some housekeeping before the thread * starts */ -#if defined(PLATFORM_WIN32) && defined(HAVE_BOEHM_GC) - thread_handle = GC_CreateThread(NULL, default_stacksize_for_thread (thread), start_wrapper, start_info, - CREATE_SUSPENDED, &tid); -#else - thread_handle = CreateThread(NULL, default_stacksize_for_thread (thread), start_wrapper, start_info, + thread_handle = CreateThread(NULL, default_stacksize_for_thread (thread), (LPTHREAD_START_ROUTINE)start_wrapper, start_info, CREATE_SUSPENDED, &tid); -#endif -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)", - tid, thread_handle); -#endif - g_assert (thread_handle); + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Started thread ID %"G_GSIZE_FORMAT" (handle %p)", + tid, thread_handle)); + if (thread_handle == NULL) { + /* The thread couldn't be created, so throw an exception */ + mono_raise_exception (mono_get_exception_execution_engine ("Couldn't create thread")); + return; + } thread->handle=thread_handle; thread->tid=tid; @@ -387,13 +389,17 @@ mono_thread_attach (MonoDomain *domain) { MonoThread *thread; HANDLE thread_handle; - guint32 tid; + gsize tid; if ((thread = mono_thread_current ())) { /* Already attached */ return thread; } + if (!mono_gc_register_thread (&domain)) { + g_error ("Thread %p 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.", GetCurrentThread ()); + } + thread = (MonoThread *)mono_object_new (domain, mono_defaults.thread_class); @@ -415,18 +421,13 @@ mono_thread_attach (MonoDomain *domain) thread->tid=tid; thread->synch_lock=mono_object_new (domain, mono_defaults.object_class); -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Attached thread ID %d (handle %p)", - tid, thread_handle); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", + tid, thread_handle)); handle_store(thread); -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": (%d) Setting current_object_key to %p", - GetCurrentThreadId (), thread); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Setting current_object_key to %p", + GetCurrentThreadId (), thread)); SET_CURRENT_OBJECT (thread); mono_domain_set (domain, TRUE); @@ -445,9 +446,7 @@ mono_thread_detach (MonoThread *thread) { g_return_if_fail (thread != NULL); -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION "mono_thread_detach for %d\n", thread->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION "mono_thread_detach for %"G_GSIZE_FORMAT, thread->tid)); SET_CURRENT_OBJECT (NULL); thread_cleanup (thread); @@ -474,63 +473,46 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this, struct StartInfo *start_info; MonoMethod *im; HANDLE thread; - guint32 tid; + gsize tid; MONO_ARCH_SAVE_REGS; -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Trying to start a new thread: this (%p) start (%p)", - this, start); -#endif + THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION + ": Trying to start a new thread: this (%p) start (%p)", this, start)); -/* FIXME: remove the code inside BROKEN_THREAD_START once martin gets rid of the - * thread_start_compile_func stuff. - */ -#define BROKEN_THREAD_START -#ifdef BROKEN_THREAD_START - im = mono_get_delegate_invoke (start->vtable->klass); - im = mono_marshal_get_delegate_invoke (im); - if (mono_thread_callbacks) - start_func = (* mono_thread_callbacks->thread_start_compile_func) (im); - else - start_func = mono_compile_method (im); + mono_monitor_enter (this->synch_lock); - if(start_func==NULL) { - g_warning(G_GNUC_PRETTY_FUNCTION - ": Can't locate start method!"); - return(NULL); - } else { -#else + if ((this->state & ThreadState_Unstarted) == 0) { + mono_monitor_exit (this->synch_lock); + mono_raise_exception (mono_get_exception_thread_state ("Thread has already been started.")); + return NULL; + } + + if ((this->state & ThreadState_Aborted) != 0) { + mono_monitor_exit (this->synch_lock); + return this; + } start_func = NULL; { -#endif /* This is freed in start_wrapper */ start_info = g_new0 (struct StartInfo, 1); start_info->func = start_func; -#ifdef BROKEN_THREAD_START - start_info->start_arg = start; -#else start_info->start_arg = this->start_obj; -#endif start_info->delegate = start; start_info->obj = this; start_info->domain = mono_domain_get (); this->start_notify=CreateSemaphore (NULL, 0, 0x7fffffff, NULL); if(this->start_notify==NULL) { + mono_monitor_exit (this->synch_lock); g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ()); return(NULL); } -#if defined(PLATFORM_WIN32) && defined(HAVE_BOEHM_GC) - thread=GC_CreateThread(NULL, default_stacksize_for_thread (this), start_wrapper, start_info, - CREATE_SUSPENDED, &tid); -#else - thread=CreateThread(NULL, default_stacksize_for_thread (this), start_wrapper, start_info, + thread=CreateThread(NULL, default_stacksize_for_thread (this), (LPTHREAD_START_ROUTINE)start_wrapper, start_info, CREATE_SUSPENDED, &tid); -#endif if(thread==NULL) { + mono_monitor_exit (this->synch_lock); g_warning(G_GNUC_PRETTY_FUNCTION ": CreateThread error 0x%x", GetLastError()); return(NULL); @@ -545,11 +527,13 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this, * store the handle till then. */ -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Started thread ID %d (handle %p)", tid, thread); -#endif + mono_thread_start (this); + + this->state &= ~ThreadState_Unstarted; + + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Started thread ID %"G_GSIZE_FORMAT" (handle %p)", tid, thread)); + mono_monitor_exit (this->synch_lock); return(thread); } } @@ -559,61 +543,46 @@ void ves_icall_System_Threading_Thread_Thread_free_internal (MonoThread *this, { MONO_ARCH_SAVE_REGS; -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p", - this, thread); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Closing thread %p, handle %p", this, thread)); CloseHandle (thread); } -void ves_icall_System_Threading_Thread_Start_internal(MonoThread *this, - HANDLE thread) +static void mono_thread_start (MonoThread *thread) { MONO_ARCH_SAVE_REGS; -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Launching thread %p (%d)", - GetCurrentThreadId (), this, this->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", GetCurrentThreadId (), thread, (gsize)thread->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. */ - handle_store(this); + handle_store (thread); if (mono_thread_callbacks) - (* mono_thread_callbacks->start_resume) (this->tid); + (* mono_thread_callbacks->start_resume) (thread->tid); - ResumeThread(thread); + ResumeThread (thread->handle); if (mono_thread_callbacks) - (* mono_thread_callbacks->end_resume) (this->tid); + (* mono_thread_callbacks->end_resume) (thread->tid); - if(this->start_notify!=NULL) { + if(thread->start_notify!=NULL) { /* 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 */ -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": (%d) waiting for thread %p (%d) to start", - GetCurrentThreadId (), this, this->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", GetCurrentThreadId (), thread, (gsize)thread->tid)); - WaitForSingleObjectEx (this->start_notify, INFINITE, FALSE); - CloseHandle (this->start_notify); - this->start_notify=NULL; + WaitForSingleObjectEx (thread->start_notify, INFINITE, FALSE); + CloseHandle (thread->start_notify); + thread->start_notify = NULL; } -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": (%d) Done launching thread %p (%d)", - GetCurrentThreadId (), this, this->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", GetCurrentThreadId (), thread, (gsize)thread->tid)); } void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) @@ -622,9 +591,7 @@ void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) MONO_ARCH_SAVE_REGS; -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms)); mono_monitor_enter (thread->synch_lock); thread->state |= ThreadState_WaitSleepJoin; @@ -648,17 +615,28 @@ ves_icall_System_Threading_Thread_GetDomainID (void) MonoString* ves_icall_System_Threading_Thread_GetName_internal (MonoThread *this_obj) { + MonoString* str; + mono_monitor_enter (this_obj->synch_lock); + if (!this_obj->name) - return NULL; + str = NULL; else - return mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len); + str = mono_string_new_utf16 (mono_domain_get (), this_obj->name, this_obj->name_len); + + mono_monitor_exit (this_obj->synch_lock); + return str; } void ves_icall_System_Threading_Thread_SetName_internal (MonoThread *this_obj, MonoString *name) { - if (this_obj->name) - g_free (this_obj->name); + mono_monitor_enter (this_obj->synch_lock); + + if (this_obj->name) { + mono_monitor_exit (this_obj->synch_lock); + mono_raise_exception (mono_get_exception_invalid_operation ("Thread.Name can only be set once.")); + return; + } if (name) { this_obj->name = g_new (gunichar2, mono_string_length (name)); memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2); @@ -666,6 +644,8 @@ ves_icall_System_Threading_Thread_SetName_internal (MonoThread *this_obj, MonoSt } else this_obj->name = NULL; + + mono_monitor_exit (this_obj->synch_lock); } MonoObject* @@ -823,16 +803,8 @@ ves_icall_System_Threading_Thread_SetSerializedCurrentUICulture (MonoThread *thi MonoThread * mono_thread_current (void) { -#ifdef THREAD_DEBUG - MonoThread *thread; - MONO_ARCH_SAVE_REGS; - thread = GET_CURRENT_OBJECT (); - g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread); - return thread; -#else - MONO_ARCH_SAVE_REGS; + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", GET_CURRENT_OBJECT ())); return GET_CURRENT_OBJECT (); -#endif } gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, @@ -843,16 +815,21 @@ gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, MONO_ARCH_SAVE_REGS; mono_monitor_enter (this->synch_lock); + + if ((this->state & ThreadState_Unstarted) != 0) { + mono_monitor_exit (this->synch_lock); + mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started.")); + return FALSE; + } + this->state |= ThreadState_WaitSleepJoin; mono_monitor_exit (this->synch_lock); if(ms== -1) { ms=INFINITE; } -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms", - thread, ms); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": joining thread handle %p, %d ms", + thread, ms)); ret=WaitForSingleObjectEx (thread, ms, TRUE); @@ -861,47 +838,16 @@ gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, mono_monitor_exit (this->synch_lock); if(ret==WAIT_OBJECT_0) { -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": join successful"); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": join successful")); return(TRUE); } -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": join failed"); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": join failed")); return(FALSE); } -void ves_icall_System_Threading_Thread_SlotHash_store(MonoObject *data) -{ - MONO_ARCH_SAVE_REGS; - -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Storing key %p", data); -#endif - - /* Object location stored here */ - TlsSetValue(slothash_key, data); -} - -MonoObject *ves_icall_System_Threading_Thread_SlotHash_lookup(void) -{ - MonoObject *data; - - MONO_ARCH_SAVE_REGS; - - data=TlsGetValue(slothash_key); - -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Retrieved key %p", data); -#endif - - return(data); -} - /* FIXME: exitContext isnt documented */ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext) { @@ -946,10 +892,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ g_free(handles); if(ret==WAIT_FAILED) { -#ifdef THREAD_WAIT_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed", - GetCurrentThreadId ()); -#endif + THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait failed", GetCurrentThreadId ())); return(FALSE); } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) { /* Do we want to try again if we get @@ -957,10 +900,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ * WaitHandle doesn't give any clues. (We'd have to * fiddle with the timeout if we retry.) */ -#ifdef THREAD_WAIT_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out", - GetCurrentThreadId ()); -#endif + THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait timed out", GetCurrentThreadId ())); return(FALSE); } @@ -1010,10 +950,7 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha g_free(handles); -#ifdef THREAD_WAIT_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) returning %d", - GetCurrentThreadId (), ret); -#endif + THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") returning %d", GetCurrentThreadId (), ret)); /* * These need to be here. See MSDN dos on WaitForMultipleObjects. @@ -1037,10 +974,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this MONO_ARCH_SAVE_REGS; -#ifdef THREAD_WAIT_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) waiting for %p, %d ms", - GetCurrentThreadId (), handle, ms); -#endif + THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") waiting for %p, %d ms", GetCurrentThreadId (), handle, ms)); if(ms== -1) { ms=INFINITE; @@ -1057,10 +991,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this mono_monitor_exit (thread->synch_lock); if(ret==WAIT_FAILED) { -#ifdef THREAD_WAIT_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed", - GetCurrentThreadId ()); -#endif + THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait failed", GetCurrentThreadId ())); return(FALSE); } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) { /* Do we want to try again if we get @@ -1068,10 +999,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this * WaitHandle doesn't give any clues. (We'd have to * fiddle with the timeout if we retry.) */ -#ifdef THREAD_WAIT_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait timed out", - GetCurrentThreadId ()); -#endif + THREAD_WAIT_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Wait timed out", GetCurrentThreadId ())); return(FALSE); } @@ -1176,47 +1104,94 @@ gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location) return ret; } -gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location1, gint32 value) +gint32 ves_icall_System_Threading_Interlocked_Exchange_Int (gint32 *location, gint32 value) { MONO_ARCH_SAVE_REGS; - return InterlockedExchange(location1, value); + return InterlockedExchange(location, value); } -MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location1, MonoObject *value) +MonoObject * ves_icall_System_Threading_Interlocked_Exchange_Object (MonoObject **location, MonoObject *value) { MONO_ARCH_SAVE_REGS; - return (MonoObject *) InterlockedExchangePointer((gpointer *) location1, value); + return (MonoObject *) InterlockedExchangePointer((gpointer *) location, value); } -gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location1, gfloat value) +gfloat ves_icall_System_Threading_Interlocked_Exchange_Single (gfloat *location, gfloat value) { IntFloatUnion val, ret; MONO_ARCH_SAVE_REGS; val.fval = value; - ret.ival = InterlockedExchange((gint32 *) location1, val.ival); + ret.ival = InterlockedExchange((gint32 *) location, val.ival); return ret.fval; } -gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand) +gint64 +ves_icall_System_Threading_Interlocked_Exchange_Long (gint64 *location, gint64 value) +{ +#if SIZEOF_VOID_P == 8 + return (gint64) InterlockedExchangePointer((gpointer *) location, (gpointer)value); +#else + gint64 res; + + /* + * According to MSDN, this function is only atomic with regards to the + * other Interlocked functions on 32 bit platforms. + */ + EnterCriticalSection(&interlocked_mutex); + res = *location; + *location = value; + LeaveCriticalSection(&interlocked_mutex); + + return res; +#endif +} + +gdouble +ves_icall_System_Threading_Interlocked_Exchange_Double (gdouble *location, gdouble value) +{ +#if SIZEOF_VOID_P == 8 + LongDoubleUnion val, ret; + + val.fval = value; + ret.ival = (gint64)InterlockedExchangePointer((gpointer *) location, (gpointer)val.ival); + + return ret.fval; +#else + gdouble res; + + /* + * According to MSDN, this function is only atomic with regards to the + * other Interlocked functions on 32 bit platforms. + */ + EnterCriticalSection(&interlocked_mutex); + res = *location; + *location = value; + LeaveCriticalSection(&interlocked_mutex); + + return res; +#endif +} + +gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location, gint32 value, gint32 comparand) { MONO_ARCH_SAVE_REGS; - return InterlockedCompareExchange(location1, value, comparand); + return InterlockedCompareExchange(location, value, comparand); } -MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location1, MonoObject *value, MonoObject *comparand) +MonoObject * ves_icall_System_Threading_Interlocked_CompareExchange_Object (MonoObject **location, MonoObject *value, MonoObject *comparand) { MONO_ARCH_SAVE_REGS; - return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location1, value, comparand); + return (MonoObject *) InterlockedCompareExchangePointer((gpointer *) location, value, comparand); } -gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location1, gfloat value, gfloat comparand) +gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *location, gfloat value, gfloat comparand) { IntFloatUnion val, ret, cmp; @@ -1224,33 +1199,183 @@ gfloat ves_icall_System_Threading_Interlocked_CompareExchange_Single (gfloat *lo val.fval = value; cmp.fval = comparand; - ret.ival = InterlockedCompareExchange((gint32 *) location1, val.ival, cmp.ival); + ret.ival = InterlockedCompareExchange((gint32 *) location, val.ival, cmp.ival); return ret.fval; } +gdouble +ves_icall_System_Threading_Interlocked_CompareExchange_Double (gdouble *location, gdouble value, gdouble comparand) +{ +#if SIZEOF_VOID_P == 8 + LongDoubleUnion val, comp, ret; + + val.fval = value; + comp.fval = comparand; + ret.ival = (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)val.ival, (gpointer)comp.ival); + + return ret.fval; +#else + gdouble old; + + EnterCriticalSection(&interlocked_mutex); + old = *location; + if (old == comparand) + *location = value; + LeaveCriticalSection(&interlocked_mutex); + + return old; +#endif +} + +gint64 +ves_icall_System_Threading_Interlocked_CompareExchange_Long (gint64 *location, gint64 value, gint64 comparand) +{ +#if SIZEOF_VOID_P == 8 + return (gint64)InterlockedCompareExchangePointer((gpointer *) location, (gpointer)value, (gpointer)comparand); +#else + gint64 old; + + EnterCriticalSection(&interlocked_mutex); + old = *location; + if (old == comparand) + *location = value; + LeaveCriticalSection(&interlocked_mutex); + + return old; +#endif +} + +gint32 +ves_icall_System_Threading_Interlocked_Add_Int (gint32 *location, gint32 value) +{ +#if SIZEOF_VOID_P == 8 + /* Should be implemented as a JIT intrinsic */ + mono_raise_exception (mono_get_exception_not_implemented (NULL)); + return 0; +#else + gint32 orig; + + EnterCriticalSection(&interlocked_mutex); + orig = *location; + *location = orig + value; + LeaveCriticalSection(&interlocked_mutex); + + return orig; +#endif +} + +gint64 +ves_icall_System_Threading_Interlocked_Add_Long (gint64 *location, gint64 value) +{ +#if SIZEOF_VOID_P == 8 + /* Should be implemented as a JIT intrinsic */ + mono_raise_exception (mono_get_exception_not_implemented (NULL)); + return 0; +#else + gint64 orig; + + EnterCriticalSection(&interlocked_mutex); + orig = *location; + *location = orig + value; + LeaveCriticalSection(&interlocked_mutex); + + return orig; +#endif +} + +gint64 +ves_icall_System_Threading_Interlocked_Read_Long (gint64 *location) +{ +#if SIZEOF_VOID_P == 8 + /* 64 bit reads are already atomic */ + return *location; +#else + gint64 res; + + EnterCriticalSection(&interlocked_mutex); + res = *location; + LeaveCriticalSection(&interlocked_mutex); + + return res; +#endif +} + +void +ves_icall_System_Threading_Thread_ClrState (MonoThread* this, guint32 state) +{ + mono_monitor_enter (this->synch_lock); + this->state &= ~state; + if (state & ThreadState_Background) { + /* If the thread changes the background mode, the main thread has to + * be notified, since it has to rebuild the list of threads to + * wait for. + */ + SetEvent (background_change_event); + } + mono_monitor_exit (this->synch_lock); +} + +void +ves_icall_System_Threading_Thread_SetState (MonoThread* this, guint32 state) +{ + mono_monitor_enter (this->synch_lock); + this->state |= state; + if (state & ThreadState_Background) { + /* If the thread changes the background mode, the main thread has to + * be notified, since it has to rebuild the list of threads to + * wait for. + */ + SetEvent (background_change_event); + } + mono_monitor_exit (this->synch_lock); +} + +guint32 +ves_icall_System_Threading_Thread_GetState (MonoThread* this) +{ + guint32 state; + mono_monitor_enter (this->synch_lock); + state = this->state; + mono_monitor_exit (this->synch_lock); + return state; +} + int mono_thread_get_abort_signal (void) { -#ifdef __MINGW32__ +#if defined (__MINGW32__) || defined (_MSC_VER) return -1; #else #ifndef SIGRTMIN return SIGUSR1; #else + static int abort_signum = -1; + int i; + if (abort_signum != -1) + return abort_signum; + /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */ + for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) { + struct sigaction sinfo; + sigaction (i, NULL, &sinfo); + if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) { + abort_signum = i; + return i; + } + } + /* fallback to the old way */ return SIGRTMIN; #endif -#endif /* __MINGW32__ */ +#endif /*defined (__MINGW32__) || defined (_MSC_VER) */ } -#ifdef __MINGW32__ -static CALLBACK void interruption_request_apc (ULONG_PTR param) +#if defined (__MINGW32__) || defined (_MSC_VER) +static void CALLBACK interruption_request_apc (ULONG_PTR param) { MonoException* exc = mono_thread_request_interruption (FALSE); if (exc) mono_raise_exception (exc); - return 0; } -#endif /* __MINGW32__ */ +#endif /* defined (__MINGW32__) || defined (_MSC_VER) */ /* * signal_thread_state_change @@ -1267,16 +1392,16 @@ static void signal_thread_state_change (MonoThread *thread) mono_raise_exception (exc); } -#ifdef __MINGW32__ - QueueUserAPC (interruption_request_apc, thread->handle, NULL); +#if defined (__MINGW32__) || defined (_MSC_VER) + QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL); #else /* fixme: store the state somewhere */ #ifdef PTHREAD_POINTER_ID - pthread_kill (GUINT_TO_POINTER(thread->tid), mono_thread_get_abort_signal ()); + pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ()); #else pthread_kill (thread->tid, mono_thread_get_abort_signal ()); #endif -#endif /* __MINGW32__ */ +#endif /* defined (__MINGW32__) || defined (__MSC_VER) */ } void @@ -1293,20 +1418,22 @@ ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state) return; } + if ((thread->state & ThreadState_Unstarted) != 0) { + thread->state |= ThreadState_Aborted; + mono_monitor_exit (thread->synch_lock); + return; + } + thread->state |= ThreadState_AbortRequested; thread->abort_state = state; thread->abort_exc = NULL; mono_monitor_exit (thread->synch_lock); -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": (%d) Abort requested for %p (%d)", GetCurrentThreadId (), - thread, thread->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", GetCurrentThreadId (), thread, (gsize)thread->tid)); /* Make sure the thread is awake */ - ves_icall_System_Threading_Thread_Resume (thread); + mono_thread_resume (thread); signal_thread_state_change (thread); } @@ -1319,7 +1446,7 @@ ves_icall_System_Threading_Thread_ResetAbort (void) MONO_ARCH_SAVE_REGS; mono_monitor_enter (thread->synch_lock); - + thread->state &= ~ThreadState_AbortRequested; if (!thread->abort_exc) { @@ -1334,29 +1461,45 @@ ves_icall_System_Threading_Thread_ResetAbort (void) mono_monitor_exit (thread->synch_lock); } -void -ves_icall_System_Threading_Thread_Suspend (MonoThread *thread) +static gboolean +mono_thread_suspend (MonoThread *thread) { MONO_ARCH_SAVE_REGS; mono_monitor_enter (thread->synch_lock); + if ((thread->state & ThreadState_Unstarted) != 0 || + (thread->state & ThreadState_Aborted) != 0 || + (thread->state & ThreadState_Stopped) != 0) + { + mono_monitor_exit (thread->synch_lock); + return FALSE; + } + if ((thread->state & ThreadState_Suspended) != 0 || (thread->state & ThreadState_SuspendRequested) != 0 || (thread->state & ThreadState_StopRequested) != 0) { mono_monitor_exit (thread->synch_lock); - return; + return TRUE; } thread->state |= ThreadState_SuspendRequested; mono_monitor_exit (thread->synch_lock); signal_thread_state_change (thread); + return TRUE; } void -ves_icall_System_Threading_Thread_Resume (MonoThread *thread) +ves_icall_System_Threading_Thread_Suspend (MonoThread *thread) +{ + if (!mono_thread_suspend (thread)) + mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead.")); +} + +static gboolean +mono_thread_resume (MonoThread *thread) { MONO_ARCH_SAVE_REGS; @@ -1365,13 +1508,16 @@ ves_icall_System_Threading_Thread_Resume (MonoThread *thread) if ((thread->state & ThreadState_SuspendRequested) != 0) { thread->state &= ~ThreadState_SuspendRequested; mono_monitor_exit (thread->synch_lock); - return; + return TRUE; } - - if ((thread->state & ThreadState_Suspended) == 0) + + if ((thread->state & ThreadState_Suspended) == 0 || + (thread->state & ThreadState_Unstarted) != 0 || + (thread->state & ThreadState_Aborted) != 0 || + (thread->state & ThreadState_Stopped) != 0) { mono_monitor_exit (thread->synch_lock); - return; + return FALSE; } thread->resume_event = CreateEvent (NULL, TRUE, FALSE, NULL); @@ -1385,6 +1531,15 @@ ves_icall_System_Threading_Thread_Resume (MonoThread *thread) WaitForSingleObject (thread->resume_event, INFINITE); CloseHandle (thread->resume_event); thread->resume_event = NULL; + + return TRUE; +} + +void +ves_icall_System_Threading_Thread_Resume (MonoThread *thread) +{ + if (!mono_thread_resume (thread)) + mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead.")); } static gboolean @@ -1423,8 +1578,8 @@ void mono_thread_stop (MonoThread *thread) } /* Make sure the thread is awake */ - ves_icall_System_Threading_Thread_Resume (thread); - + mono_thread_resume (thread); + thread->state |= ThreadState_StopRequested; thread->state &= ~ThreadState_AbortRequested; @@ -1499,22 +1654,18 @@ void mono_thread_init (MonoThreadStartCB start_cb, InitializeCriticalSection(&threads_mutex); InitializeCriticalSection(&interlocked_mutex); InitializeCriticalSection(&contexts_mutex); - InitializeCriticalSection(&interruption_mutex); + background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL); mono_init_static_data_info (&thread_static_info); mono_init_static_data_info (&context_static_info); current_object_key=TlsAlloc(); -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d", - current_object_key); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Allocated current_object_key %d", + current_object_key)); mono_thread_start_cb = start_cb; mono_thread_attach_cb = attach_cb; - slothash_key=TlsAlloc(); - /* Get a pseudo handle to the current process. This is just a * kludge so that wapi can build a process handle if needed. * As a pseudo handle is returned, we don't need to clean @@ -1534,12 +1685,17 @@ void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks) mono_thread_callbacks = callbacks; } -#ifdef THREAD_DEBUG +G_GNUC_UNUSED static void print_tids (gpointer key, gpointer value, gpointer user) { - g_message ("Waiting for: %d", GPOINTER_TO_UINT(key)); + /* GPOINTER_TO_UINT breaks horribly if sizeof(void *) > + * sizeof(uint) and a cast to uint would overflow + */ + /* Older versions of glib don't have G_GSIZE_FORMAT, so just + * print this as a pointer. + */ + g_message ("Waiting for: %p", key); } -#endif struct wait_data { @@ -1552,18 +1708,14 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout) { guint32 i, ret; -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": %d threads to wait for in this batch", wait->num); -#endif + THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION + ": %d threads to wait for in this batch", wait->num)); ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, FALSE); if(ret==WAIT_FAILED) { /* See the comment in build_wait_tids() */ -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed"); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed")); return; } @@ -1574,9 +1726,9 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout) return; for(i=0; inum; i++) { - guint32 tid=wait->threads[i]->tid; + gsize tid = wait->threads[i]->tid; - if(mono_g_hash_table_lookup (threads, GUINT_TO_POINTER(tid))!=NULL) { + 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 @@ -1588,15 +1740,57 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout) * same thread.) */ -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": cleaning up after thread %d", tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up after thread %"G_GSIZE_FORMAT, tid)); thread_cleanup (wait->threads[i]); } } } +static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout) +{ + guint32 i, ret, count; + + THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION + ": %d threads to wait for in this batch", wait->num)); + + /* Add the thread state change event, so it wakes up if a thread changes + * to background mode. + */ + count = wait->num; + if (count < MAXIMUM_WAIT_OBJECTS) { + wait->handles [count] = background_change_event; + count++; + } + + ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, FALSE); + + if(ret==WAIT_FAILED) { + /* See the comment in build_wait_tids() */ + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Wait failed")); + return; + } + + for(i=0; inum; i++) + CloseHandle (wait->handles[i]); + + if (ret == WAIT_TIMEOUT) + return; + + if (ret < wait->num) { + gsize tid = wait->threads[ret]->tid; + EnterCriticalSection (&threads_mutex); + if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) { + /* See comment in wait_for_tids about thread cleanup */ + LeaveCriticalSection (&threads_mutex); + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ": cleaning up after thread %"G_GSIZE_FORMAT, tid)); + thread_cleanup (wait->threads [ret]); + } + else + LeaveCriticalSection (&threads_mutex); + } +} + static void build_wait_tids (gpointer key, gpointer value, gpointer user) { struct wait_data *wait=(struct wait_data *)user; @@ -1606,8 +1800,12 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user) MonoThread *thread=(MonoThread *)value; /* Ignore background threads, we abort them later */ - if (thread->state & ThreadState_Background) + mono_monitor_enter (thread->synch_lock); + if (thread->state & ThreadState_Background) { + mono_monitor_exit (thread->synch_lock); return; /* just leave, ignore */ + } + mono_monitor_exit (thread->synch_lock); if (mono_gc_is_finalizer_thread (thread)) return; @@ -1636,32 +1834,28 @@ static gboolean remove_and_abort_threads (gpointer key, gpointer value, gpointer user) { struct wait_data *wait=(struct wait_data *)user; - guint32 self = GetCurrentThreadId (); + gsize self = GetCurrentThreadId (); MonoThread *thread = (MonoThread *) value; HANDLE handle; /* The finalizer thread is not a background thread */ - if (thread->tid != self && thread->state & ThreadState_Background) { + if (thread->tid != self && (thread->state & ThreadState_Background) != 0) { handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid); if (handle == NULL) return FALSE; - wait->handles[wait->num]=thread->handle; - wait->threads[wait->num]=thread; - wait->num++; - if(thread->state & ThreadState_AbortRequested || thread->state & ThreadState_Aborted) { -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Thread id %d already aborting", thread->tid); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Thread id %"G_GSIZE_FORMAT" already aborting", (gsize)thread->tid)); return(TRUE); } - -#ifdef THREAD_DEBUG - g_print (G_GNUC_PRETTY_FUNCTION ": Aborting id: %d\n", thread->tid); -#endif + + wait->handles[wait->num]=thread->handle; + wait->threads[wait->num]=thread; + wait->num++; + + THREAD_DEBUG (g_print (G_GNUC_PRETTY_FUNCTION ": Aborting id: %"G_GSIZE_FORMAT"\n", (gsize)thread->tid)); mono_thread_stop (thread); return TRUE; } @@ -1674,15 +1868,11 @@ void mono_thread_manage (void) struct wait_data *wait=g_new0 (struct wait_data, 1); /* join each thread that's still running */ -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": Joining each running thread..."); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Joining each running thread...")); EnterCriticalSection (&threads_mutex); if(threads==NULL) { -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": No threads"); -#endif + THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION ": No threads")); LeaveCriticalSection (&threads_mutex); return; } @@ -1690,22 +1880,24 @@ void mono_thread_manage (void) do { EnterCriticalSection (&threads_mutex); -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ":There are %d threads to join", - mono_g_hash_table_size (threads)); - mono_g_hash_table_foreach (threads, print_tids, NULL); -#endif + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ":There are %d threads to join", mono_g_hash_table_size (threads)); + mono_g_hash_table_foreach (threads, print_tids, NULL)); + ResetEvent (background_change_event); wait->num=0; mono_g_hash_table_foreach (threads, build_wait_tids, wait); LeaveCriticalSection (&threads_mutex); if(wait->num>0) { /* Something to wait for */ - wait_for_tids (wait, INFINITE); + wait_for_tids_or_state_change (wait, INFINITE); } + THREAD_DEBUG (g_message ("I have %d threads after waiting.", wait->num)); } while(wait->num>0); - + + mono_runtime_set_shutting_down (); + + THREAD_DEBUG (g_message ("threadpool cleanup")); mono_thread_pool_cleanup (); EnterCriticalSection(&threads_mutex); @@ -1719,6 +1911,7 @@ void mono_thread_manage (void) LeaveCriticalSection(&threads_mutex); + THREAD_DEBUG (g_message ("wait->num is now %d", wait->num)); if(wait->num>0) { /* Something to wait for */ wait_for_tids (wait, INFINITE); @@ -1738,37 +1931,42 @@ void mono_thread_manage (void) static void terminate_thread (gpointer key, gpointer value, gpointer user) { MonoThread *thread=(MonoThread *)value; - guint32 self=GPOINTER_TO_UINT (user); - if(thread->tid!=self) { + if(thread->tid != (gsize)user) { /*TerminateThread (thread->handle, -1);*/ } } void mono_thread_abort_all_other_threads (void) { - guint32 self=GetCurrentThreadId (); + gsize self = GetCurrentThreadId (); EnterCriticalSection (&threads_mutex); -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort", + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort", mono_g_hash_table_size (threads)); - mono_g_hash_table_foreach (threads, print_tids, NULL); -#endif + mono_g_hash_table_foreach (threads, print_tids, NULL)); - mono_g_hash_table_foreach (threads, terminate_thread, - GUINT_TO_POINTER (self)); + mono_g_hash_table_foreach (threads, terminate_thread, (gpointer)self); LeaveCriticalSection (&threads_mutex); } -static void suspend_thread (gpointer key, gpointer value, gpointer user) +static void +collect_threads (gpointer key, gpointer value, gpointer user_data) { - MonoThread *thread=(MonoThread *)value; - guint32 self=GPOINTER_TO_UINT (user); - - if ((thread->tid!=self) && !mono_gc_is_finalizer_thread (thread)) - SuspendThread (thread->handle); + MonoThread *thread = (MonoThread*)value; + struct wait_data *wait = (struct wait_data*)user_data; + HANDLE handle; + + if (wait->numtid); + if (handle == NULL) + return; + + wait->handles [wait->num] = handle; + wait->threads [wait->num] = thread; + wait->num++; + } } /* @@ -1778,12 +1976,75 @@ static void suspend_thread (gpointer key, gpointer value, gpointer user) */ void mono_thread_suspend_all_other_threads (void) { - guint32 self=GetCurrentThreadId (); + struct wait_data *wait = g_new0 (struct wait_data, 1); + int i, waitnum; + gsize self = GetCurrentThreadId (); + gpointer *events; + guint32 eventidx = 0; + /* + * Make a copy of the hashtable since we can't do anything with + * threads while threads_mutex is held. + */ EnterCriticalSection (&threads_mutex); - mono_g_hash_table_foreach (threads, suspend_thread, - GUINT_TO_POINTER (self)); + mono_g_hash_table_foreach (threads, collect_threads, wait); LeaveCriticalSection (&threads_mutex); + + events = g_new0 (gpointer, wait->num); + waitnum = 0; + /* Get the suspended events that we'll be waiting for */ + for (i = 0; i < wait->num; ++i) { + MonoThread *thread = wait->threads [i]; + + if ((thread->tid == self) || mono_gc_is_finalizer_thread (thread)) { + //CloseHandle (wait->handles [i]); + wait->threads [i] = NULL; /* ignore this thread in next loop */ + continue; + } + + mono_monitor_enter (thread->synch_lock); + + if ((thread->state & ThreadState_Suspended) != 0 || + (thread->state & ThreadState_SuspendRequested) != 0 || + (thread->state & ThreadState_StopRequested) != 0 || + (thread->state & ThreadState_Stopped) != 0) { + mono_monitor_exit (thread->synch_lock); + CloseHandle (wait->handles [i]); + wait->threads [i] = NULL; /* ignore this thread in next loop */ + continue; + } + + /* Convert abort requests into suspend requests */ + if ((thread->state & ThreadState_AbortRequested) != 0) + thread->state &= ~ThreadState_AbortRequested; + + thread->state |= ThreadState_SuspendRequested; + + if (thread->suspended_event == NULL) + thread->suspended_event = CreateEvent (NULL, TRUE, FALSE, NULL); + + events [eventidx++] = thread->suspended_event; + mono_monitor_exit (thread->synch_lock); + + /* Signal the thread to suspend */ + signal_thread_state_change (thread); + } + + WaitForMultipleObjectsEx (eventidx, events, TRUE, INFINITE, FALSE); + for (i = 0; i < wait->num; ++i) { + MonoThread *thread = wait->threads [i]; + + if (thread == NULL) + continue; + + mono_monitor_enter (thread->synch_lock); + CloseHandle (thread->suspended_event); + thread->suspended_event = NULL; + mono_monitor_exit (thread->synch_lock); + } + + g_free (events); + g_free (wait); } /* @@ -1799,7 +2060,7 @@ mono_thread_push_appdomain_ref (MonoDomain *domain) MonoThread *thread = mono_thread_current (); if (thread) { - /* printf ("PUSH REF: %x -> %s.\n", thread->tid, domain->friendly_name); */ + /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */ EnterCriticalSection (&threads_mutex); thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain); LeaveCriticalSection (&threads_mutex); @@ -1812,7 +2073,7 @@ mono_thread_pop_appdomain_ref (void) MonoThread *thread = mono_thread_current (); if (thread) { - /* printf ("POP REF: %x -> %s.\n", thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */ + /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */ EnterCriticalSection (&threads_mutex); /* FIXME: How can the list be empty ? */ if (thread->appdomain_refs) @@ -1844,11 +2105,12 @@ abort_appdomain_thread (gpointer key, gpointer value, gpointer user_data) MonoDomain *domain = data->domain; if (mono_thread_has_appdomain_ref (thread, domain)) { - /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */ HANDLE handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid); if (handle == NULL) return; + /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */ + ves_icall_System_Threading_Thread_Abort (thread, NULL); if(data->wait.num 0) - wait_for_tids (&user_data.wait, timeout); + /* + * We should wait for the threads either to abort, or to leave the + * domain. We can't do the latter, so we wait with a timeout. + */ + wait_for_tids (&user_data.wait, 100); /* Update remaining time */ timeout -= GetTickCount () - start_time; @@ -2140,13 +2406,10 @@ mono_get_special_static_data (guint32 offset) static void gc_stop_world (gpointer key, gpointer value, gpointer user) { MonoThread *thread=(MonoThread *)value; - guint32 self=GPOINTER_TO_UINT (user); -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid); -#endif - - if(thread->tid==self) + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %"G_GSIZE_FORMAT, (gsize)user, (gsize)thread->tid)); + + if(thread->tid == (gsize)user) return; SuspendThread (thread->handle); @@ -2154,16 +2417,14 @@ static void gc_stop_world (gpointer key, gpointer value, gpointer user) void mono_gc_stop_world (void) { - guint32 self=GetCurrentThreadId (); + gsize self = GetCurrentThreadId (); -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads); -#endif + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %p", self, threads)); EnterCriticalSection (&threads_mutex); if (threads != NULL) - mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self)); + mono_g_hash_table_foreach (threads, gc_stop_world, (gpointer)self); LeaveCriticalSection (&threads_mutex); } @@ -2171,13 +2432,10 @@ void mono_gc_stop_world (void) static void gc_start_world (gpointer key, gpointer value, gpointer user) { MonoThread *thread=(MonoThread *)value; - guint32 self=GPOINTER_TO_UINT (user); - -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d", self, thread->tid); -#endif - if(thread->tid==self) + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %"G_GSIZE_FORMAT, (gsize)user, (gsize)thread->tid)); + + if(thread->tid == (gsize)user) return; ResumeThread (thread->handle); @@ -2185,16 +2443,14 @@ static void gc_start_world (gpointer key, gpointer value, gpointer user) void mono_gc_start_world (void) { - guint32 self=GetCurrentThreadId (); + gsize self = GetCurrentThreadId (); -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads); -#endif + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %p", self, threads)); EnterCriticalSection (&threads_mutex); if (threads != NULL) - mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self)); + mono_g_hash_table_foreach (threads, gc_start_world, (gpointer)self); LeaveCriticalSection (&threads_mutex); } @@ -2223,9 +2479,7 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) if (thread->interruption_requested) { /* this will consume pending APC calls */ WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE); - EnterCriticalSection (&interruption_mutex); - thread_interruption_requested--; - LeaveCriticalSection (&interruption_mutex); + InterlockedDecrement (&thread_interruption_requested); thread->interruption_requested = FALSE; } @@ -2239,6 +2493,8 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) thread->state &= ~ThreadState_SuspendRequested; thread->state |= ThreadState_Suspended; thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (thread->suspended_event) + SetEvent (thread->suspended_event); mono_monitor_exit (thread->synch_lock); WaitForSingleObject (thread->suspend_event, INFINITE); @@ -2249,7 +2505,7 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) thread->state &= ~ThreadState_Suspended; /* The thread that requested the resume will have replaced this event - * and will be waiting for it + * and will be waiting for it */ SetEvent (thread->resume_event); mono_monitor_exit (thread->synch_lock); @@ -2293,18 +2549,14 @@ MonoException* mono_thread_request_interruption (gboolean running_managed) /* Can't stop while in unmanaged code. Increase the global interruption request count. When exiting the unmanaged method the count will be checked and the thread will be interrupted. */ - - EnterCriticalSection (&interruption_mutex); - thread_interruption_requested++; - LeaveCriticalSection (&interruption_mutex); - + + InterlockedIncrement (&thread_interruption_requested); thread->interruption_requested = TRUE; mono_monitor_exit (thread->synch_lock); - + /* this will awake the thread if it is in WaitForSingleObject - or similar */ - QueueUserAPC (dummy_apc, thread->handle, NULL); - + or similar */ + QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, NULL); return NULL; } else { @@ -2329,9 +2581,9 @@ static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_p MonoThread *thread = mono_thread_current (); /* The thread may already be stopping */ - if (thread == NULL) + if (thread == NULL) return; - + if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) { MonoException* exc = mono_thread_execute_interruption (thread); if (exc) mono_raise_exception (exc); @@ -2374,16 +2626,12 @@ gint32* mono_thread_interruption_request_flag () static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user) { MonoThread *thread=(MonoThread *)value; - guint32 *selfp=(guint32 *)user, self = *selfp; + gsize *selfp = (gsize *)user, self = *selfp; -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %d - %p", self, thread->tid, thread->stack_ptr); -#endif - - if(thread->tid==self) { -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr); -#endif + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %"G_GSIZE_FORMAT" - %p", self, (gsize)thread->tid, thread->stack_ptr)); + + if(thread->tid == self) { + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %p - %p", selfp, thread->stack_ptr)); GC_push_all_stack (selfp, thread->stack_ptr); return; } @@ -2397,11 +2645,9 @@ static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user) void mono_gc_push_all_stacks (void) { - guint32 self=GetCurrentThreadId (); + gsize self = GetCurrentThreadId (); -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads); -#endif + LIBGC_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": %"G_GSIZE_FORMAT" - %p", self, threads)); EnterCriticalSection (&threads_mutex);