X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=256b96ce470d86f7aa4130a15c66dc0b5844f767;hb=089d5fed1917f774dfd602b2e3e4f5a60285a349;hp=26568288af1c9e340d0a42e64db650f223fd98ad;hpb=7f449e7aff8f374d13db7023c7b326dde01b763d;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 26568288af1..256b96ce470 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -30,17 +30,24 @@ #include #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 { guint32 (*func)(void *); MonoThread *obj; - void *this; + MonoObject *delegate; + void *start_arg; MonoDomain *domain; }; @@ -48,12 +55,20 @@ typedef union { gint32 ival; gfloat fval; } IntFloatUnion; + +typedef union { + gint64 ival; + gdouble fval; +} LongDoubleUnion; typedef struct { int idx; int offset; } StaticDataInfo; +/* Number of cached culture objects in the MonoThread->culture_info array */ +#define NUM_CACHED_CULTURES 4 + /* * The "os_handle" field of the WaitHandle class. */ @@ -77,6 +92,21 @@ static MonoGHashTable *threads=NULL; /* The TLS key that holds the MonoObject assigned to each thread */ static guint32 current_object_key = -1; +#ifdef HAVE_KW_THREAD +/* 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 MONO_TLS_FAST; +#define SET_CURRENT_OBJECT(x) do { \ + tls_current_object = x; \ + TlsSetValue (current_object_key, x); \ +} while (FALSE) +#define GET_CURRENT_OBJECT() tls_current_object +#else +#define SET_CURRENT_OBJECT(x) TlsSetValue (current_object_key, x); +#define GET_CURRENT_OBJECT() (MonoThread*) TlsGetValue (current_object_key); +#endif + /* function called at thread start */ static MonoThreadStartCB mono_thread_start_cb = NULL; @@ -89,25 +119,38 @@ 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) 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. @@ -116,32 +159,29 @@ 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); threads=mono_g_hash_table_new(NULL, NULL); } /* 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); @@ -162,6 +202,10 @@ 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)) return; @@ -173,22 +217,26 @@ static void thread_cleanup (MonoThread *thread) mono_thread_pop_appdomain_ref (); + if (thread->serialized_culture_info) + g_free (thread->serialized_culture_info); + + mono_gc_free_fixed (thread->culture_info); + mono_gc_free_fixed (thread->ui_culture_info); + if (mono_thread_cleanup) 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 *); - void *this; + void *start_arg; guint32 tid; MonoThread *thread=start_info->obj; - -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Start wrapper", - GetCurrentThreadId ()); -#endif + MonoObject *start_delegate = start_info->delegate; + + 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 @@ -198,7 +246,7 @@ static guint32 start_wrapper(void *data) tid=thread->tid; - TlsSetValue (current_object_key, thread); + SET_CURRENT_OBJECT (thread); if (!mono_domain_set (start_info->domain, FALSE)) { /* No point in raising an appdomain_unloaded exception here */ @@ -207,7 +255,7 @@ static guint32 start_wrapper(void *data) } start_func = start_info->func; - this = start_info->this; + start_arg = start_info->start_arg; /* This MUST be called before any managed code can be * executed, as it calls the callback function that (for the @@ -216,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); @@ -244,20 +288,27 @@ 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 (this); -#ifdef PLATFORM_WIN32 + /* start_func is set only for unamanged start functions */ + if (start_func) { + start_func (start_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 */ + mono_runtime_delegate_invoke (start_delegate, args, NULL); + } + /* 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 @@ -267,15 +318,14 @@ static guint32 start_wrapper(void *data) * Boehm GC - the io-layer keeps a GC-visible hash of pointers * to TLS data.) */ - TlsSetValue (current_object_key, NULL); - + SET_CURRENT_OBJECT (NULL); + thread_cleanup (thread); -#endif 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); @@ -300,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); @@ -309,18 +359,20 @@ void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg) start_info->func = func; start_info->obj = thread; start_info->domain = domain; - start_info->this = arg; + start_info->start_arg = arg; /* Create suspended, so we can do some housekeeping before the thread * starts */ - thread_handle = CreateThread(NULL, default_stacksize, start_wrapper, start_info, + thread_handle = CreateThread(NULL, default_stacksize_for_thread (thread), (LPTHREAD_START_ROUTINE)start_wrapper, start_info, CREATE_SUSPENDED, &tid); -#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; @@ -337,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); @@ -352,23 +408,28 @@ mono_thread_attach (MonoDomain *domain) tid=GetCurrentThreadId (); +#ifdef PLATFORM_WIN32 + /* + * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to + * refer to the thread from other threads for things like aborting. + */ + DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, + THREAD_ALL_ACCESS, TRUE, 0); +#endif + thread->handle=thread_handle; 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)); - TlsSetValue (current_object_key, thread); + SET_CURRENT_OBJECT (thread); mono_domain_set (domain, TRUE); thread_adjust_static_data (thread); @@ -385,60 +446,73 @@ 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 - TlsSetValue (current_object_key, NULL); + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION "mono_thread_detach for %"G_GSIZE_FORMAT, thread->tid)); + SET_CURRENT_OBJECT (NULL); thread_cleanup (thread); } +void +mono_thread_exit () +{ + MonoThread *thread = mono_thread_current (); + + SET_CURRENT_OBJECT (NULL); + thread_cleanup (thread); + + /* we could add a callback here for embedders to use. */ + if (thread == mono_thread_get_main ()) + exit (mono_environment_exitcode_get ()); + ExitThread (-1); +} + HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this, MonoObject *start) { - MonoMulticastDelegate *delegate = (MonoMulticastDelegate*)start; guint32 (*start_func)(void *); 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 - - 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); + THREAD_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION + ": Trying to start a new thread: this (%p) start (%p)", this, start)); - if(start_func==NULL) { - g_warning(G_GNUC_PRETTY_FUNCTION - ": Can't locate start method!"); - return(NULL); - } else { + 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 already been started.")); + return NULL; + } + + if ((this->state & ThreadState_Aborted) != 0) { + mono_monitor_exit (this->synch_lock); + return this; + } + start_func = NULL; + { /* This is freed in start_wrapper */ start_info = g_new0 (struct StartInfo, 1); start_info->func = start_func; - start_info->this = delegate; + start_info->start_arg = this->start_obj; + 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); } - thread=CreateThread(NULL, default_stacksize, start_wrapper, start_info, + thread=CreateThread(NULL, default_stacksize_for_thread (this), (LPTHREAD_START_ROUTINE)start_wrapper, start_info, CREATE_SUSPENDED, &tid); if(thread==NULL) { + mono_monitor_exit (this->synch_lock); g_warning(G_GNUC_PRETTY_FUNCTION ": CreateThread error 0x%x", GetLastError()); return(NULL); @@ -453,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); } } @@ -467,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) @@ -530,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; @@ -556,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); @@ -574,23 +644,167 @@ ves_icall_System_Threading_Thread_SetName_internal (MonoThread *this_obj, MonoSt } else this_obj->name = NULL; + + mono_monitor_exit (this_obj->synch_lock); } -MonoThread * -mono_thread_current (void) +MonoObject* +ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoThread *this) { - MonoThread *thread; - - MONO_ARCH_SAVE_REGS; + MonoObject *res; + MonoDomain *domain; + int i; - /* Find the current thread object */ - thread=TlsGetValue (current_object_key); - -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", thread); -#endif + /* No need to lock here */ + if (this->culture_info) { + domain = mono_domain_get (); + for (i = 0; i < NUM_CACHED_CULTURES; ++i) { + res = this->culture_info [i]; + if (res && res->vtable->domain == domain) + return res; + } + } + + return NULL; +} + +MonoArray* +ves_icall_System_Threading_Thread_GetSerializedCurrentCulture (MonoThread *this) +{ + MonoArray *res; + + mono_monitor_enter (this->synch_lock); + if (this->serialized_culture_info) { + res = mono_array_new (mono_domain_get (), mono_defaults.byte_class, this->serialized_culture_info_len); + memcpy (mono_array_addr (res, guint8, 0), this->serialized_culture_info, this->serialized_culture_info_len); + } + else + res = NULL; + mono_monitor_exit (this->synch_lock); + + return res; +} + +void +ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this, MonoObject *culture) +{ + int i; + MonoDomain *domain = mono_domain_get (); + + mono_monitor_enter (this->synch_lock); + if (!this->culture_info) { + this->culture_info = mono_gc_alloc_fixed (sizeof (MonoObject*) * NUM_CACHED_CULTURES, NULL); + } + + for (i = 0; i < NUM_CACHED_CULTURES; ++i) { + if (this->culture_info [i]) { + if (this->culture_info [i]->vtable->domain == domain) + /* Replace */ + break; + } + else + /* Free entry */ + break; + } + if (i < NUM_CACHED_CULTURES) + this->culture_info [i] = culture; + mono_monitor_exit (this->synch_lock); +} + +void +ves_icall_System_Threading_Thread_SetSerializedCurrentCulture (MonoThread *this, MonoArray *arr) +{ + mono_monitor_enter (this->synch_lock); + if (this->serialized_culture_info) + g_free (this->serialized_culture_info); + this->serialized_culture_info = g_new0 (guint8, mono_array_length (arr)); + this->serialized_culture_info_len = mono_array_length (arr); + memcpy (this->serialized_culture_info, mono_array_addr (arr, guint8, 0), mono_array_length (arr)); + mono_monitor_exit (this->synch_lock); +} + + +MonoObject* +ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoThread *this) +{ + MonoObject *res; + MonoDomain *domain; + int i; + + /* No need to lock here */ + if (this->ui_culture_info) { + domain = mono_domain_get (); + for (i = 0; i < NUM_CACHED_CULTURES; ++i) { + res = this->ui_culture_info [i]; + if (res && res->vtable->domain == domain) + return res; + } + } + + return NULL; +} + +MonoArray* +ves_icall_System_Threading_Thread_GetSerializedCurrentUICulture (MonoThread *this) +{ + MonoArray *res; + + mono_monitor_enter (this->synch_lock); + if (this->serialized_ui_culture_info) { + res = mono_array_new (mono_domain_get (), mono_defaults.byte_class, this->serialized_ui_culture_info_len); + memcpy (mono_array_addr (res, guint8, 0), this->serialized_ui_culture_info, this->serialized_ui_culture_info_len); + } + else + res = NULL; + mono_monitor_exit (this->synch_lock); + + return res; +} + +void +ves_icall_System_Threading_Thread_SetCachedCurrentUICulture (MonoThread *this, MonoObject *culture) +{ + int i; + MonoDomain *domain = mono_domain_get (); + + mono_monitor_enter (this->synch_lock); + if (!this->ui_culture_info) { + this->ui_culture_info = mono_gc_alloc_fixed (sizeof (MonoObject*) * NUM_CACHED_CULTURES, NULL); + } + + for (i = 0; i < NUM_CACHED_CULTURES; ++i) { + if (this->ui_culture_info [i]) { + if (this->ui_culture_info [i]->vtable->domain == domain) + /* Replace */ + break; + } + else + /* Free entry */ + break; + } + if (i < NUM_CACHED_CULTURES) + this->ui_culture_info [i] = culture; + mono_monitor_exit (this->synch_lock); +} - return (thread); +void +ves_icall_System_Threading_Thread_SetSerializedCurrentUICulture (MonoThread *this, MonoArray *arr) +{ + mono_monitor_enter (this->synch_lock); + if (this->serialized_ui_culture_info) + g_free (this->serialized_ui_culture_info); + this->serialized_ui_culture_info = g_new0 (guint8, mono_array_length (arr)); + this->serialized_ui_culture_info_len = mono_array_length (arr); + memcpy (this->serialized_ui_culture_info, mono_array_addr (arr, guint8, 0), mono_array_length (arr)); + mono_monitor_exit (this->synch_lock); +} + +/* the jit may read the compiled code of this function */ +MonoThread * +mono_thread_current (void) +{ + THREAD_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", GET_CURRENT_OBJECT ())); + return GET_CURRENT_OBJECT (); } gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, @@ -601,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); @@ -619,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) { @@ -669,6 +857,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ guint32 i; MonoObject *waitHandle; MonoClass *klass; + MonoThread *thread = mono_thread_current (); MONO_ARCH_SAVE_REGS; @@ -689,16 +878,21 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ if(ms== -1) { ms=INFINITE; } + + mono_monitor_enter (thread->synch_lock); + thread->state |= ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); ret=WaitForMultipleObjectsEx(numhandles, handles, TRUE, ms, TRUE); + mono_monitor_enter (thread->synch_lock); + thread->state &= ~ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); + 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 @@ -706,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); } @@ -725,6 +916,7 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha guint32 i; MonoObject *waitHandle; MonoClass *klass; + MonoThread *thread = mono_thread_current (); MONO_ARCH_SAVE_REGS; @@ -746,14 +938,19 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha ms=INFINITE; } + mono_monitor_enter (thread->synch_lock); + thread->state |= ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); + ret=WaitForMultipleObjectsEx(numhandles, handles, FALSE, ms, TRUE); + mono_monitor_enter (thread->synch_lock); + thread->state &= ~ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); + 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. @@ -773,25 +970,28 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext) { guint32 ret; + MonoThread *thread = mono_thread_current (); 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; } + mono_monitor_enter (thread->synch_lock); + thread->state |= ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); + ret=WaitForSingleObjectEx (handle, ms, TRUE); + + mono_monitor_enter (thread->synch_lock); + thread->state &= ~ThreadState_WaitSleepJoin; + 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 @@ -799,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); } @@ -871,22 +1068,18 @@ gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location) gint64 ves_icall_System_Threading_Interlocked_Increment_Long (gint64 *location) { - gint32 lowret; - gint32 highret; + gint64 ret; MONO_ARCH_SAVE_REGS; EnterCriticalSection(&interlocked_mutex); - lowret = InterlockedIncrement((gint32 *) location); - if (0 == lowret) - highret = InterlockedIncrement((gint32 *) location + 1); - else - highret = *((gint32 *) location + 1); - + ret = ++ *location; + LeaveCriticalSection(&interlocked_mutex); - return (gint64) highret << 32 | (gint64) lowret; + + return ret; } gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location) @@ -898,65 +1091,107 @@ gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location) gint64 ves_icall_System_Threading_Interlocked_Decrement_Long (gint64 * location) { - gint32 lowret; - gint32 highret; + gint64 ret; MONO_ARCH_SAVE_REGS; EnterCriticalSection(&interlocked_mutex); - lowret = InterlockedDecrement((gint32 *) location); - if (-1 == lowret) - highret = InterlockedDecrement((gint32 *) location + 1); - else - highret = *((gint32 *) location + 1); - + ret = -- *location; + LeaveCriticalSection(&interlocked_mutex); - return (gint64) highret << 32 | (gint64) lowret; + 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; @@ -964,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 guint32 interruption_request_apc (gpointer 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 @@ -1000,16 +1385,23 @@ static guint32 interruption_request_apc (gpointer param) */ static void signal_thread_state_change (MonoThread *thread) { -#ifdef __MINGW32__ - QueueUserAPC (interruption_request_apc, thread->handle, NULL); + if (thread == mono_thread_current ()) { + /* Do it synchronously */ + MonoException *exc = mono_thread_request_interruption (FALSE); + if (exc) + mono_raise_exception (exc); + } + +#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 @@ -1026,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); } @@ -1052,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) { @@ -1067,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; @@ -1098,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); @@ -1118,6 +1531,39 @@ 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 +find_wrapper (MonoMethod *m, gint no, gint ilo, gboolean managed, gpointer data) +{ + if (managed) + return TRUE; + + if (m->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE || + m->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE || + m->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH) + { + *((gboolean*)data) = TRUE; + return TRUE; + } + return FALSE; +} + +static gboolean +is_running_protected_wrapper (void) +{ + gboolean found = FALSE; + mono_stack_walk (find_wrapper, &found); + return found; } void mono_thread_stop (MonoThread *thread) @@ -1132,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; @@ -1208,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 @@ -1243,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 { @@ -1261,27 +1708,27 @@ 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; } + for(i=0; inum; i++) + CloseHandle (wait->handles[i]); + + if (ret == WAIT_TIMEOUT) + return; for(i=0; inum; i++) { - guint32 tid=wait->threads[i]->tid; - CloseHandle (wait->handles[i]); + 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 @@ -1293,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; @@ -1311,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; @@ -1320,6 +1813,9 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user) if (thread == mono_thread_current ()) return; + if (thread == mono_thread_get_main ()) + return; + handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid); if (handle == NULL) return; @@ -1338,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; } @@ -1376,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; } @@ -1392,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); @@ -1421,10 +1911,19 @@ 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); } + /* + * give the subthreads a chance to really quit (this is mainly needed + * to get correct user and system times from getrusage/wait/time(1)). + * This could be removed if we avoid pthread_detach() and use pthread_join(). + */ +#ifndef PLATFORM_WIN32 + sched_yield (); +#endif g_free (wait); } @@ -1432,30 +1931,122 @@ 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 +collect_threads (gpointer key, gpointer value, gpointer user_data) +{ + 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++; + } +} + +/* + * mono_thread_suspend_all_other_threads: + * + * Suspend all managed threads except the finalizer thread and this thread. + */ +void mono_thread_suspend_all_other_threads (void) +{ + 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, 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); +} + /* * mono_thread_push_appdomain_ref: * @@ -1469,7 +2060,7 @@ mono_thread_push_appdomain_ref (MonoDomain *domain) MonoThread *thread = mono_thread_current (); if (thread) { - /* printf ("PUSH REF: %p -> %s.\n", thread, 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); @@ -1482,7 +2073,7 @@ mono_thread_pop_appdomain_ref (void) MonoThread *thread = mono_thread_current (); if (thread) { - /* printf ("POP REF: %p -> %s.\n", thread, ((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) @@ -1491,7 +2082,7 @@ mono_thread_pop_appdomain_ref (void) } } -static gboolean +gboolean mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain) { gboolean res; @@ -1514,11 +2105,16 @@ 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, 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.numwait.handles [data->wait.num] = thread->handle; + data->wait.handles [data->wait.num] = handle; data->wait.threads [data->wait.num] = thread; data->wait.num++; } else { @@ -1552,7 +2148,11 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout) LeaveCriticalSection (&threads_mutex); if (user_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; @@ -1568,6 +2168,43 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout) return TRUE; } +static void +clear_cached_culture (gpointer key, gpointer value, gpointer user_data) +{ + MonoThread *thread = (MonoThread*)value; + MonoDomain *domain = (MonoDomain*)user_data; + int i; + + /* No locking needed here */ + + if (thread->culture_info) { + for (i = 0; i < NUM_CACHED_CULTURES; ++i) { + if (thread->culture_info [i] && thread->culture_info [i]->vtable->domain == domain) + thread->culture_info [i] = NULL; + } + } + if (thread->ui_culture_info) { + for (i = 0; i < NUM_CACHED_CULTURES; ++i) { + if (thread->ui_culture_info [i] && thread->ui_culture_info [i]->vtable->domain == domain) + thread->ui_culture_info [i] = NULL; + } + } +} + +/* + * mono_threads_clear_cached_culture: + * + * Clear the cached_current_culture from all threads if it is in the + * given appdomain. + */ +void +mono_threads_clear_cached_culture (MonoDomain *domain) +{ + EnterCriticalSection (&threads_mutex); + mono_g_hash_table_foreach (threads, clear_cached_culture, domain); + LeaveCriticalSection (&threads_mutex); +} + /* * mono_thread_get_pending_exception: * @@ -1581,7 +2218,7 @@ mono_thread_get_pending_exception (void) MONO_ARCH_SAVE_REGS; - if (thread && thread->abort_exc) { + if (thread && thread->abort_exc && !is_running_protected_wrapper ()) { /* * FIXME: Clear the abort exception and return an AppDomainUnloaded * exception if the thread no longer references a dying appdomain. @@ -1613,23 +2250,15 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset) gpointer* static_data = *static_data_ptr; if (!static_data) { -#if HAVE_BOEHM_GC - static_data = GC_MALLOC (static_data_size [0]); -#else - static_data = g_malloc0 (static_data_size [0]); -#endif + static_data = mono_gc_alloc_fixed (static_data_size [0], NULL); *static_data_ptr = static_data; static_data [0] = static_data; } - - for (i = 1; i < idx; ++i) { + + for (i = 1; i <= idx; ++i) { if (static_data [i]) continue; -#if HAVE_BOEHM_GC - static_data [i] = GC_MALLOC (static_data_size [i]); -#else - static_data [i] = g_malloc0 (static_data_size [i]); -#endif + static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL); } } @@ -1777,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); @@ -1791,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); } @@ -1808,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); @@ -1822,25 +2443,28 @@ 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); } - +#ifdef __MINGW32__ +static CALLBACK void dummy_apc (ULONG_PTR param) +{ +} +#else static guint32 dummy_apc (gpointer param) { return 0; } +#endif /* * mono_thread_execute_interruption @@ -1851,18 +2475,17 @@ static guint32 dummy_apc (gpointer param) static MonoException* mono_thread_execute_interruption (MonoThread *thread) { mono_monitor_enter (thread->synch_lock); - + 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; } if ((thread->state & ThreadState_AbortRequested) != 0) { - thread->abort_exc = mono_get_exception_thread_abort (); + if (thread->abort_exc == NULL) + thread->abort_exc = mono_get_exception_thread_abort (); mono_monitor_exit (thread->synch_lock); return thread->abort_exc; } @@ -1870,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); @@ -1880,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); @@ -1889,7 +2514,7 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) else if ((thread->state & ThreadState_StopRequested) != 0) { /* FIXME: do this through the JIT? */ mono_monitor_exit (thread->synch_lock); - ExitThread (-1); + mono_thread_exit (); return NULL; } @@ -1920,22 +2545,18 @@ MonoException* mono_thread_request_interruption (gboolean running_managed) return NULL; } - if (!running_managed) { + if (!running_managed || is_running_protected_wrapper ()) { /* 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 { @@ -1955,24 +2576,40 @@ gboolean mono_thread_interruption_requested () return FALSE; } -/* - * Performs the interruption of the current thread, if one has been requested. - */ -void mono_thread_interruption_checkpoint () +static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection) { MonoThread *thread = mono_thread_current (); - + /* The thread may already be stopping */ - if (thread == NULL) + if (thread == NULL) return; - - if (thread->interruption_requested) { + + if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) { MonoException* exc = mono_thread_execute_interruption (thread); if (exc) mono_raise_exception (exc); } } /* + * Performs the interruption of the current thread, if one has been requested, + * and the thread is not running a protected wrapper. + */ +void mono_thread_interruption_checkpoint () +{ + mono_thread_interruption_checkpoint_request (FALSE); +} + +/* + * Performs the interruption of the current thread, if one has been requested. + */ +void mono_thread_force_interruption_checkpoint () +{ + mono_thread_interruption_checkpoint_request (TRUE); +} + +/** + * mono_thread_interruption_request_flag: + * * Returns the address of a flag that will be non-zero if an interruption has * been requested for a thread. The thread to interrupt may not be the current * thread, so an additional call to mono_thread_interruption_requested() or @@ -1989,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; } @@ -2012,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);