X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=dfbe2f24ddedc3857b43fbd83b7d4aba8faf9209;hb=a8b22e0e864c03b8cfd2f2cb5a8075b6611c5553;hp=2159aad9ac4eadecc5bd4bc445bb85bdfdebf3ef;hpb=b7c17c47e6b3c02192e64175cb5ee0ce7f7dda1b;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 2159aad9ac4..dfbe2f24dde 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -30,17 +30,34 @@ #include #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) + +/* Provide this for systems with glib < 2.6 */ +#ifndef G_GSIZE_FORMAT +# if GLIB_SIZEOF_LONG == 8 +# define G_GSIZE_FORMAT "lu" +# else +# define G_GSIZE_FORMAT "u" +# endif +#endif struct StartInfo { guint32 (*func)(void *); MonoThread *obj; - void *this; + MonoObject *delegate; + void *start_arg; MonoDomain *domain; }; @@ -48,21 +65,38 @@ 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->cached_culture_info array + * (per-type): we use the first NUM entries for CultureInfo and the last for + * UICultureInfo. So the size of the array is really NUM_CACHED_CULTURES * 2. + */ +#define NUM_CACHED_CULTURES 4 +#define CULTURES_START_IDX 0 +#define UICULTURES_START_IDX NUM_CACHED_CULTURES + /* * The "os_handle" field of the WaitHandle class. */ static MonoClassField *wait_handle_os_handle_field = NULL; /* Controls access to the 'threads' hash table */ +#define mono_threads_lock() EnterCriticalSection (&threads_mutex) +#define mono_threads_unlock() LeaveCriticalSection (&threads_mutex) static CRITICAL_SECTION threads_mutex; /* Controls access to context static data */ +#define mono_contexts_lock() EnterCriticalSection (&contexts_mutex) +#define mono_contexts_unlock() LeaveCriticalSection (&contexts_mutex) static CRITICAL_SECTION contexts_mutex; /* Holds current status of static data heap */ @@ -77,6 +111,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; @@ -84,66 +133,78 @@ static MonoThreadStartCB mono_thread_start_cb = NULL; static MonoThreadAttachCB mono_thread_attach_cb = NULL; /* function called at thread cleanup */ -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; +static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL; /* 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); +static void signal_thread_state_change (MonoThread *thread); /* Spin lock for InterlockedXXX 64 bit functions */ +#define mono_interlocked_lock() EnterCriticalSection (&interlocked_mutex) +#define mono_interlocked_unlock() LeaveCriticalSection (&interlocked_mutex) 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. */ static void handle_store(MonoThread *thread) { - EnterCriticalSection(&threads_mutex); + mono_threads_lock (); -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": thread %p ID %d", thread, - thread->tid); -#endif + THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, 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); - LeaveCriticalSection(&threads_mutex); + mono_g_hash_table_insert(threads, (gpointer)(gsize)(thread->tid), + thread); + + mono_threads_unlock (); } -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 ("%s: thread ID %"G_GSIZE_FORMAT, __func__, tid)); - EnterCriticalSection(&threads_mutex); + mono_threads_lock (); + if (threads) - mono_g_hash_table_remove (threads, GUINT_TO_POINTER(tid)); + mono_g_hash_table_remove (threads, (gpointer)tid); - LeaveCriticalSection(&threads_mutex); + mono_threads_unlock (); /* Don't close the handle here, wait for the object finalizer * to do it. Otherwise, the following race condition applies: @@ -162,6 +223,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,23 +238,26 @@ static void thread_cleanup (MonoThread *thread) mono_thread_pop_appdomain_ref (); - if (mono_thread_cleanup) - mono_thread_cleanup (thread); + if (thread->serialized_culture_info) + g_free (thread->serialized_culture_info); + + thread->cached_culture_info = NULL; + + if (mono_thread_cleanup_fn) + mono_thread_cleanup_fn (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; - guint32 tid; + void *start_arg; + gsize 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 ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, GetCurrentThreadId ())); + /* We can be sure start_info->obj->tid and * start_info->obj->handle have been set, because the thread * was created suspended, and these values were set before the @@ -198,7 +266,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 +275,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 +284,9 @@ 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 ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, 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 ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread)); mono_profiler_thread_start (tid); @@ -244,20 +304,29 @@ 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 ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__, + thread->tid); #endif - start_func (this); -#ifdef PLATFORM_WIN32 + /* start_func is set only for unmanaged 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 ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, GetCurrentThreadId ())); + + thread_cleanup (thread); /* Remove the reference to the thread object in the TLS data, * so the thread object can be finalized. This won't be @@ -267,22 +336,16 @@ 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); - - thread_cleanup (thread); -#endif + SET_CURRENT_OBJECT (NULL); 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); } - - if (mono_thread_callbacks) - (* mono_thread_callbacks->thread_created) (tid, stack_start, func); } void mono_threads_set_default_stacksize (guint32 stacksize) @@ -300,8 +363,8 @@ 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,23 +372,24 @@ 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 ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, 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; - thread->synch_lock=mono_object_new (domain, mono_defaults.object_class); + MONO_OBJECT_SETREF (thread, synch_lock, mono_object_new (domain, mono_defaults.object_class)); handle_store(thread); @@ -337,13 +401,19 @@ mono_thread_attach (MonoDomain *domain) { MonoThread *thread; HANDLE thread_handle; - guint32 tid; + gsize tid; if ((thread = mono_thread_current ())) { + if (domain != mono_domain_get ()) + mono_domain_set (domain, TRUE); /* Already attached */ return thread; } + if (!mono_gc_register_thread (&domain)) { + g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing before in the file containing the thread creation code.", GetCurrentThreadId ()); + } + thread = (MonoThread *)mono_object_new (domain, mono_defaults.thread_class); @@ -352,23 +422,25 @@ mono_thread_attach (MonoDomain *domain) tid=GetCurrentThreadId (); + /* + * 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); + thread->handle=thread_handle; thread->tid=tid; + thread->stack_ptr = &tid; + MONO_OBJECT_SETREF (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 ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, 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 ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread)); - TlsSetValue (current_object_key, thread); + SET_CURRENT_OBJECT (thread); mono_domain_set (domain, TRUE); thread_adjust_static_data (thread); @@ -385,62 +457,78 @@ 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 ("%s: mono_thread_detach for %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); thread_cleanup (thread); + + SET_CURRENT_OBJECT (NULL); + + /* Don't need to CloseHandle this thread, even though we took a + * reference in mono_thread_attach (), because the GC will do it + * when the Thread object is finalised. + */ +} + +void +mono_thread_exit () +{ + MonoThread *thread = mono_thread_current (); + + thread_cleanup (thread); + SET_CURRENT_OBJECT (NULL); + + /* 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("%s: Trying to start a new thread: this (%p) start (%p)", __func__, 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; /* FIXME: GC object stored in unmanaged memory */ + 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) { - g_warning (G_GNUC_PRETTY_FUNCTION ": CreateSemaphore error 0x%x", GetLastError ()); + mono_monitor_exit (this->synch_lock); + g_warning ("%s: CreateSemaphore error 0x%x", __func__, 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) { - g_warning(G_GNUC_PRETTY_FUNCTION - ": CreateThread error 0x%x", GetLastError()); + mono_monitor_exit (this->synch_lock); + g_warning("%s: CreateThread error 0x%x", __func__, GetLastError()); return(NULL); } @@ -453,11 +541,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 ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread)); + + mono_monitor_exit (this->synch_lock); return(thread); } } @@ -467,61 +557,40 @@ 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 ("%s: Closing thread %p, handle %p", __func__, 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 ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, 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); - - if (mono_thread_callbacks) - (* mono_thread_callbacks->start_resume) (this->tid); + handle_store (thread); - ResumeThread(thread); - - if (mono_thread_callbacks) - (* mono_thread_callbacks->end_resume) (this->tid); + ResumeThread (thread->handle); - 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 ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, 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 ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid)); } void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) @@ -530,10 +599,10 @@ 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 ("%s: Sleeping for %d ms", __func__, ms)); + mono_thread_current_check_pending_interrupt (); + mono_monitor_enter (thread->synch_lock); thread->state |= ThreadState_WaitSleepJoin; mono_monitor_exit (thread->synch_lock); @@ -545,6 +614,18 @@ void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) mono_monitor_exit (thread->synch_lock); } +void ves_icall_System_Threading_Thread_SpinWait_internal (gint32 iterations) +{ + gint32 i; + + for(i = 0; i < iterations; i++) { + /* We're busy waiting, but at least we can tell the + * scheduler to let someone else have a go... + */ + Sleep (0); + } +} + gint32 ves_icall_System_Threading_Thread_GetDomainID (void) { @@ -556,17 +637,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 +666,152 @@ 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) +static MonoObject* +lookup_cached_culture (MonoThread *this, MonoDomain *domain, int start_idx) { - MonoThread *thread; - - MONO_ARCH_SAVE_REGS; + MonoObject *res; + 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 + if (this->cached_culture_info) { + domain = mono_domain_get (); + for (i = start_idx; i < start_idx + NUM_CACHED_CULTURES; ++i) { + res = mono_array_get (this->cached_culture_info, MonoObject*, i); + if (res && res->vtable->domain == domain) + return res; + } + } + + return NULL; +} + +MonoObject* +ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoThread *this) +{ + return lookup_cached_culture (this, mono_domain_get (), CULTURES_START_IDX); +} - return (thread); +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; +} + +static void +cache_culture (MonoThread *this, MonoObject *culture, int start_idx) +{ + int i; + MonoDomain *domain = mono_domain_get (); + MonoObject *obj; + int free_slot = -1; + int same_domain_slot = -1; + + mono_monitor_enter (this->synch_lock); + if (!this->cached_culture_info) + this->cached_culture_info = mono_array_new (mono_object_domain (this), mono_defaults.object_class, NUM_CACHED_CULTURES * 2); + + for (i = start_idx; i < start_idx + NUM_CACHED_CULTURES; ++i) { + obj = mono_array_get (this->cached_culture_info, MonoObject*, i); + /* Free entry */ + if (!obj) { + free_slot = i; + /* we continue, because there may be a slot used with the same domain */ + continue; + } + /* Replace */ + if (obj->vtable->domain == domain) { + same_domain_slot = i; + break; + } + } + if (same_domain_slot >= 0) + mono_array_setref (this->cached_culture_info, same_domain_slot, culture); + else if (free_slot >= 0) + mono_array_setref (this->cached_culture_info, free_slot, culture); + /* we may want to replace an existing entry here, even when no suitable slot is found */ + mono_monitor_exit (this->synch_lock); +} + +void +ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this, MonoObject *culture) +{ + cache_culture (this, culture, CULTURES_START_IDX); +} + +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) +{ + return lookup_cached_culture (this, mono_domain_get (), UICULTURES_START_IDX); +} + +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) +{ + cache_culture (this, culture, UICULTURES_START_IDX); +} + +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 ("%s: returning %p", __func__, GET_CURRENT_OBJECT ())); + return GET_CURRENT_OBJECT (); } gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, @@ -601,16 +822,22 @@ 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; + } + + mono_thread_current_check_pending_interrupt (); + 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 ("%s: joining thread handle %p, %d ms", __func__, thread, ms)); ret=WaitForSingleObjectEx (thread, ms, TRUE); @@ -619,47 +846,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 ("%s: join successful", __func__)); return(TRUE); } -#ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": join failed"); -#endif + THREAD_DEBUG (g_message ("%s: join failed", __func__)); 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,9 +865,13 @@ 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; + /* Do this WaitSleepJoin check before creating objects */ + mono_thread_current_check_pending_interrupt (); + numhandles = mono_array_length(mono_handles); handles = g_new0(HANDLE, numhandles); @@ -689,16 +889,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 ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, GetCurrentThreadId ())); return(FALSE); } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) { /* Do we want to try again if we get @@ -706,10 +911,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 ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ())); return(FALSE); } @@ -725,9 +927,13 @@ 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; + /* Do this WaitSleepJoin check before creating objects */ + mono_thread_current_check_pending_interrupt (); + numhandles = mono_array_length(mono_handles); handles = g_new0(HANDLE, numhandles); @@ -746,14 +952,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 ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, GetCurrentThreadId (), ret)); /* * These need to be here. See MSDN dos on WaitForMultipleObjects. @@ -773,25 +984,30 @@ 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 ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, GetCurrentThreadId (), handle, ms)); if(ms== -1) { ms=INFINITE; } + mono_thread_current_check_pending_interrupt (); + + 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 ("%s: (%"G_GSIZE_FORMAT") Wait failed", __func__, GetCurrentThreadId ())); return(FALSE); } else if(ret==WAIT_TIMEOUT || ret == WAIT_IO_COMPLETION) { /* Do we want to try again if we get @@ -799,10 +1015,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 ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ())); return(FALSE); } @@ -836,17 +1049,100 @@ void ves_icall_System_Threading_Mutex_ReleaseMutex_internal (HANDLE handle ) { ReleaseMutex(handle); } -HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name) { +HANDLE ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, + gint32 rights, + gint32 *error) +{ + HANDLE ret; + MONO_ARCH_SAVE_REGS; - - return(CreateEvent (NULL, manual, initial, - name==NULL?NULL:mono_string_chars (name))); + + *error = ERROR_SUCCESS; + + ret = OpenMutex (rights, FALSE, mono_string_chars (name)); + if (ret == NULL) { + *error = GetLastError (); + } + + return(ret); } -gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) { - MONO_ARCH_SAVE_REGS; - return (SetEvent(handle)); +HANDLE ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, MonoBoolean *created) +{ + HANDLE sem; + + MONO_ARCH_SAVE_REGS; + + *created = TRUE; + + if (name == NULL) { + sem = CreateSemaphore (NULL, initialCount, maximumCount, NULL); + } else { + sem = CreateSemaphore (NULL, initialCount, maximumCount, + mono_string_chars (name)); + + if (GetLastError () == ERROR_ALREADY_EXISTS) { + *created = FALSE; + } + } + + return(sem); +} + +gint32 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (HANDLE handle, gint32 releaseCount, MonoBoolean *fail) +{ + gint32 prevcount; + + MONO_ARCH_SAVE_REGS; + + *fail = !ReleaseSemaphore (handle, releaseCount, &prevcount); + + return (prevcount); +} + +HANDLE ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error) +{ + HANDLE ret; + + MONO_ARCH_SAVE_REGS; + + *error = ERROR_SUCCESS; + + ret = OpenSemaphore (rights, FALSE, mono_string_chars (name)); + if (ret == NULL) { + *error = GetLastError (); + } + + return(ret); +} + +HANDLE ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, MonoBoolean *created) +{ + HANDLE event; + + MONO_ARCH_SAVE_REGS; + + *created = TRUE; + + if (name == NULL) { + event = CreateEvent (NULL, manual, initial, NULL); + } else { + event = CreateEvent (NULL, manual, initial, + mono_string_chars (name)); + + if (GetLastError () == ERROR_ALREADY_EXISTS) { + *created = FALSE; + } + } + + return(event); +} + +gboolean ves_icall_System_Threading_Events_SetEvent_internal (HANDLE handle) { + MONO_ARCH_SAVE_REGS; + + return (SetEvent(handle)); } gboolean ves_icall_System_Threading_Events_ResetEvent_internal (HANDLE handle) { @@ -862,6 +1158,24 @@ ves_icall_System_Threading_Events_CloseEvent_internal (HANDLE handle) { CloseHandle (handle); } +HANDLE ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name, + gint32 rights, + gint32 *error) +{ + HANDLE ret; + + MONO_ARCH_SAVE_REGS; + + *error = ERROR_SUCCESS; + + ret = OpenEvent (rights, FALSE, mono_string_chars (name)); + if (ret == NULL) { + *error = GetLastError (); + } + + return(ret); +} + gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location) { MONO_ARCH_SAVE_REGS; @@ -871,22 +1185,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); + mono_interlocked_lock (); - lowret = InterlockedIncrement((gint32 *) location); - if (0 == lowret) - highret = InterlockedIncrement((gint32 *) location + 1); - else - highret = *((gint32 *) location + 1); - - LeaveCriticalSection(&interlocked_mutex); + ret = ++ *location; + + mono_interlocked_unlock (); - return (gint64) highret << 32 | (gint64) lowret; + + return ret; } gint32 ves_icall_System_Threading_Interlocked_Decrement_Int (gint32 *location) @@ -898,65 +1208,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); + mono_interlocked_lock (); - LeaveCriticalSection(&interlocked_mutex); + ret = -- *location; + + mono_interlocked_unlock (); - 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; +} + +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. + */ + mono_interlocked_lock (); + res = *location; + *location = value; + mono_interlocked_unlock (); + + 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. + */ + mono_interlocked_lock (); + res = *location; + *location = value; + mono_interlocked_unlock (); + + return res; +#endif } -gint32 ves_icall_System_Threading_Interlocked_CompareExchange_Int(gint32 *location1, gint32 value, gint32 comparand) +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 +1316,247 @@ 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; + + mono_interlocked_lock (); + old = *location; + if (old == comparand) + *location = value; + mono_interlocked_unlock (); + + 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; + + mono_interlocked_lock (); + old = *location; + if (old == comparand) + *location = value; + mono_interlocked_unlock (); + + return old; +#endif +} + +MonoObject* +ves_icall_System_Threading_Interlocked_CompareExchange_T (MonoObject **location, MonoObject *value, MonoObject *comparand) +{ + MONO_ARCH_SAVE_REGS; + + return InterlockedCompareExchangePointer ((gpointer *)location, value, comparand); +} + +MonoObject* +ves_icall_System_Threading_Interlocked_Exchange_T (MonoObject **location, MonoObject *value) +{ + MONO_ARCH_SAVE_REGS; + + return InterlockedExchangePointer ((gpointer *)location, value); +} + +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; + + mono_interlocked_lock (); + orig = *location; + *location = orig + value; + mono_interlocked_unlock (); + + return orig + value; +#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; + + mono_interlocked_lock (); + orig = *location; + *location = orig + value; + mono_interlocked_unlock (); + + return orig + value; +#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; + + mono_interlocked_lock (); + res = *location; + mono_interlocked_unlock (); + + return res; +#endif +} + +void +ves_icall_System_Threading_Thread_MemoryBarrier (void) +{ + /* Should be implemented as a JIT intrinsic */ + mono_raise_exception (mono_get_exception_not_implemented (NULL)); +} + +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; +} + +void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this) +{ + gboolean throw = FALSE; + + mono_monitor_enter (this->synch_lock); + + /* Clear out any previous request */ + this->thread_interrupt_requested = FALSE; + + if (this->state & ThreadState_WaitSleepJoin) { + throw = TRUE; + } else { + this->thread_interrupt_requested = TRUE; + } + + mono_monitor_exit (this->synch_lock); + + if (throw) { + signal_thread_state_change (this); + } +} + +void mono_thread_current_check_pending_interrupt () +{ + MonoThread *thread = mono_thread_current (); + gboolean throw = FALSE; + + mono_monitor_enter (thread->synch_lock); + + if (thread->thread_interrupt_requested) { + throw = TRUE; + thread->thread_interrupt_requested = FALSE; + } + + mono_monitor_exit (thread->synch_lock); + + if (throw) { + mono_raise_exception (mono_get_exception_thread_interrupted ()); + } +} + 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 +1566,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 @@ -1020,26 +1593,29 @@ ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state) mono_monitor_enter (thread->synch_lock); if ((thread->state & ThreadState_AbortRequested) != 0 || - (thread->state & ThreadState_StopRequested) != 0) + (thread->state & ThreadState_StopRequested) != 0 || + (thread->state & ThreadState_Stopped) != 0) { mono_monitor_exit (thread->synch_lock); 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; + MONO_OBJECT_SETREF (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 ("%s: (%"G_GSIZE_FORMAT") Abort requested for %p (%"G_GSIZE_FORMAT")", __func__, 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 +1628,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 +1643,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,16 +1690,23 @@ 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); + if (thread->resume_event == NULL) { + mono_monitor_exit (thread->synch_lock); + return(FALSE); + } /* Awake the thread */ SetEvent (thread->suspend_event); @@ -1118,6 +1717,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 +1764,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 +1840,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); + g_assert(background_change_event != 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 ("%s: Allocated current_object_key %d", __func__, 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 @@ -1232,23 +1860,53 @@ void mono_thread_init (MonoThreadStartCB start_cb, GetCurrentProcess (); } -void -mono_threads_install_cleanup (MonoThreadCleanupFunc func) +void mono_thread_cleanup (void) { - mono_thread_cleanup = func; +#if !defined(PLATFORM_WIN32) && !defined(RUN_IN_SUBTHREAD) + /* The main thread must abandon any held mutexes (particularly + * important for named mutexes as they are shared across + * processes, see bug 74680.) This will happen when the + * thread exits, but if it's not running in a subthread it + * won't exit in time. + */ + /* Using non-w32 API is a nasty kludge, but I couldn't find + * anything in the documentation that would let me do this + * here yet still be safe to call on windows. + */ + _wapi_thread_signal_self (mono_environment_exitcode_get ()); +#endif + +#if 0 + /* This stuff needs more testing, it seems one of these + * critical sections can be locked when mono_thread_cleanup is + * called. + */ + DeleteCriticalSection (&threads_mutex); + DeleteCriticalSection (&interlocked_mutex); + DeleteCriticalSection (&contexts_mutex); + CloseHandle (background_change_event); +#endif + + TlsFree (current_object_key); } -void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks) +void +mono_threads_install_cleanup (MonoThreadCleanupFunc func) { - mono_thread_callbacks = callbacks; + mono_thread_cleanup_fn = func; } -#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 +1919,26 @@ 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("%s: %d threads to wait for in this batch", __func__, 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 ("%s: Wait failed", __func__)); 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 +1950,54 @@ 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 ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, 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("%s: %d threads to wait for in this batch", __func__, 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 ("%s: Wait failed", __func__)); + 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; + mono_threads_lock (); + if (mono_g_hash_table_lookup (threads, (gpointer)tid)!=NULL) { + /* See comment in wait_for_tids about thread cleanup */ + mono_threads_unlock (); + THREAD_DEBUG (g_message ("%s: cleaning up after thread %"G_GSIZE_FORMAT, __func__, tid)); + thread_cleanup (wait->threads [ret]); + } else + mono_threads_unlock (); + } +} + static void build_wait_tids (gpointer key, gpointer value, gpointer user) { struct wait_data *wait=(struct wait_data *)user; @@ -1311,22 +2007,40 @@ 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) { + THREAD_DEBUG (g_message ("%s: ignoring background thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + mono_monitor_exit (thread->synch_lock); return; /* just leave, ignore */ + } + mono_monitor_exit (thread->synch_lock); - if (mono_gc_is_finalizer_thread (thread)) + if (mono_gc_is_finalizer_thread (thread)) { + THREAD_DEBUG (g_message ("%s: ignoring finalizer thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); + return; + } + + if (thread == mono_thread_current ()) { + THREAD_DEBUG (g_message ("%s: ignoring current thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); return; + } - if (thread == mono_thread_current ()) + if (thread == mono_thread_get_main ()) { + THREAD_DEBUG (g_message ("%s: ignoring main thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); return; + } handle = OpenThread (THREAD_ALL_ACCESS, TRUE, thread->tid); - if (handle == NULL) + if (handle == NULL) { + THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); return; + } wait->handles[wait->num]=handle; wait->threads[wait->num]=thread; wait->num++; + + THREAD_DEBUG (g_message ("%s: adding thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid)); } else { /* Just ignore the rest, we can't do anything with * them yet @@ -1338,32 +2052,32 @@ 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; + if (wait->num >= MAXIMUM_WAIT_OBJECTS) + return FALSE; + /* 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 ("%s: Thread id %"G_GSIZE_FORMAT" already aborting", __func__, (gsize)thread->tid)); return(TRUE); } - -#ifdef THREAD_DEBUG - g_print (G_GNUC_PRETTY_FUNCTION ": Aborting id: %d\n", thread->tid); -#endif + + /* printf ("A: %d\n", wait->num); */ + wait->handles[wait->num]=thread->handle; + wait->threads[wait->num]=thread; + wait->num++; + + THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid)); mono_thread_stop (thread); return TRUE; } @@ -1376,55 +2090,64 @@ 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 ("%s: Joining each running thread...", __func__)); - EnterCriticalSection (&threads_mutex); + mono_threads_lock (); if(threads==NULL) { -#ifdef THREAD_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": No threads"); -#endif - LeaveCriticalSection (&threads_mutex); + THREAD_DEBUG (g_message("%s: No threads", __func__)); + mono_threads_unlock (); return; } - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); 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 + mono_threads_lock (); + THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads)); + mono_g_hash_table_foreach (threads, print_tids, NULL)); + ResetEvent (background_change_event); wait->num=0; mono_g_hash_table_foreach (threads, build_wait_tids, wait); - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); 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 ("%s: I have %d threads after waiting.", __func__, wait->num)); } while(wait->num>0); - - mono_thread_pool_cleanup (); - EnterCriticalSection(&threads_mutex); + mono_runtime_set_shutting_down (); + + THREAD_DEBUG (g_message ("%s: threadpool cleanup", __func__)); + mono_thread_pool_cleanup (); /* * Remove everything but the finalizer thread and self. * Also abort all the background threads * */ - wait->num = 0; - mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait); + do { + mono_threads_lock (); - LeaveCriticalSection(&threads_mutex); + wait->num = 0; + mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait); - if(wait->num>0) { - /* Something to wait for */ - wait_for_tids (wait, INFINITE); - } + mono_threads_unlock (); + + THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num)); + if(wait->num>0) { + /* Something to wait for */ + wait_for_tids (wait, INFINITE); + } + } while (wait->num > 0); + + /* + * 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,28 +2155,158 @@ 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", - mono_g_hash_table_size (threads)); - mono_g_hash_table_foreach (threads, print_tids, NULL); -#endif + mono_threads_lock (); + THREAD_DEBUG (g_message ("%s: There are %d threads to abort", __func__, + mono_g_hash_table_size (threads)); + 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); + mono_threads_unlock (); +} + +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. + */ + mono_threads_lock (); + mono_g_hash_table_foreach (threads, collect_threads, wait); + mono_threads_unlock (); + + 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); + if (thread->suspended_event == NULL) { + /* Forget this one and go on to the next */ + mono_monitor_exit (thread->synch_lock); + continue; + } + } + + 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_threads_request_thread_dump: + * + * Ask all threads except the current to print their stacktrace to stdout. + */ +void +mono_threads_request_thread_dump (void) +{ + struct wait_data *wait = g_new0 (struct wait_data, 1); + int i; + + /* + * Make a copy of the hashtable since we can't do anything with + * threads while threads_mutex is held. + */ + mono_threads_lock (); + mono_g_hash_table_foreach (threads, collect_threads, wait); + mono_threads_unlock (); + + for (i = 0; i < wait->num; ++i) { + MonoThread *thread = wait->threads [i]; + + if (!mono_gc_is_finalizer_thread (thread) && (thread != mono_thread_current ()) && !thread->thread_dump_requested) { + thread->thread_dump_requested = TRUE; + + signal_thread_state_change (thread); + } + + CloseHandle (wait->handles [i]); + } } /* @@ -1469,10 +2322,10 @@ mono_thread_push_appdomain_ref (MonoDomain *domain) MonoThread *thread = mono_thread_current (); if (thread) { - /* printf ("PUSH REF: %p -> %s.\n", thread, domain->friendly_name); */ - EnterCriticalSection (&threads_mutex); + /* printf ("PUSH REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, domain->friendly_name); */ + mono_threads_lock (); thread->appdomain_refs = g_slist_prepend (thread->appdomain_refs, domain); - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); } } @@ -1482,22 +2335,22 @@ 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); */ - EnterCriticalSection (&threads_mutex); + /* printf ("POP REF: %"G_GSIZE_FORMAT" -> %s.\n", (gsize)thread->tid, ((MonoDomain*)(thread->appdomain_refs->data))->friendly_name); */ + mono_threads_lock (); /* FIXME: How can the list be empty ? */ if (thread->appdomain_refs) thread->appdomain_refs = g_slist_remove (thread->appdomain_refs, thread->appdomain_refs->data); - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); } } -static gboolean +gboolean mono_thread_has_appdomain_ref (MonoThread *thread, MonoDomain *domain) { gboolean res; - EnterCriticalSection (&threads_mutex); + mono_threads_lock (); res = g_slist_find (thread->appdomain_refs, domain) != NULL; - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); return res; } @@ -1514,11 +2367,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, 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; @@ -1572,6 +2430,39 @@ 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 */ + /* FIXME: why no locking? writes to the cache are protected with synch_lock above */ + + if (thread->cached_culture_info) { + for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) { + MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i); + if (obj && obj->vtable->domain == domain) + mono_array_set (thread->cached_culture_info, MonoObject*, 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) +{ + mono_threads_lock (); + mono_g_hash_table_foreach (threads, clear_cached_culture, domain); + mono_threads_unlock (); +} + /* * mono_thread_get_pending_exception: * @@ -1585,7 +2476,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. @@ -1617,23 +2508,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); } } @@ -1697,13 +2580,13 @@ thread_adjust_static_data (MonoThread *thread) { guint32 offset; - EnterCriticalSection (&threads_mutex); + mono_threads_lock (); if (thread_static_info.offset || thread_static_info.idx > 0) { /* get the current allocated size */ offset = thread_static_info.offset | ((thread_static_info.idx + 1) << 24); mono_alloc_static_data (&(thread->static_data), offset); } - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); } static void @@ -1729,19 +2612,19 @@ mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align guint32 offset; if (static_type == SPECIAL_STATIC_THREAD) { - EnterCriticalSection (&threads_mutex); + mono_threads_lock (); offset = mono_alloc_static_data_slot (&thread_static_info, size, align); /* This can be called during startup */ if (threads != NULL) mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset)); - LeaveCriticalSection (&threads_mutex); + mono_threads_unlock (); } else { g_assert (static_type == SPECIAL_STATIC_CONTEXT); - EnterCriticalSection (&contexts_mutex); + mono_contexts_lock (); offset = mono_alloc_static_data_slot (&context_static_info, size, align); - LeaveCriticalSection (&contexts_mutex); + mono_contexts_unlock (); offset |= 0x80000000; /* Set the high bit to indicate context static data */ } return offset; @@ -1770,81 +2653,88 @@ mono_get_special_static_data (guint32 offset) */ MonoAppContext *context = mono_context_get (); if (!context->static_data || !context->static_data [idx]) { - EnterCriticalSection (&contexts_mutex); + mono_contexts_lock (); mono_alloc_static_data (&(context->static_data), offset); - LeaveCriticalSection (&contexts_mutex); + mono_contexts_unlock (); } return ((char*) context->static_data [idx]) + (offset & 0xffffff); } } -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) - return; +static MonoClassField *local_slots = NULL; - SuspendThread (thread->handle); -} +typedef struct { + /* local tls data to get locals_slot from a thread */ + guint32 offset; + int idx; + /* index in the locals_slot array */ + int slot; +} LocalSlotID; -void mono_gc_stop_world (void) +static void +clear_local_slot (gpointer key, gpointer value, gpointer user_data) { - guint32 self=GetCurrentThreadId (); - -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads); -#endif - - EnterCriticalSection (&threads_mutex); - - if (threads != NULL) - mono_g_hash_table_foreach (threads, gc_stop_world, GUINT_TO_POINTER (self)); - - LeaveCriticalSection (&threads_mutex); + LocalSlotID *sid = user_data; + MonoThread *thread = (MonoThread*)value; + MonoArray *slots_array; + /* + * the static field is stored at: ((char*) thread->static_data [idx]) + (offset & 0xffffff); + * it is for the right domain, so we need to check if it is allocated an initialized + * for the current thread. + */ + /*g_print ("handling thread %p\n", thread);*/ + if (!thread->static_data || !thread->static_data [sid->idx]) + return; + slots_array = *(MonoArray **)(((char*) thread->static_data [sid->idx]) + (sid->offset & 0xffffff)); + if (!slots_array || sid->slot >= mono_array_length (slots_array)) + return; + mono_array_set (slots_array, MonoObject*, sid->slot, NULL); } -static void gc_start_world (gpointer key, gpointer value, gpointer user) +void +mono_thread_free_local_slot_values (int slot, MonoBoolean thread_local) { - 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) - return; - - ResumeThread (thread->handle); + MonoDomain *domain; + LocalSlotID sid; + sid.slot = slot; + if (thread_local) { + void *addr = NULL; + if (!local_slots) { + local_slots = mono_class_get_field_from_name (mono_defaults.thread_class, "local_slots"); + if (!local_slots) { + g_warning ("local_slots field not found in Thread class"); + return; + } + } + domain = mono_domain_get (); + mono_domain_lock (domain); + if (domain->special_static_fields) + addr = g_hash_table_lookup (domain->special_static_fields, local_slots); + mono_domain_unlock (domain); + if (!addr) + return; + /*g_print ("freeing slot %d at %p\n", slot, addr);*/ + sid.offset = GPOINTER_TO_UINT (addr); + sid.offset &= 0x7fffffff; + sid.idx = (sid.offset >> 24) - 1; + mono_threads_lock (); + mono_g_hash_table_foreach (threads, clear_local_slot, &sid); + mono_threads_unlock (); + } else { + /* FIXME: clear the slot for MonoAppContexts, too */ + } } -void mono_gc_start_world (void) +#ifdef __MINGW32__ +static CALLBACK void dummy_apc (ULONG_PTR param) { - guint32 self=GetCurrentThreadId (); - -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads); -#endif - - EnterCriticalSection (&threads_mutex); - - if (threads != NULL) - mono_g_hash_table_foreach (threads, gc_start_world, GUINT_TO_POINTER (self)); - - LeaveCriticalSection (&threads_mutex); } - - +#else static guint32 dummy_apc (gpointer param) { return 0; } +#endif /* * mono_thread_execute_interruption @@ -1855,18 +2745,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) + MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ()); mono_monitor_exit (thread->synch_lock); return thread->abort_exc; } @@ -1874,6 +2763,12 @@ 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->suspend_event == NULL) { + mono_monitor_exit (thread->synch_lock); + return(NULL); + } + if (thread->suspended_event) + SetEvent (thread->suspended_event); mono_monitor_exit (thread->synch_lock); WaitForSingleObject (thread->suspend_event, INFINITE); @@ -1884,7 +2779,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); @@ -1893,8 +2788,11 @@ 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; + } else if (thread->thread_interrupt_requested) { + mono_monitor_exit (thread->synch_lock); + return(mono_get_exception_thread_interrupted ()); } mono_monitor_exit (thread->synch_lock); @@ -1924,22 +2822,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 { @@ -1959,24 +2853,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 @@ -1987,47 +2897,3 @@ gint32* mono_thread_interruption_request_flag () { return &thread_interruption_requested; } - -#ifdef WITH_INCLUDED_LIBGC - -static void gc_push_all_stacks (gpointer key, gpointer value, gpointer user) -{ - MonoThread *thread=(MonoThread *)value; - guint32 *selfp=(guint32 *)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 - GC_push_all_stack (selfp, thread->stack_ptr); - return; - } - -#ifdef PLATFORM_WIN32 - GC_win32_push_thread_stack (thread->handle, thread->stack_ptr); -#else - mono_wapi_push_thread_stack (thread->handle, thread->stack_ptr); -#endif -} - -void mono_gc_push_all_stacks (void) -{ - guint32 self=GetCurrentThreadId (); - -#ifdef LIBGC_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d - %p", self, threads); -#endif - - EnterCriticalSection (&threads_mutex); - - if (threads != NULL) - mono_g_hash_table_foreach (threads, gc_push_all_stacks, &self); - - LeaveCriticalSection (&threads_mutex); -} - -#endif /* WITH_INCLUDED_LIBGC */