X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=96bdefc0bdce864fdc243e8a0fa6e131dcc9be0a;hb=87accf5e934bd049ce7f59b9fab13a151ae88075;hp=6092ccd6a32ff65c33e5f815c934c7a9d7a5a6e4;hpb=ae951c64bac44f47295a6a7959e3930e86e2ccc3;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 6092ccd6a32..96bdefc0bdc 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -27,6 +27,9 @@ #include #include #include +#ifndef PLATFORM_WIN32 +#include +#endif #include #include #include @@ -415,6 +418,31 @@ mono_hazard_pointer_get (void) return &hazard_table [current_thread->small_id]; } +static void +try_free_delayed_free_item (int index) +{ + if (delayed_free_table->len > index) { + DelayedFreeItem item = { NULL, NULL }; + + EnterCriticalSection (&delayed_free_table_mutex); + /* We have to check the length again because another + thread might have freed an item before we acquired + the lock. */ + if (delayed_free_table->len > index) { + item = g_array_index (delayed_free_table, DelayedFreeItem, index); + + if (!is_pointer_hazardous (item.p)) + g_array_remove_index_fast (delayed_free_table, index); + else + item.p = NULL; + } + LeaveCriticalSection (&delayed_free_table_mutex); + + if (item.p != NULL) + item.free_func (item.p); + } +} + void mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func) { @@ -422,29 +450,8 @@ mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func /* First try to free a few entries in the delayed free table. */ - for (i = 2; i >= 0; --i) { - if (delayed_free_table->len > i) { - DelayedFreeItem item; - - item.p = NULL; - EnterCriticalSection (&delayed_free_table_mutex); - /* We have to check the length again because another - thread might have freed an item before we acquired - the lock. */ - if (delayed_free_table->len > i) { - item = g_array_index (delayed_free_table, DelayedFreeItem, i); - - if (!is_pointer_hazardous (item.p)) - g_array_remove_index_fast (delayed_free_table, i); - else - item.p = NULL; - } - LeaveCriticalSection (&delayed_free_table_mutex); - - if (item.p != NULL) - item.free_func (item.p); - } - } + for (i = 2; i >= 0; --i) + try_free_delayed_free_item (i); /* Now see if the pointer we're freeing is hazardous. If it isn't, free it. Otherwise put it in the delay list. */ @@ -460,6 +467,21 @@ mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func free_func (p); } +void +mono_thread_hazardous_try_free_all (void) +{ + int len; + int i; + + if (!delayed_free_table) + return; + + len = delayed_free_table->len; + + for (i = len - 1; i >= 0; --i) + try_free_delayed_free_item (i); +} + static void ensure_synch_cs_set (MonoThread *thread) { CRITICAL_SECTION *synch_cs; @@ -508,6 +530,8 @@ static void thread_cleanup (MonoThread *thread) if (thread->serialized_culture_info) g_free (thread->serialized_culture_info); + g_free (thread->name); + thread->cached_culture_info = NULL; mono_gc_free_fixed (thread->static_data); @@ -541,6 +565,8 @@ static guint32 WINAPI start_wrapper(void *data) SET_CURRENT_OBJECT (thread); + mono_monitor_init_tls (); + /* Every thread references the appdomain which created it */ mono_thread_push_appdomain_ref (start_info->domain); @@ -569,7 +595,7 @@ static guint32 WINAPI start_wrapper(void *data) /* On 2.0 profile (and higher), set explicitly since state might have been Unknown */ - if (mono_get_runtime_info ()->framework_version [0] != '1') { + if (mono_framework_version () != 1) { if (thread->apartment_state == ThreadApartmentState_Unknown) thread->apartment_state = ThreadApartmentState_MTA; } @@ -709,6 +735,8 @@ void mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer ar InitializeCriticalSection (thread->synch_cs); thread->threadpool_thread = threadpool_thread; + if (threadpool_thread) + mono_thread_set_state (thread, ThreadState_Background); if (handle_store (thread)) ResumeThread (thread_handle); @@ -723,8 +751,8 @@ mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg) /* * mono_thread_get_stack_bounds: * - * Return the address and size of the current threads stack. Return NULL as the stack - * address if the stack address cannot be determined. + * Return the address and size of the current threads stack. Return NULL as the + * stack address if the stack address cannot be determined. */ void mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize) @@ -732,6 +760,7 @@ mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize) #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self ()); *stsize = pthread_get_stacksize_np (pthread_self ()); + *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1)); return; /* FIXME: simplify the mess below */ #elif !defined(PLATFORM_WIN32) @@ -740,28 +769,31 @@ mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize) pthread_attr_init (&attr); #ifdef HAVE_PTHREAD_GETATTR_NP - pthread_getattr_np (pthread_self(), &attr); + pthread_getattr_np (pthread_self(), &attr); #else #ifdef HAVE_PTHREAD_ATTR_GET_NP - pthread_attr_get_np (pthread_self(), &attr); + pthread_attr_get_np (pthread_self(), &attr); #elif defined(sun) - *staddr = NULL; - pthread_attr_getstacksize (&attr, &stsize); + *staddr = NULL; + pthread_attr_getstacksize (&attr, &stsize); #else - *staddr = NULL; - *stsize = 0; - return; + *staddr = NULL; + *stsize = 0; + return; #endif #endif #ifndef sun - pthread_attr_getstack (&attr, (void**)staddr, stsize); - if (*staddr) - g_assert ((current > *staddr) && (current < *staddr + *stsize)); + pthread_attr_getstack (&attr, (void**)staddr, stsize); + if (*staddr) + g_assert ((current > *staddr) && (current < *staddr + *stsize)); #endif - pthread_attr_destroy (&attr); + pthread_attr_destroy (&attr); #endif + + /* When running under emacs, sometimes staddr is not aligned to a page size */ + *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1)); } MonoThread * @@ -819,6 +851,8 @@ mono_thread_attach (MonoDomain *domain) SET_CURRENT_OBJECT (thread); mono_domain_set (domain, TRUE); + mono_monitor_init_tls (); + thread_adjust_static_data (thread); if (mono_thread_attach_cb) { @@ -1024,16 +1058,8 @@ void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); } -void ves_icall_System_Threading_Thread_SpinWait_internal (gint32 iterations) +void ves_icall_System_Threading_Thread_SpinWait_nop (void) { - 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 @@ -1247,8 +1273,9 @@ ves_icall_System_Threading_Thread_SetSerializedCurrentUICulture (MonoThread *thi MonoThread * mono_thread_current (void) { - THREAD_DEBUG (g_message ("%s: returning %p", __func__, GET_CURRENT_OBJECT ())); - return GET_CURRENT_OBJECT (); + MonoThread *res = GET_CURRENT_OBJECT () + THREAD_DEBUG (g_message ("%s: returning %p", __func__, res)); + return res; } gboolean ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, @@ -1904,6 +1931,9 @@ void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this) gboolean throw = FALSE; ensure_synch_cs_set (this); + + if (this == mono_thread_current ()) + return; EnterCriticalSection (this->synch_cs); @@ -1925,6 +1955,8 @@ void mono_thread_current_check_pending_interrupt () MonoThread *thread = mono_thread_current (); gboolean throw = FALSE; + mono_debugger_check_interruption (); + ensure_synch_cs_set (thread); EnterCriticalSection (thread->synch_cs); @@ -2001,6 +2033,15 @@ static void signal_thread_state_change (MonoThread *thread) #else pthread_kill (thread->tid, mono_thread_get_abort_signal ()); #endif + + /* + * This will cause waits to be broken. + * It will also prevent the thread from entering a wait, so if the thread returns + * from the wait before it receives the abort signal, it will just spin in the wait + * functions in the io-layer until the signal handler calls QueueUserAPC which will + * make it return. + */ + wapi_interrupt_thread (thread->handle); #endif /* PLATFORM_WIN32 */ } @@ -2034,9 +2075,11 @@ ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state) LeaveCriticalSection (thread->synch_cs); 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 */ - mono_thread_resume (thread); + + /* During shutdown, we can't wait for other threads */ + if (!shutting_down) + /* Make sure the thread is awake */ + mono_thread_resume (thread); signal_thread_state_change (thread); } @@ -2298,6 +2341,8 @@ void mono_thread_init (MonoThreadStartCB start_cb, void mono_thread_cleanup (void) { + mono_thread_hazardous_try_free_all (); + #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 @@ -2326,6 +2371,7 @@ void mono_thread_cleanup (void) #endif g_array_free (delayed_free_table, TRUE); + delayed_free_table = NULL; TlsFree (current_object_key); } @@ -2996,6 +3042,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout) { abort_appdomain_data user_data; guint32 start_time; + int orig_timeout = timeout; THREAD_DEBUG (g_message ("%s: starting abort", __func__)); @@ -3019,7 +3066,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout) timeout -= mono_msec_ticks () - start_time; start_time = mono_msec_ticks (); - if (timeout < 0) + if (orig_timeout != -1 && timeout < 0) return FALSE; } while (user_data.wait.num > 0); @@ -3423,6 +3470,10 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE); InterlockedDecrement (&thread_interruption_requested); thread->interruption_requested = FALSE; +#ifndef PLATFORM_WIN32 + /* Clear the interrupted flag of the thread so it can wait again */ + wapi_clear_interruption (); +#endif } if ((thread->state & ThreadState_AbortRequested) != 0) { @@ -3476,6 +3527,7 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) return NULL; } else if (thread->thread_interrupt_requested) { + thread->thread_interrupt_requested = FALSE; LeaveCriticalSection (thread->synch_cs); return(mono_get_exception_thread_interrupted ()); @@ -3494,7 +3546,8 @@ static MonoException* mono_thread_execute_interruption (MonoThread *thread) * the thread. If the result is an exception that needs to be throw, it is * provided as return value. */ -MonoException* mono_thread_request_interruption (gboolean running_managed) +MonoException* +mono_thread_request_interruption (gboolean running_managed) { MonoThread *thread = mono_thread_current (); @@ -3502,16 +3555,8 @@ MonoException* mono_thread_request_interruption (gboolean running_managed) if (thread == NULL) return NULL; - ensure_synch_cs_set (thread); - - /* FIXME: This is NOT signal safe */ - EnterCriticalSection (thread->synch_cs); - - if (thread->interruption_requested) { - LeaveCriticalSection (thread->synch_cs); - + if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) return NULL; - } if (!running_managed || is_running_protected_wrapper ()) { /* Can't stop while in unmanaged code. Increase the global interruption @@ -3519,9 +3564,6 @@ MonoException* mono_thread_request_interruption (gboolean running_managed) checked and the thread will be interrupted. */ InterlockedIncrement (&thread_interruption_requested); - thread->interruption_requested = TRUE; - - LeaveCriticalSection (thread->synch_cs); if (mono_thread_notify_pending_exc_fn && !running_managed) /* The JIT will notify the thread about the interruption */ @@ -3535,8 +3577,6 @@ MonoException* mono_thread_request_interruption (gboolean running_managed) return NULL; } else { - LeaveCriticalSection (thread->synch_cs); - return mono_thread_execute_interruption (thread); } } @@ -3560,6 +3600,8 @@ static void mono_thread_interruption_checkpoint_request (gboolean bypass_abort_p if (thread == NULL) return; + mono_debugger_check_interruption (); + if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) { MonoException* exc = mono_thread_execute_interruption (thread); if (exc) mono_raise_exception (exc); @@ -3600,8 +3642,44 @@ mono_thread_get_and_clear_pending_exception (void) if (thread->interruption_requested && !is_running_protected_wrapper ()) { return mono_thread_execute_interruption (thread); } - else - return NULL; + + if (thread->pending_exception) { + MonoException *exc = thread->pending_exception; + + thread->pending_exception = NULL; + return exc; + } + + return NULL; +} + +/* + * mono_set_pending_exception: + * + * Set the pending exception of the current thread to EXC. On platforms which + * support it, the exception will be thrown when execution returns to managed code. + * On other platforms, this function is equivalent to mono_raise_exception (). + * Internal calls which report exceptions using this function instead of + * raise_exception () might be called by JITted code using a more efficient calling + * convention. + */ +void +mono_set_pending_exception (MonoException *exc) +{ + MonoThread *thread = mono_thread_current (); + + /* The thread may already be stopping */ + if (thread == NULL) + return; + + if (mono_thread_notify_pending_exc_fn) { + MONO_OBJECT_SETREF (thread, pending_exception, exc); + + mono_thread_notify_pending_exc_fn (); + } else { + /* No way to notify the JIT about the exception, have to throw it now */ + mono_raise_exception (exc); + } } /**