X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=ba0a6de92467f85a2f7ffe560982c51f483b7025;hb=9f3ef8e4bac11601a2cf2670cbab337e6276103b;hp=94e92076f3f371605feb14334a6d261fc33610e2;hpb=e7528a2e37e3f79611ce29683a0a9f212a7fe661;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 94e92076f3f..2e05a935793 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -8,6 +8,7 @@ * * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) */ #include @@ -16,6 +17,11 @@ #include #include +#if defined(__OpenBSD__) || defined(__FreeBSD__) +#include +#include +#endif + #include #include #include @@ -37,9 +43,23 @@ #include #include #include +#include +#include +#include #include +#ifdef PLATFORM_ANDROID +#include + +extern int tkill (pid_t tid, int signal); +#endif + +#if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) +void *pthread_get_stackaddr_np(pthread_t); +size_t pthread_get_stacksize_np(pthread_t); +#endif + /*#define THREAD_DEBUG(a) do { a; } while (0)*/ #define THREAD_DEBUG(a) /*#define THREAD_WAIT_DEBUG(a) do { a; } while (0)*/ @@ -47,6 +67,14 @@ /*#define LIBGC_DEBUG(a) do { a; } while (0)*/ #define LIBGC_DEBUG(a) +#define SPIN_TRYLOCK(i) (InterlockedCompareExchange (&(i), 1, 0) == 0) +#define SPIN_LOCK(i) do { \ + if (SPIN_TRYLOCK (i)) \ + break; \ + } while (1) + +#define SPIN_UNLOCK(i) i = 0 + /* Provide this for systems with glib < 2.6 */ #ifndef G_GSIZE_FORMAT # if GLIB_SIZEOF_LONG == 8 @@ -87,11 +115,6 @@ typedef struct { MonoThreadDomainTls *freelist; } StaticDataInfo; -typedef struct { - gpointer p; - MonoHazardousFreeFunc free_func; -} DelayedFreeItem; - /* 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. @@ -131,21 +154,21 @@ static MonoGHashTable *threads_starting_up = NULL; static MonoGHashTable *thread_start_args = NULL; /* The TLS key that holds the MonoObject assigned to each thread */ -static guint32 current_object_key = -1; +static MonoNativeTlsKey current_object_key; -#ifdef HAVE_KW_THREAD +#ifdef MONO_HAVE_FAST_TLS /* we need to use both the Tls* functions and __thread because * the gc needs to see all the threads */ -static __thread MonoInternalThread * tls_current_object MONO_TLS_FAST; +MONO_FAST_TLS_DECLARE(tls_current_object); #define SET_CURRENT_OBJECT(x) do { \ - tls_current_object = x; \ - TlsSetValue (current_object_key, x); \ + MONO_FAST_TLS_SET (tls_current_object, x); \ + mono_native_tls_set_value (current_object_key, x); \ } while (FALSE) -#define GET_CURRENT_OBJECT() tls_current_object +#define GET_CURRENT_OBJECT() ((MonoInternalThread*) MONO_FAST_TLS_GET (tls_current_object)) #else -#define SET_CURRENT_OBJECT(x) TlsSetValue (current_object_key, x) -#define GET_CURRENT_OBJECT() (MonoThread*) TlsGetValue (current_object_key) +#define SET_CURRENT_OBJECT(x) mono_native_tls_set_value (current_object_key, x) +#define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_native_tls_get_value (current_object_key) #endif /* function called at thread start */ @@ -165,13 +188,19 @@ static guint32 default_stacksize = 0; #define default_stacksize_for_thread(thread) ((thread)->stack_size? (thread)->stack_size: default_stacksize) static void thread_adjust_static_data (MonoInternalThread *thread); +static void mono_free_static_data (gpointer* static_data, gboolean threadlocal); 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 (MonoInternalThread* thread); static void mono_thread_start (MonoThread *thread); static void signal_thread_state_change (MonoInternalThread *thread); +static void abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort); +static void suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt); +static void self_suspend_internal (MonoInternalThread *thread); +static gboolean resume_thread_internal (MonoInternalThread *thread); static MonoException* mono_thread_execute_interruption (MonoInternalThread *thread); +static void ref_stack_destroy (gpointer rs); /* Spin lock for InterlockedXXX 64 bit functions */ #define mono_interlocked_lock() EnterCriticalSection (&interlocked_mutex) @@ -184,30 +213,17 @@ static gint32 thread_interruption_requested = 0; /* Event signaled when a thread changes its background mode */ static HANDLE background_change_event; -/* The table for small ID assignment */ -static CRITICAL_SECTION small_id_mutex; -static int small_id_table_size = 0; -static int small_id_next = 0; -static int highest_small_id = -1; -static MonoInternalThread **small_id_table = NULL; - -/* The hazard table */ -#if MONO_SMALL_CONFIG -#define HAZARD_TABLE_MAX_SIZE 256 -#else -#define HAZARD_TABLE_MAX_SIZE 16384 /* There cannot be more threads than this number. */ -#endif -static volatile int hazard_table_size = 0; -static MonoThreadHazardPointers * volatile hazard_table = NULL; +static gboolean shutting_down = FALSE; -/* The table where we keep pointers to blocks to be freed but that - have to wait because they're guarded by a hazard pointer. */ -static CRITICAL_SECTION delayed_free_table_mutex; -static GArray *delayed_free_table = NULL; +static gint32 managed_thread_id_counter = 0; -static gboolean shutting_down = FALSE; +static guint32 +get_next_managed_thread_id (void) +{ + return InterlockedIncrement (&managed_thread_id_counter); +} -guint32 +MonoNativeTlsKey mono_thread_get_tls_key (void) { return current_object_key; @@ -231,7 +247,7 @@ static gboolean handle_store(MonoThread *thread) { mono_threads_lock (); - THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->tid)); + THREAD_DEBUG (g_message ("%s: thread %p ID %"G_GSIZE_FORMAT, __func__, thread, (gsize)thread->internal_thread->tid)); if (threads_starting_up) mono_g_hash_table_remove (threads_starting_up, thread); @@ -242,7 +258,7 @@ static gboolean handle_store(MonoThread *thread) } if(threads==NULL) { - MONO_GC_REGISTER_ROOT (threads); + MONO_GC_REGISTER_ROOT_FIXED (threads); threads=mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC); } @@ -302,200 +318,6 @@ static gboolean handle_remove(MonoInternalThread *thread) return ret; } -/* - * Allocate a small thread id. - * - * FIXME: The biggest part of this function is very similar to - * domain_id_alloc() in domain.c and should be merged. - */ -static int -small_id_alloc (MonoInternalThread *thread) -{ - int id = -1, i; - - EnterCriticalSection (&small_id_mutex); - - if (!small_id_table) { - small_id_table_size = 2; - small_id_table = mono_gc_alloc_fixed (small_id_table_size * sizeof (MonoInternalThread*), NULL); - } - for (i = small_id_next; i < small_id_table_size; ++i) { - if (!small_id_table [i]) { - id = i; - break; - } - } - if (id == -1) { - for (i = 0; i < small_id_next; ++i) { - if (!small_id_table [i]) { - id = i; - break; - } - } - } - if (id == -1) { - MonoInternalThread **new_table; - int new_size = small_id_table_size * 2; - if (new_size >= (1 << 16)) - g_assert_not_reached (); - id = small_id_table_size; - new_table = mono_gc_alloc_fixed (new_size * sizeof (MonoInternalThread*), NULL); - memcpy (new_table, small_id_table, small_id_table_size * sizeof (void*)); - mono_gc_free_fixed (small_id_table); - small_id_table = new_table; - small_id_table_size = new_size; - } - thread->small_id = id; - g_assert (small_id_table [id] == NULL); - small_id_table [id] = thread; - small_id_next++; - if (small_id_next > small_id_table_size) - small_id_next = 0; - - g_assert (id < HAZARD_TABLE_MAX_SIZE); - if (id >= hazard_table_size) { -#if MONO_SMALL_CONFIG - hazard_table = g_malloc0 (sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE); - hazard_table_size = HAZARD_TABLE_MAX_SIZE; -#else - gpointer page_addr; - int pagesize = mono_pagesize (); - int num_pages = (hazard_table_size * sizeof (MonoThreadHazardPointers) + pagesize - 1) / pagesize; - - if (hazard_table == NULL) { - hazard_table = mono_valloc (NULL, - sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE, - MONO_MMAP_NONE); - } - - g_assert (hazard_table != NULL); - page_addr = (guint8*)hazard_table + num_pages * pagesize; - - mono_mprotect (page_addr, pagesize, MONO_MMAP_READ | MONO_MMAP_WRITE); - - ++num_pages; - hazard_table_size = num_pages * pagesize / sizeof (MonoThreadHazardPointers); - -#endif - g_assert (id < hazard_table_size); - hazard_table [id].hazard_pointers [0] = NULL; - hazard_table [id].hazard_pointers [1] = NULL; - } - - if (id > highest_small_id) { - highest_small_id = id; - mono_memory_write_barrier (); - } - - LeaveCriticalSection (&small_id_mutex); - - return id; -} - -static void -small_id_free (int id) -{ - g_assert (id >= 0 && id < small_id_table_size); - g_assert (small_id_table [id] != NULL); - - small_id_table [id] = NULL; -} - -static gboolean -is_pointer_hazardous (gpointer p) -{ - int i; - int highest = highest_small_id; - - g_assert (highest < hazard_table_size); - - for (i = 0; i <= highest; ++i) { - if (hazard_table [i].hazard_pointers [0] == p - || hazard_table [i].hazard_pointers [1] == p) - return TRUE; - } - - return FALSE; -} - -MonoThreadHazardPointers* -mono_hazard_pointer_get (void) -{ - MonoInternalThread *current_thread = mono_thread_internal_current (); - - if (!(current_thread && current_thread->small_id >= 0)) { - static MonoThreadHazardPointers emerg_hazard_table; - g_warning ("Thread %p may have been prematurely finalized", current_thread); - return &emerg_hazard_table; - } - - 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) -{ - int i; - - /* First try to free a few entries in the delayed free - table. */ - 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. */ - if (is_pointer_hazardous (p)) { - DelayedFreeItem item = { p, free_func }; - - ++mono_stats.hazardous_pointer_count; - - EnterCriticalSection (&delayed_free_table_mutex); - g_array_append_val (delayed_free_table, item); - LeaveCriticalSection (&delayed_free_table_mutex); - } else - 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 (MonoInternalThread *thread) { CRITICAL_SECTION *synch_cs; @@ -542,10 +364,7 @@ static void thread_cleanup (MonoInternalThread *thread) mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL); } - /* if the thread is not in the hash it has been removed already */ - if (!handle_remove (thread)) - return; - mono_release_type_locks (thread); + ensure_synch_cs_set (thread); EnterCriticalSection (thread->synch_cs); @@ -553,7 +372,29 @@ static void thread_cleanup (MonoInternalThread *thread) thread->state &= ~ThreadState_Background; LeaveCriticalSection (thread->synch_cs); + + /* + An interruption request has leaked to cleanup. Adjust the global counter. + + This can happen is the abort source thread finds the abortee (this) thread + in unmanaged code. If this thread never trips back to managed code or check + the local flag it will be left set and positively unbalance the global counter. + Leaving the counter unbalanced will cause a performance degradation since all threads + will now keep checking their local flags all the time. + */ + if (InterlockedExchange (&thread->interruption_requested, 0)) + InterlockedDecrement (&thread_interruption_requested); + + /* if the thread is not in the hash it has been removed already */ + if (!handle_remove (thread)) { + /* This needs to be called even if handle_remove () fails */ + if (mono_thread_cleanup_fn) + mono_thread_cleanup_fn (thread); + return; + } + mono_release_type_locks (thread); + mono_profiler_thread_end (thread->tid); if (thread == mono_thread_internal_current ()) @@ -561,22 +402,15 @@ static void thread_cleanup (MonoInternalThread *thread) thread->cached_culture_info = NULL; - mono_gc_free_fixed (thread->static_data); + mono_free_static_data (thread->static_data, TRUE); thread->static_data = NULL; + ref_stack_destroy (thread->appdomain_refs); + thread->appdomain_refs = NULL; - /* - * FIXME: The check for shutting_down here is a kludge and - * should be removed. The reason we need it here is because - * mono_thread_manage() does not wait for finalizer threads, - * so we might still be at this point in a finalizer thread - * after the main thread has cleared the root domain, so - * thread could have been zeroed out. - */ - if (mono_thread_cleanup_fn && !shutting_down) - mono_thread_cleanup_fn (thread->root_domain_thread); + if (mono_thread_cleanup_fn) + mono_thread_cleanup_fn (thread); - small_id_free (thread->small_id); - thread->small_id = -2; + MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref); } static gpointer @@ -621,10 +455,24 @@ set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, M *current_thread_ptr = current; } +static MonoInternalThread* +create_internal_thread_object (void) +{ + MonoVTable *vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class); + return (MonoInternalThread*)mono_gc_alloc_mature (vt); +} + +static MonoThread* +create_thread_object (MonoDomain *domain) +{ + MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class); + return (MonoThread*)mono_gc_alloc_mature (vt); +} + static MonoThread* new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal) { - MonoThread *thread = (MonoThread*) mono_object_new (domain, mono_defaults.thread_class); + MonoThread *thread = create_thread_object (domain); MONO_OBJECT_SETREF (thread, internal_thread, internal); return thread; } @@ -641,15 +489,20 @@ init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate) MONO_OBJECT_SETREF (thread, root_domain_thread, candidate); } -static guint32 WINAPI start_wrapper(void *data) +static guint32 WINAPI start_wrapper_internal(void *data) { + MonoThreadInfo *info; struct StartInfo *start_info=(struct StartInfo *)data; guint32 (*start_func)(void *); void *start_arg; gsize tid; - MonoThread *thread=start_info->obj; - MonoInternalThread *internal = thread->internal_thread; + /* + * We don't create a local to hold start_info->obj, so hopefully it won't get pinned during a + * GC stack walk. + */ + MonoInternalThread *internal = start_info->obj->internal_thread; MonoObject *start_delegate = start_info->delegate; + MonoDomain *domain = start_info->obj->obj.vtable->domain; THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, GetCurrentThreadId ())); @@ -659,6 +512,11 @@ static guint32 WINAPI start_wrapper(void *data) * thread resumed */ + info = mono_thread_info_current (); + g_assert (info); + internal->thread_info = info; + + tid=internal->tid; SET_CURRENT_OBJECT (internal); @@ -666,9 +524,9 @@ static guint32 WINAPI start_wrapper(void *data) mono_monitor_init_tls (); /* Every thread references the appdomain which created it */ - mono_thread_push_appdomain_ref (thread->obj.vtable->domain); + mono_thread_push_appdomain_ref (domain); - if (!mono_domain_set (thread->obj.vtable->domain, FALSE)) { + if (!mono_domain_set (domain, FALSE)) { /* No point in raising an appdomain_unloaded exception here */ /* FIXME: Cleanup here */ mono_thread_pop_appdomain_ref (); @@ -681,7 +539,7 @@ static guint32 WINAPI start_wrapper(void *data) /* We have to do this here because mono_thread_new_init() requires that root_domain_thread is set up. */ thread_adjust_static_data (internal); - init_root_domain_thread (internal, thread); + init_root_domain_thread (internal, start_info->obj); /* This MUST be called before any managed code can be * executed, as it calls the callback function that (for the @@ -692,7 +550,7 @@ static guint32 WINAPI start_wrapper(void *data) LIBGC_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT",%d) Setting thread stack to %p", __func__, GetCurrentThreadId (), getpid (), thread->stack_ptr)); - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), thread)); + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, GetCurrentThreadId (), internal)); /* On 2.0 profile (and higher), set explicitly since state might have been Unknown */ @@ -709,17 +567,15 @@ static guint32 WINAPI start_wrapper(void *data) } mono_threads_lock (); - mono_g_hash_table_remove (thread_start_args, thread); + mono_g_hash_table_remove (thread_start_args, start_info->obj); mono_threads_unlock (); - g_free (start_info); -#ifdef DEBUG - g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__, - thread->tid); -#endif + mono_thread_set_execution_context (start_info->obj->ec_to_set); + start_info->obj->ec_to_set = NULL; - mono_thread_set_execution_context (thread->ec_to_set); - thread->ec_to_set = NULL; + g_free (start_info); + THREAD_DEBUG (g_message ("%s: start_wrapper for %"G_GSIZE_FORMAT, __func__, + internal->tid)); /* * Call this after calling start_notify, since the profiler callback might want @@ -769,6 +625,16 @@ static guint32 WINAPI start_wrapper(void *data) return(0); } +static guint32 WINAPI start_wrapper(void *data) +{ + volatile int dummy; + + /* Avoid scanning the frames above this frame during a GC */ + mono_gc_set_stack_end ((void*)&dummy); + + return start_wrapper_internal (data); +} + void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func) { if (mono_thread_start_cb) { @@ -801,7 +667,7 @@ gpointer mono_create_thread (WapiSecurityAttributes *security, #ifdef HOST_WIN32 DWORD real_tid; - res = CreateThread (security, stacksize, start, param, create, &real_tid); + res = mono_threads_CreateThread (security, stacksize, start, param, create, &real_tid); if (tid) *tid = real_tid; #else @@ -823,13 +689,13 @@ static void register_thread_start_argument (MonoThread *thread, struct StartInfo *start_info) { if (thread_start_args == NULL) { - MONO_GC_REGISTER_ROOT (thread_start_args); + MONO_GC_REGISTER_ROOT_FIXED (thread_start_args); thread_start_args = mono_g_hash_table_new (NULL, NULL); } mono_g_hash_table_insert (thread_start_args, thread, start_info->start_arg); } -MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread) +MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size) { MonoThread *thread; MonoInternalThread *internal; @@ -837,10 +703,8 @@ MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer fu struct StartInfo *start_info; gsize tid; - thread=(MonoThread *)mono_object_new (domain, - mono_defaults.thread_class); - internal = (MonoInternalThread*)mono_object_new (mono_get_root_domain (), - mono_defaults.internal_thread_class); + thread = create_thread_object (domain); + internal = create_internal_thread_object (); MONO_OBJECT_SETREF (thread, internal_thread, internal); start_info=g_new0 (struct StartInfo, 1); @@ -855,18 +719,21 @@ MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer fu return NULL; } if (threads_starting_up == NULL) { - MONO_GC_REGISTER_ROOT (threads_starting_up); - threads_starting_up = mono_g_hash_table_new (NULL, NULL); + MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up); + threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC); } register_thread_start_argument (thread, start_info); mono_g_hash_table_insert (threads_starting_up, thread, thread); mono_threads_unlock (); + if (stack_size == 0) + stack_size = default_stacksize_for_thread (internal); + /* Create suspended, so we can do some housekeeping before the thread * starts */ - thread_handle = mono_create_thread (NULL, default_stacksize_for_thread (internal), (LPTHREAD_START_ROUTINE)start_wrapper, start_info, + thread_handle = mono_create_thread (NULL, stack_size, (LPTHREAD_START_ROUTINE)start_wrapper, start_info, CREATE_SUSPENDED, &tid); THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle)); if (thread_handle == NULL) { @@ -882,7 +749,9 @@ MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer fu internal->handle=thread_handle; internal->tid=tid; internal->apartment_state=ThreadApartmentState_Unknown; - small_id_alloc (internal); + internal->thread_pinning_ref = internal; + internal->managed_id = get_next_managed_thread_id (); + MONO_GC_REGISTER_ROOT_PINNING (internal->thread_pinning_ref); internal->synch_cs = g_new0 (CRITICAL_SECTION, 1); InitializeCriticalSection (internal->synch_cs); @@ -894,13 +763,16 @@ MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer fu if (handle_store (thread)) ResumeThread (thread_handle); + /* Check that the managed and unmanaged layout of MonoInternalThread matches */ + g_assert (((char*)&internal->unused2 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset); + return internal; } void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg) { - mono_thread_create_internal (domain, func, arg, FALSE); + mono_thread_create_internal (domain, func, arg, FALSE, 0); } /* @@ -915,6 +787,9 @@ 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 points to the start of the stack, not the end */ + *staddr -= *stsize; *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1)); return; /* FIXME: simplify the mess below */ @@ -931,6 +806,15 @@ mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize) # elif defined(sun) *staddr = NULL; pthread_attr_getstacksize (&attr, &stsize); +# elif defined(__OpenBSD__) + stack_t ss; + int rslt; + + rslt = pthread_stackseg_np(pthread_self(), &ss); + g_assert (rslt == 0); + + *staddr = (guint8*)((size_t)ss.ss_sp - ss.ss_size); + *stsize = ss.ss_size; # else *staddr = NULL; *stsize = 0; @@ -938,13 +822,18 @@ mono_thread_get_stack_bounds (guint8 **staddr, size_t *stsize) # endif # endif -# ifndef sun +# if !defined(sun) +# if !defined(__OpenBSD__) pthread_attr_getstack (&attr, (void**)staddr, stsize); +# endif if (*staddr) g_assert ((current > *staddr) && (current < *staddr + *stsize)); # endif - pthread_attr_destroy (&attr); + pthread_attr_destroy (&attr); +#else + *staddr = NULL; + *stsize = (size_t)-1; #endif /* When running under emacs, sometimes staddr is not aligned to a page size */ @@ -970,7 +859,7 @@ mono_thread_attach (MonoDomain *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 = (MonoInternalThread *)mono_object_new (domain, mono_defaults.internal_thread_class); + thread = create_internal_thread_object (); thread_handle = GetCurrentThread (); g_assert (thread_handle); @@ -986,8 +875,14 @@ mono_thread_attach (MonoDomain *domain) thread->handle=thread_handle; thread->tid=tid; +#ifdef PLATFORM_ANDROID + thread->android_tid = (gpointer) gettid (); +#endif thread->apartment_state=ThreadApartmentState_Unknown; - small_id_alloc (thread); + thread->thread_pinning_ref = thread; + thread->managed_id = get_next_managed_thread_id (); + MONO_GC_REGISTER_ROOT_PINNING (thread->thread_pinning_ref); + thread->stack_ptr = &tid; thread->synch_cs = g_new0 (CRITICAL_SECTION, 1); @@ -1040,8 +935,10 @@ mono_thread_detach (MonoThread *thread) { g_return_if_fail (thread != NULL); - THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid)); + THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->internal_thread->tid)); + mono_profiler_thread_end (thread->internal_thread->tid); + thread_cleanup (thread->internal_thread); SET_CURRENT_OBJECT (NULL); @@ -1073,9 +970,11 @@ mono_thread_exit () void ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this) { - MonoInternalThread *internal = (MonoInternalThread*)mono_object_new (mono_get_root_domain (), mono_defaults.internal_thread_class); + MonoInternalThread *internal = create_internal_thread_object (); + internal->state = ThreadState_Unstarted; internal->apartment_state = ThreadApartmentState_Unknown; + internal->managed_id = get_next_managed_thread_id (); InterlockedCompareExchangePointer ((gpointer)&this->internal_thread, internal, NULL); } @@ -1105,8 +1004,6 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this, return NULL; } - internal->small_id = -1; - if ((internal->state & ThreadState_Aborted) != 0) { LeaveCriticalSection (internal->synch_cs); return this; @@ -1132,8 +1029,8 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this, mono_threads_lock (); register_thread_start_argument (this, start_info); if (threads_starting_up == NULL) { - MONO_GC_REGISTER_ROOT (threads_starting_up); - threads_starting_up = mono_g_hash_table_new (NULL, NULL); + MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up); + threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC); } mono_g_hash_table_insert (threads_starting_up, this, this); mono_threads_unlock (); @@ -1151,7 +1048,9 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this, internal->handle=thread; internal->tid=tid; - small_id_alloc (internal); + internal->thread_pinning_ref = internal; + MONO_GC_REGISTER_ROOT_PINNING (internal->thread_pinning_ref); + /* Don't call handle_store() here, delay it to Start. * We can't join a thread (trying to will just block @@ -1180,19 +1079,24 @@ void ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInterna CloseHandle (thread); if (this->synch_cs) { - DeleteCriticalSection (this->synch_cs); - g_free (this->synch_cs); + CRITICAL_SECTION *synch_cs = this->synch_cs; this->synch_cs = NULL; + DeleteCriticalSection (synch_cs); + g_free (synch_cs); } - g_free (this->name); + if (this->name) { + void *name = this->name; + this->name = NULL; + g_free (name); + } } static void mono_thread_start (MonoThread *thread) { MonoInternalThread *internal = thread->internal_thread; - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid)); + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), internal, (gsize)internal->tid)); /* Only store the handle when the thread is about to be * launched, to avoid the main thread deadlocking while trying @@ -1210,14 +1114,14 @@ static void mono_thread_start (MonoThread *thread) * started */ - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid)); + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, GetCurrentThreadId (), internal, (gsize)internal->tid)); WaitForSingleObjectEx (internal->start_notify, INFINITE, FALSE); CloseHandle (internal->start_notify); internal->start_notify = NULL; } - THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), thread, (gsize)thread->tid)); + THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, GetCurrentThreadId (), internal, (gsize)internal->tid)); } void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) @@ -1229,15 +1133,25 @@ void ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) mono_thread_current_check_pending_interrupt (); - mono_thread_set_state (thread, ThreadState_WaitSleepJoin); + while (TRUE) { + mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - res = SleepEx(ms,TRUE); + res = SleepEx(ms,TRUE); - mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); - - if (res == WAIT_IO_COMPLETION) { /* we might have been interrupted */ - MonoException* exc = mono_thread_execute_interruption (thread); - if (exc) mono_raise_exception (exc); + mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); + + if (res == WAIT_IO_COMPLETION) { /* we might have been interrupted */ + MonoException* exc = mono_thread_execute_interruption (thread); + if (exc) { + mono_raise_exception (exc); + } else { + // FIXME: !INFINITE + if (ms != INFINITE) + break; + } + } else { + break; + } } } @@ -1253,6 +1167,16 @@ ves_icall_System_Threading_Thread_GetDomainID (void) return mono_domain_get()->domain_id; } +gboolean +ves_icall_System_Threading_Thread_Yield (void) +{ +#ifdef HOST_WIN32 + return SwitchToThread (); +#else + return sched_yield () == 0; +#endif +} + /* * mono_thread_get_name: * @@ -1303,13 +1227,13 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj } void -ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name) +mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean managed) { ensure_synch_cs_set (this_obj); EnterCriticalSection (this_obj->synch_cs); - - if (this_obj->name) { + + if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) { LeaveCriticalSection (this_obj->synch_cs); mono_raise_exception (mono_get_exception_invalid_operation ("Thread.Name can only be set once.")); @@ -1322,26 +1246,22 @@ ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj } else this_obj->name = NULL; + + if (managed) + this_obj->flags |= MONO_THREAD_FLAG_NAME_SET; LeaveCriticalSection (this_obj->synch_cs); + if (this_obj->name) { + char *tname = mono_string_to_utf8 (name); + mono_profiler_thread_name (this_obj->tid, tname); + mono_free (tname); + } } -static MonoObject* -lookup_cached_culture (MonoInternalThread *this, MonoDomain *domain, int start_idx) +void +ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name) { - MonoObject *res; - int i; - - 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; + mono_thread_set_name_internal (this_obj, name, TRUE); } /* If the array is already in the requested domain, we just return it, @@ -1358,7 +1278,7 @@ byte_array_to_domain (MonoArray *arr, MonoDomain *domain) return arr; copy = mono_array_new (domain, mono_defaults.byte_class, arr->max_length); - memcpy (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length); + mono_gc_memmove (mono_array_addr (copy, guint8, 0), mono_array_addr (arr, guint8, 0), arr->max_length); return copy; } @@ -1374,73 +1294,6 @@ ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr) return byte_array_to_domain (arr, mono_domain_get ()); } -MonoObject* -ves_icall_System_Threading_Thread_GetCachedCurrentCulture (MonoInternalThread *this) -{ - return lookup_cached_culture (this, mono_domain_get (), CULTURES_START_IDX); -} - -static void -cache_culture (MonoInternalThread *this, MonoObject *culture, int start_idx) -{ - int i; - MonoDomain *domain = mono_domain_get (); - MonoObject *obj; - int free_slot = -1; - int same_domain_slot = -1; - - ensure_synch_cs_set (this); - - EnterCriticalSection (this->synch_cs); - - if (!this->cached_culture_info) - MONO_OBJECT_SETREF (this, cached_culture_info, mono_array_new_cached (mono_get_root_domain (), 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 */ - - LeaveCriticalSection (this->synch_cs); -} - -void -ves_icall_System_Threading_Thread_SetCachedCurrentCulture (MonoThread *this, MonoObject *culture) -{ - MonoDomain *domain = mono_object_get_domain (&this->obj); - g_assert (domain == mono_domain_get ()); - cache_culture (this->internal_thread, culture, CULTURES_START_IDX); -} - -MonoObject* -ves_icall_System_Threading_Thread_GetCachedCurrentUICulture (MonoInternalThread *this) -{ - return lookup_cached_culture (this, mono_domain_get (), UICULTURES_START_IDX); -} - -void -ves_icall_System_Threading_Thread_SetCachedCurrentUICulture (MonoThread *this, MonoObject *culture) -{ - MonoDomain *domain = mono_object_get_domain (&this->obj); - g_assert (domain == mono_domain_get ()); - cache_culture (this->internal_thread, culture, UICULTURES_START_IDX); -} - MonoThread * mono_thread_current (void) { @@ -1509,6 +1362,44 @@ gboolean ves_icall_System_Threading_Thread_Join_internal(MonoInternalThread *thi return(FALSE); } +static gint32 +mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, gboolean alertable) +{ + MonoException *exc; + guint32 ret; + gint64 start; + gint32 diff_ms; + gint32 wait = ms; + + start = (ms == -1) ? 0 : mono_100ns_ticks (); + do { + if (multiple) + ret = WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, alertable); + else + ret = WaitForSingleObjectEx (handles [0], ms, alertable); + + if (ret != WAIT_IO_COMPLETION) + break; + + exc = mono_thread_execute_interruption (thread); + if (exc) + mono_raise_exception (exc); + + if (ms == -1) + continue; + + /* Re-calculate ms according to the time passed */ + diff_ms = (mono_100ns_ticks () - start) / 10000; + if (diff_ms >= ms) { + ret = WAIT_TIMEOUT; + break; + } + wait = ms - diff_ms; + } while (TRUE); + + return ret; +} + /* FIXME: exitContext isnt documented */ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext) { @@ -1536,7 +1427,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - ret=WaitForMultipleObjectsEx(numhandles, handles, TRUE, ms, TRUE); + ret = mono_wait_uninterrupted (thread, TRUE, numhandles, handles, TRUE, ms, TRUE); mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); @@ -1545,12 +1436,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ if(ret==WAIT_FAILED) { 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 - * WAIT_IO_COMPLETION? The documentation for - * WaitHandle doesn't give any clues. (We'd have to - * fiddle with the timeout if we retry.) - */ + } else if(ret==WAIT_TIMEOUT) { THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ())); return(FALSE); } @@ -1561,7 +1447,7 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ /* FIXME: exitContext isnt documented */ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms, gboolean exitContext) { - HANDLE *handles; + HANDLE handles [MAXIMUM_WAIT_OBJECTS]; guint32 numhandles; guint32 ret; guint32 i; @@ -1572,7 +1458,8 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha mono_thread_current_check_pending_interrupt (); numhandles = mono_array_length(mono_handles); - handles = g_new0(HANDLE, numhandles); + if (numhandles > MAXIMUM_WAIT_OBJECTS) + return WAIT_FAILED; for(i = 0; i < numhandles; i++) { waitHandle = mono_array_get(mono_handles, MonoObject*, i); @@ -1584,12 +1471,10 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha } mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - - ret=WaitForMultipleObjectsEx(numhandles, handles, FALSE, ms, TRUE); + + ret = mono_wait_uninterrupted (thread, TRUE, numhandles, handles, FALSE, ms, TRUE); mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); - - g_free(handles); THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, GetCurrentThreadId (), ret)); @@ -1623,19 +1508,14 @@ gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - ret=WaitForSingleObjectEx (handle, ms, TRUE); + ret = mono_wait_uninterrupted (thread, FALSE, 1, &handle, FALSE, ms, TRUE); mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); if(ret==WAIT_FAILED) { 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 - * WAIT_IO_COMPLETION? The documentation for - * WaitHandle doesn't give any clues. (We'd have to - * fiddle with the timeout if we retry.) - */ + } else if(ret==WAIT_TIMEOUT) { THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Wait timed out", __func__, GetCurrentThreadId ())); return(FALSE); } @@ -2144,25 +2024,22 @@ ves_icall_System_Threading_Thread_GetState (MonoInternalThread* this) void ves_icall_System_Threading_Thread_Interrupt_internal (MonoInternalThread *this) { - gboolean throw = FALSE; - + MonoInternalThread *current; + gboolean throw; + ensure_synch_cs_set (this); - if (this == mono_thread_internal_current ()) - return; - - EnterCriticalSection (this->synch_cs); - - this->thread_interrupt_requested = TRUE; - - if (this->state & ThreadState_WaitSleepJoin) { - throw = TRUE; - } - + current = mono_thread_internal_current (); + + EnterCriticalSection (this->synch_cs); + + this->thread_interrupt_requested = TRUE; + throw = current != this && (this->state & ThreadState_WaitSleepJoin); + LeaveCriticalSection (this->synch_cs); if (throw) { - signal_thread_state_change (this); + abort_thread_internal (this, TRUE, FALSE); } } @@ -2248,11 +2125,7 @@ static void signal_thread_state_change (MonoInternalThread *thread) QueueUserAPC ((PAPCFUNC)interruption_request_apc, thread->handle, NULL); #else /* fixme: store the state somewhere */ -#ifdef PTHREAD_POINTER_ID - pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ()); -#else - pthread_kill (thread->tid, mono_thread_get_abort_signal ()); -#endif + mono_thread_kill (thread, mono_thread_get_abort_signal ()); /* * This will cause waits to be broken. @@ -2314,35 +2187,33 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject /* Make sure the thread is awake */ mono_thread_resume (thread); - signal_thread_state_change (thread); + abort_thread_internal (thread, TRUE, TRUE); } void ves_icall_System_Threading_Thread_ResetAbort (void) { MonoInternalThread *thread = mono_thread_internal_current (); + gboolean was_aborting; ensure_synch_cs_set (thread); EnterCriticalSection (thread->synch_cs); - + was_aborting = thread->state & ThreadState_AbortRequested; thread->state &= ~ThreadState_AbortRequested; - - if (!thread->abort_exc) { + LeaveCriticalSection (thread->synch_cs); + + if (!was_aborting) { const char *msg = "Unable to reset abort because no abort was requested"; - LeaveCriticalSection (thread->synch_cs); mono_raise_exception (mono_get_exception_thread_state (msg)); - } else { - thread->abort_exc = NULL; - if (thread->abort_state_handle) { - mono_gchandle_free (thread->abort_state_handle); - /* This is actually not necessary - the handle - only counts if the exception is set */ - thread->abort_state_handle = 0; - } } - - LeaveCriticalSection (thread->synch_cs); + thread->abort_exc = NULL; + if (thread->abort_state_handle) { + mono_gchandle_free (thread->abort_state_handle); + /* This is actually not necessary - the handle + only counts if the exception is set */ + thread->abort_state_handle = 0; + } } void @@ -2423,7 +2294,7 @@ mono_thread_suspend (MonoInternalThread *thread) LeaveCriticalSection (thread->synch_cs); - signal_thread_state_change (thread); + suspend_thread_internal (thread, FALSE); return TRUE; } @@ -2455,24 +2326,8 @@ mono_thread_resume (MonoInternalThread *thread) LeaveCriticalSection (thread->synch_cs); return FALSE; } - - thread->resume_event = CreateEvent (NULL, TRUE, FALSE, NULL); - if (thread->resume_event == NULL) { - LeaveCriticalSection (thread->synch_cs); - return(FALSE); - } - - /* Awake the thread */ - SetEvent (thread->suspend_event); - - LeaveCriticalSection (thread->synch_cs); - /* Wait for the thread to awake */ - WaitForSingleObject (thread->resume_event, INFINITE); - CloseHandle (thread->resume_event); - thread->resume_event = NULL; - - return TRUE; + return resume_thread_internal (thread); } void @@ -2482,16 +2337,25 @@ ves_icall_System_Threading_Thread_Resume (MonoThread *thread) mono_raise_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead.")); } +static gboolean +mono_threads_is_critical_method (MonoMethod *method) +{ + switch (method->wrapper_type) { + case MONO_WRAPPER_RUNTIME_INVOKE: + case MONO_WRAPPER_XDOMAIN_INVOKE: + case MONO_WRAPPER_XDOMAIN_DISPATCH: + return TRUE; + } + return FALSE; +} + 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) - { + if (mono_threads_is_critical_method (m)) { *((gboolean*)data) = TRUE; return TRUE; } @@ -2527,7 +2391,7 @@ void mono_thread_internal_stop (MonoInternalThread *thread) LeaveCriticalSection (thread->synch_cs); - signal_thread_state_change (thread); + abort_thread_internal (thread, TRUE, TRUE); } void mono_thread_stop (MonoThread *thread) @@ -2565,6 +2429,18 @@ ves_icall_System_Threading_Thread_VolatileReadIntPtr (void *ptr) return (void *) *((volatile void **) ptr); } +double +ves_icall_System_Threading_Thread_VolatileReadDouble (void *ptr) +{ + return *((volatile double *) (ptr)); +} + +float +ves_icall_System_Threading_Thread_VolatileReadFloat (void *ptr) +{ + return *((volatile float *) (ptr)); +} + void ves_icall_System_Threading_Thread_VolatileWrite1 (void *ptr, gint8 value) { @@ -2601,15 +2477,24 @@ ves_icall_System_Threading_Thread_VolatileWriteObject (void *ptr, void *value) mono_gc_wbarrier_generic_store (ptr, value); } +void +ves_icall_System_Threading_Thread_VolatileWriteDouble (void *ptr, double value) +{ + *((volatile double *) ptr) = value; +} + +void +ves_icall_System_Threading_Thread_VolatileWriteFloat (void *ptr, float value) +{ + *((volatile float *) ptr) = value; +} + void mono_thread_init (MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb) { - MONO_GC_REGISTER_ROOT (small_id_table); InitializeCriticalSection(&threads_mutex); InitializeCriticalSection(&interlocked_mutex); InitializeCriticalSection(&contexts_mutex); - InitializeCriticalSection(&delayed_free_table_mutex); - InitializeCriticalSection(&small_id_mutex); background_change_event = CreateEvent (NULL, TRUE, FALSE, NULL); g_assert(background_change_event != NULL); @@ -2617,14 +2502,13 @@ void mono_thread_init (MonoThreadStartCB start_cb, mono_init_static_data_info (&thread_static_info); mono_init_static_data_info (&context_static_info); - current_object_key=TlsAlloc(); + MONO_FAST_TLS_INIT (tls_current_object); + mono_native_tls_alloc (¤t_object_key, NULL); 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; - delayed_free_table = g_array_new (FALSE, FALSE, sizeof (DelayedFreeItem)); - /* 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 @@ -2635,8 +2519,6 @@ void mono_thread_init (MonoThreadStartCB start_cb, void mono_thread_cleanup (void) { - mono_thread_hazardous_try_free_all (); - #if !defined(HOST_WIN32) && !defined(RUN_IN_SUBTHREAD) /* The main thread must abandon any held mutexes (particularly * important for named mutexes as they are shared across @@ -2664,10 +2546,7 @@ void mono_thread_cleanup (void) CloseHandle (background_change_event); #endif - g_array_free (delayed_free_table, TRUE); - delayed_free_table = NULL; - - TlsFree (current_object_key); + mono_native_tls_free (current_object_key); } void @@ -2712,7 +2591,7 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout) 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); + ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE); if(ret==WAIT_FAILED) { /* See the comment in build_wait_tids() */ @@ -2766,7 +2645,7 @@ static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeo count++; } - ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, FALSE); + ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE); if(ret==WAIT_FAILED) { /* See the comment in build_wait_tids() */ @@ -2916,6 +2795,9 @@ mono_threads_set_shutting_down (void) LeaveCriticalSection (current_thread->synch_cs); } + /*since we're killing the thread, unset the current domain.*/ + mono_domain_unset (); + /* Wake up other threads potentially waiting for us */ ExitThread (0); } else { @@ -2948,8 +2830,10 @@ mono_threads_is_shutting_down (void) void mono_thread_manage (void) { - struct wait_data *wait=g_new0 (struct wait_data, 1); + struct wait_data wait_data; + struct wait_data *wait = &wait_data; + memset (wait, 0, sizeof (struct wait_data)); /* join each thread that's still running */ THREAD_DEBUG (g_message ("%s: Joining each running thread...", __func__)); @@ -2957,7 +2841,6 @@ void mono_thread_manage (void) if(threads==NULL) { THREAD_DEBUG (g_message("%s: No threads", __func__)); mono_threads_unlock (); - g_free (wait); return; } mono_threads_unlock (); @@ -2974,6 +2857,8 @@ void mono_thread_manage (void) ResetEvent (background_change_event); wait->num=0; + /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/ + memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); mono_g_hash_table_foreach (threads, build_wait_tids, wait); mono_threads_unlock (); if(wait->num>0) { @@ -3000,6 +2885,8 @@ void mono_thread_manage (void) mono_threads_lock (); wait->num = 0; + /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/ + memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); mono_g_hash_table_foreach_remove (threads, remove_and_abort_threads, wait); mono_threads_unlock (); @@ -3019,8 +2906,6 @@ void mono_thread_manage (void) #ifndef HOST_WIN32 sched_yield (); #endif - - g_free (wait); } static void terminate_thread (gpointer key, gpointer value, gpointer user) @@ -3081,13 +2966,15 @@ collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data) */ void mono_thread_suspend_all_other_threads (void) { - struct wait_data *wait = g_new0 (struct wait_data, 1); + struct wait_data wait_data; + struct wait_data *wait = &wait_data; int i; gsize self = GetCurrentThreadId (); gpointer *events; guint32 eventidx = 0; gboolean starting, finished; + memset (wait, 0, sizeof (struct wait_data)); /* * The other threads could be in an arbitrary state at this point, i.e. * they could be starting up, shutting down etc. This means that there could be @@ -3114,6 +3001,8 @@ void mono_thread_suspend_all_other_threads (void) * threads while threads_mutex is held. */ wait->num = 0; + /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/ + memset (wait->threads, 0, MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P); mono_threads_lock (); mono_g_hash_table_foreach (threads, collect_threads_for_suspend, wait); mono_threads_unlock (); @@ -3167,17 +3056,22 @@ void mono_thread_suspend_all_other_threads (void) LeaveCriticalSection (thread->synch_cs); /* Signal the thread to suspend */ - if (signal_suspend) + if (mono_thread_info_new_interrupt_enabled ()) + suspend_thread_internal (thread, TRUE); + else if (signal_suspend) signal_thread_state_change (thread); } - if (eventidx > 0) { + /*Only wait on the suspend event if we are using the old path */ + if (eventidx > 0 && !mono_thread_info_new_interrupt_enabled ()) { WaitForMultipleObjectsEx (eventidx, events, TRUE, 100, FALSE); for (i = 0; i < wait->num; ++i) { MonoInternalThread *thread = wait->threads [i]; if (thread == NULL) continue; + + ensure_synch_cs_set (thread); EnterCriticalSection (thread->synch_cs); if ((thread->state & ThreadState_Suspended) != 0) { @@ -3186,7 +3080,9 @@ void mono_thread_suspend_all_other_threads (void) } LeaveCriticalSection (thread->synch_cs); } - } else { + } + + if (eventidx <= 0) { /* * If there are threads which are starting up, we wait until they * are suspended when they try to register in the threads hash. @@ -3208,8 +3104,6 @@ void mono_thread_suspend_all_other_threads (void) g_free (events); } - - g_free (wait); } static void @@ -3230,6 +3124,109 @@ collect_threads (gpointer key, gpointer value, gpointer user_data) } } +static gboolean thread_dump_requested; + +static G_GNUC_UNUSED gboolean +print_stack_frame_to_string (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + GString *p = (GString*)data; + MonoMethod *method = NULL; + if (frame->ji) + method = frame->ji->method; + + if (method) { + gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain); + g_string_append_printf (p, " %s\n", location); + g_free (location); + } else + g_string_append_printf (p, " at <0x%05x>\n", frame->native_offset); + + return FALSE; +} + +static void +print_thread_dump (MonoInternalThread *thread, MonoThreadInfo *info) +{ + GString* text = g_string_new (0); + char *name; + GError *error = NULL; + + if (thread->name) { + name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error); + g_assert (!error); + g_string_append_printf (text, "\n\"%s\"", name); + g_free (name); + } + else if (thread->threadpool_thread) + g_string_append (text, "\n\"\""); + else + g_string_append (text, "\n\"\""); + +#if 0 +/* This no longer works with remote unwinding */ +#ifndef HOST_WIN32 + wapi_desc = wapi_current_thread_desc (); + g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread, wapi_desc); + free (wapi_desc); +#endif +#endif + + mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text); + mono_thread_info_resume (mono_thread_info_get_tid (info)); + + fprintf (stdout, "%s", text->str); + +#if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG + OutputDebugStringA(text->str); +#endif + + g_string_free (text, TRUE); + fflush (stdout); +} + +static void +dump_thread (gpointer key, gpointer value, gpointer user) +{ + MonoInternalThread *thread = (MonoInternalThread *)value; + MonoThreadInfo *info; + + if (thread == mono_thread_internal_current ()) + return; + + /* + FIXME This still can hang if we stop a thread during malloc. + FIXME This can hang if we suspend on a critical method and the GC kicks in. A fix might be to have function + that takes a callback and runs it with the target suspended. + We probably should loop a bit around trying to get it to either managed code + or WSJ state. + */ + info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE); + + if (!info) + return; + + print_thread_dump (thread, info); +} + +void +mono_threads_perform_thread_dump (void) +{ + if (!thread_dump_requested) + return; + + printf ("Full thread dump:\n"); + + /* + * 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, dump_thread, NULL); + mono_threads_unlock (); + + thread_dump_requested = FALSE; +} + /** * mono_threads_request_thread_dump: * @@ -3238,9 +3235,20 @@ collect_threads (gpointer key, gpointer value, gpointer user_data) void mono_threads_request_thread_dump (void) { - struct wait_data *wait = g_new0 (struct wait_data, 1); + struct wait_data wait_data; + struct wait_data *wait = &wait_data; int i; + /*The new thread dump code runs out of the finalizer thread. */ + if (mono_thread_info_new_interrupt_enabled ()) { + thread_dump_requested = TRUE; + mono_gc_finalize_notify (); + return; + } + + + memset (wait, 0, sizeof (struct wait_data)); + /* * Make a copy of the hashtable since we can't do anything with * threads while threads_mutex is held. @@ -3264,6 +3272,75 @@ mono_threads_request_thread_dump (void) } } +struct ref_stack { + gpointer *refs; + gint allocated; /* +1 so that refs [allocated] == NULL */ + gint bottom; +}; + +typedef struct ref_stack RefStack; + +static RefStack * +ref_stack_new (gint initial_size) +{ + RefStack *rs; + + initial_size = MAX (initial_size, 16) + 1; + rs = g_new0 (RefStack, 1); + rs->refs = g_new0 (gpointer, initial_size); + rs->allocated = initial_size; + return rs; +} + +static void +ref_stack_destroy (gpointer ptr) +{ + RefStack *rs = ptr; + + if (rs != NULL) { + g_free (rs->refs); + g_free (rs); + } +} + +static void +ref_stack_push (RefStack *rs, gpointer ptr) +{ + g_assert (rs != NULL); + + if (rs->bottom >= rs->allocated) { + rs->refs = g_realloc (rs->refs, rs->allocated * 2 * sizeof (gpointer) + 1); + rs->allocated <<= 1; + rs->refs [rs->allocated] = NULL; + } + rs->refs [rs->bottom++] = ptr; +} + +static void +ref_stack_pop (RefStack *rs) +{ + if (rs == NULL || rs->bottom == 0) + return; + + rs->bottom--; + rs->refs [rs->bottom] = NULL; +} + +static gboolean +ref_stack_find (RefStack *rs, gpointer ptr) +{ + gpointer *refs; + + if (rs == NULL) + return FALSE; + + for (refs = rs->refs; refs && *refs; refs++) { + if (*refs == ptr) + return TRUE; + } + return FALSE; +} + /* * mono_thread_push_appdomain_ref: * @@ -3278,9 +3355,11 @@ mono_thread_push_appdomain_ref (MonoDomain *domain) if (thread) { /* 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); - mono_threads_unlock (); + SPIN_LOCK (thread->lock_thread_id); + if (thread->appdomain_refs == NULL) + thread->appdomain_refs = ref_stack_new (16); + ref_stack_push (thread->appdomain_refs, domain); + SPIN_UNLOCK (thread->lock_thread_id); } } @@ -3291,11 +3370,9 @@ mono_thread_pop_appdomain_ref (void) if (thread) { /* 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); - mono_threads_unlock (); + SPIN_LOCK (thread->lock_thread_id); + ref_stack_pop (thread->appdomain_refs); + SPIN_UNLOCK (thread->lock_thread_id); } } @@ -3303,9 +3380,9 @@ gboolean mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain) { gboolean res; - mono_threads_lock (); - res = g_slist_find (thread->appdomain_refs, domain) != NULL; - mono_threads_unlock (); + SPIN_LOCK (thread->lock_thread_id); + res = ref_stack_find (thread->appdomain_refs, domain); + SPIN_UNLOCK (thread->lock_thread_id); return res; } @@ -3465,20 +3542,51 @@ static const int static_data_size [NUM_STATIC_DATA_IDX] = { }; #endif +static uintptr_t* static_reference_bitmaps [NUM_STATIC_DATA_IDX]; + +static void +mark_tls_slots (void *addr, MonoGCMarkFunc mark_func) +{ + int i; + gpointer *static_data = addr; + for (i = 0; i < NUM_STATIC_DATA_IDX; ++i) { + int j, numwords; + void **ptr; + if (!static_data [i]) + continue; + numwords = 1 + static_data_size [i] / sizeof (gpointer) / (sizeof(uintptr_t) * 8); + ptr = static_data [i]; + for (j = 0; j < numwords; ++j, ptr += sizeof (uintptr_t) * 8) { + uintptr_t bmap = static_reference_bitmaps [i][j]; + void ** p = ptr; + while (bmap) { + if ((bmap & 1) && *p) { + mark_func (p); + } + p++; + bmap >>= 1; + } + } + } +} + /* * mono_alloc_static_data * * Allocate memory blocks for storing threads or context static data */ static void -mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset) +mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal) { guint idx = (offset >> 24) - 1; int i; gpointer* static_data = *static_data_ptr; if (!static_data) { - static_data = mono_gc_alloc_fixed (static_data_size [0], NULL); + static void* tls_desc = NULL; + if (mono_gc_user_markers_supported () && !tls_desc) + tls_desc = mono_gc_make_root_descr_user (mark_tls_slots); + static_data = mono_gc_alloc_fixed (static_data_size [0], threadlocal?tls_desc:NULL); *static_data_ptr = static_data; static_data [0] = static_data; } @@ -3486,10 +3594,28 @@ mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset) for (i = 1; i <= idx; ++i) { if (static_data [i]) continue; - static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL); + if (mono_gc_user_markers_supported () && threadlocal) + static_data [i] = g_malloc0 (static_data_size [i]); + else + static_data [i] = mono_gc_alloc_fixed (static_data_size [i], NULL); } } +static void +mono_free_static_data (gpointer* static_data, gboolean threadlocal) +{ + int i; + for (i = 1; i < NUM_STATIC_DATA_IDX; ++i) { + if (!static_data [i]) + continue; + if (mono_gc_user_markers_supported () && threadlocal) + g_free (static_data [i]); + else + mono_gc_free_fixed (static_data [i]); + } + mono_gc_free_fixed (static_data); +} + /* * mono_init_static_data_info * @@ -3546,7 +3672,7 @@ thread_adjust_static_data (MonoInternalThread *thread) 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); + mono_alloc_static_data (&(thread->static_data), offset, TRUE); } mono_threads_unlock (); } @@ -3557,7 +3683,7 @@ alloc_thread_static_data_helper (gpointer key, gpointer value, gpointer user) MonoInternalThread *thread = value; guint32 offset = GPOINTER_TO_UINT (user); - mono_alloc_static_data (&(thread->static_data), offset); + mono_alloc_static_data (&(thread->static_data), offset, TRUE); } static MonoThreadDomainTls* @@ -3578,6 +3704,39 @@ search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 return NULL; } +static void +update_tls_reference_bitmap (guint32 offset, uintptr_t *bitmap, int numbits) +{ + int i; + int idx = (offset >> 24) - 1; + uintptr_t *rb; + if (!static_reference_bitmaps [idx]) + static_reference_bitmaps [idx] = g_new0 (uintptr_t, 1 + static_data_size [idx] / sizeof(gpointer) / (sizeof(uintptr_t) * 8)); + rb = static_reference_bitmaps [idx]; + offset &= 0xffffff; + offset /= sizeof (gpointer); + /* offset is now the bitmap offset */ + for (i = 0; i < numbits; ++i) { + if (bitmap [i / sizeof (uintptr_t)] & (1L << (i & (sizeof (uintptr_t) * 8 -1)))) + rb [(offset + i) / (sizeof (uintptr_t) * 8)] |= (1L << ((offset + i) & (sizeof (uintptr_t) * 8 -1))); + } +} + +static void +clear_reference_bitmap (guint32 offset, guint32 size) +{ + int idx = (offset >> 24) - 1; + uintptr_t *rb; + rb = static_reference_bitmaps [idx]; + offset &= 0xffffff; + offset /= sizeof (gpointer); + size /= sizeof (gpointer); + size += offset; + /* offset is now the bitmap offset */ + for (; offset < size; ++offset) + rb [offset / (sizeof (uintptr_t) * 8)] &= ~(1L << (offset & (sizeof (uintptr_t) * 8 -1))); +} + /* * The offset for a special static variable is composed of three parts: * a bit that indicates the type of static data (0:thread, 1:context), @@ -3587,11 +3746,10 @@ search_tls_slot_in_freelist (StaticDataInfo *static_data, guint32 size, guint32 */ guint32 -mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align) +mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align, uintptr_t *bitmap, int numbits) { guint32 offset; - if (static_type == SPECIAL_STATIC_THREAD) - { + if (static_type == SPECIAL_STATIC_THREAD) { MonoThreadDomainTls *item; mono_threads_lock (); item = search_tls_slot_in_freelist (&thread_static_info, size, align); @@ -3602,13 +3760,12 @@ mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align } else { offset = mono_alloc_static_data_slot (&thread_static_info, size, align); } + update_tls_reference_bitmap (offset, bitmap, numbits); /* This can be called during startup */ if (threads != NULL) mono_g_hash_table_foreach (threads, alloc_thread_static_data_helper, GUINT_TO_POINTER (offset)); mono_threads_unlock (); - } - else - { + } else { g_assert (static_type == SPECIAL_STATIC_CONTEXT); mono_contexts_lock (); offset = mono_alloc_static_data_slot (&context_static_info, size, align); @@ -3619,7 +3776,7 @@ mono_alloc_special_static_data (guint32 static_type, guint32 size, guint32 align } gpointer -mono_get_special_static_data (guint32 offset) +mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset) { /* The high bit means either thread (0) or static (1) data. */ @@ -3630,7 +3787,7 @@ mono_get_special_static_data (guint32 offset) idx = (offset >> 24) - 1; if (static_type == 0) { - return get_thread_static_data (mono_thread_internal_current (), offset); + return get_thread_static_data (thread, offset); } else { /* Allocate static data block under demand, since we don't have a list // of contexts @@ -3638,13 +3795,19 @@ mono_get_special_static_data (guint32 offset) MonoAppContext *context = mono_context_get (); if (!context->static_data || !context->static_data [idx]) { mono_contexts_lock (); - mono_alloc_static_data (&(context->static_data), offset); + mono_alloc_static_data (&(context->static_data), offset, FALSE); mono_contexts_unlock (); } return ((char*) context->static_data [idx]) + (offset & 0xffffff); } } +gpointer +mono_get_special_static_data (guint32 offset) +{ + return mono_get_special_static_data_for_thread (mono_thread_internal_current (), offset); +} + typedef struct { guint32 offset; guint32 size; @@ -3661,35 +3824,48 @@ free_thread_static_data_helper (gpointer key, gpointer value, gpointer user) if (!thread->static_data || !thread->static_data [idx]) return; ptr = ((char*) thread->static_data [idx]) + (data->offset & 0xffffff); - memset (ptr, 0, data->size); + mono_gc_bzero (ptr, data->size); } static void -do_free_special (gpointer key, gpointer value, gpointer data) +do_free_special_slot (guint32 offset, guint32 size) { - MonoClassField *field = key; - guint32 offset = GPOINTER_TO_UINT (value); guint32 static_type = (offset & 0x80000000); - gint32 align; - guint32 size; - size = mono_type_size (field->type, &align); /*g_print ("free %s , size: %d, offset: %x\n", field->name, size, offset);*/ if (static_type == 0) { TlsOffsetSize data; MonoThreadDomainTls *item = g_new0 (MonoThreadDomainTls, 1); data.offset = offset & 0x7fffffff; data.size = size; + clear_reference_bitmap (data.offset, data.size); if (threads != NULL) mono_g_hash_table_foreach (threads, free_thread_static_data_helper, &data); item->offset = offset; item->size = size; - item->next = thread_static_info.freelist; - thread_static_info.freelist = item; + + if (!mono_runtime_is_shutting_down ()) { + item->next = thread_static_info.freelist; + thread_static_info.freelist = item; + } else { + /* We could be called during shutdown after mono_thread_cleanup () is called */ + g_free (item); + } } else { /* FIXME: free context static data as well */ } } +static void +do_free_special (gpointer key, gpointer value, gpointer data) +{ + MonoClassField *field = key; + guint32 offset = GPOINTER_TO_UINT (value); + gint32 align; + guint32 size; + size = mono_type_size (field->type, &align); + do_free_special_slot (offset, size); +} + void mono_alloc_special_static_data_free (GHashTable *special_static_fields) { @@ -3698,6 +3874,85 @@ mono_alloc_special_static_data_free (GHashTable *special_static_fields) mono_threads_unlock (); } +void +mono_special_static_data_free_slot (guint32 offset, guint32 size) +{ + mono_threads_lock (); + do_free_special_slot (offset, size); + mono_threads_unlock (); +} + +/* + * allocates room in the thread local area for storing an instance of the struct type + * the allocation is kept track of in domain->tlsrec_list. + */ +uint32_t +mono_thread_alloc_tls (MonoReflectionType *type) +{ + MonoDomain *domain = mono_domain_get (); + MonoClass *klass; + MonoTlsDataRecord *tlsrec; + int max_set = 0; + gsize *bitmap; + gsize default_bitmap [4] = {0}; + uint32_t tls_offset; + guint32 size; + gint32 align; + + klass = mono_class_from_mono_type (type->type); + /* TlsDatum is a struct, so we subtract the object header size offset */ + bitmap = mono_class_compute_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE); + size = mono_type_size (type->type, &align); + tls_offset = mono_alloc_special_static_data (SPECIAL_STATIC_THREAD, size, align, (uintptr_t*)bitmap, max_set + 1); + if (bitmap != default_bitmap) + g_free (bitmap); + tlsrec = g_new0 (MonoTlsDataRecord, 1); + tlsrec->tls_offset = tls_offset; + tlsrec->size = size; + mono_domain_lock (domain); + tlsrec->next = domain->tlsrec_list; + domain->tlsrec_list = tlsrec; + mono_domain_unlock (domain); + return tls_offset; +} + +void +mono_thread_destroy_tls (uint32_t tls_offset) +{ + MonoTlsDataRecord *prev = NULL; + MonoTlsDataRecord *cur; + guint32 size = 0; + MonoDomain *domain = mono_domain_get (); + mono_domain_lock (domain); + cur = domain->tlsrec_list; + while (cur) { + if (cur->tls_offset == tls_offset) { + if (prev) + prev->next = cur->next; + else + domain->tlsrec_list = cur->next; + size = cur->size; + g_free (cur); + break; + } + prev = cur; + cur = cur->next; + } + mono_domain_unlock (domain); + if (size) + mono_special_static_data_free_slot (tls_offset, size); +} + +/* + * This is just to ensure cleanup: the finalizers should have taken care, so this is not perf-critical. + */ +void +mono_thread_destroy_domain_tls (MonoDomain *domain) +{ + while (domain->tlsrec_list) + mono_thread_destroy_tls (domain->tlsrec_list->tls_offset); +} + static MonoClassField *local_slots = NULL; typedef struct { @@ -3808,39 +4063,7 @@ static MonoException* mono_thread_execute_interruption (MonoInternalThread *thre return thread->abort_exc; } else if ((thread->state & ThreadState_SuspendRequested) != 0) { - thread->state &= ~ThreadState_SuspendRequested; - thread->state |= ThreadState_Suspended; - thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL); - if (thread->suspend_event == NULL) { - LeaveCriticalSection (thread->synch_cs); - return(NULL); - } - if (thread->suspended_event) - SetEvent (thread->suspended_event); - - LeaveCriticalSection (thread->synch_cs); - - if (shutting_down) { - /* After we left the lock, the runtime might shut down so everything becomes invalid */ - for (;;) - Sleep (1000); - } - - WaitForSingleObject (thread->suspend_event, INFINITE); - - EnterCriticalSection (thread->synch_cs); - - CloseHandle (thread->suspend_event); - thread->suspend_event = NULL; - thread->state &= ~ThreadState_Suspended; - - /* The thread that requested the resume will have replaced this event - * and will be waiting for it - */ - SetEvent (thread->resume_event); - - LeaveCriticalSection (thread->synch_cs); - + self_suspend_internal (thread); return NULL; } else if ((thread->state & ThreadState_StopRequested) != 0) { @@ -3850,6 +4073,14 @@ static MonoException* mono_thread_execute_interruption (MonoInternalThread *thre mono_thread_exit (); return NULL; + } else if (thread->pending_exception) { + MonoException *exc; + + exc = thread->pending_exception; + thread->pending_exception = NULL; + + LeaveCriticalSection (thread->synch_cs); + return exc; } else if (thread->thread_interrupt_requested) { thread->thread_interrupt_requested = FALSE; @@ -3889,13 +4120,13 @@ mono_thread_request_interruption (gboolean running_managed) if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) return NULL; + InterlockedIncrement (&thread_interruption_requested); 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. */ - InterlockedIncrement (&thread_interruption_requested); if (mono_thread_notify_pending_exc_fn && !running_managed) /* The JIT will notify the thread about the interruption */ @@ -3913,6 +4144,37 @@ mono_thread_request_interruption (gboolean running_managed) } } +/*This function should be called by a thread after it has exited all of + * its handle blocks at interruption time.*/ +MonoException* +mono_thread_resume_interruption (void) +{ + MonoInternalThread *thread = mono_thread_internal_current (); + gboolean still_aborting; + + /* The thread may already be stopping */ + if (thread == NULL) + return NULL; + + ensure_synch_cs_set (thread); + EnterCriticalSection (thread->synch_cs); + still_aborting = (thread->state & ThreadState_AbortRequested) != 0; + LeaveCriticalSection (thread->synch_cs); + + /*This can happen if the protected block called Thread::ResetAbort*/ + if (!still_aborting) + return FALSE; + + if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) + return NULL; + InterlockedIncrement (&thread_interruption_requested); + +#ifndef HOST_WIN32 + wapi_self_interrupt (); +#endif + return mono_thread_execute_interruption (thread); +} + gboolean mono_thread_interruption_requested () { if (thread_interruption_requested) { @@ -3988,12 +4250,8 @@ mono_thread_get_and_clear_pending_exception (void) /* * 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. + * Set the pending exception of the current thread to EXC. + * The exception will be thrown when execution returns to managed code. */ void mono_set_pending_exception (MonoException *exc) @@ -4004,14 +4262,9 @@ mono_set_pending_exception (MonoException *exc) if (thread == NULL) return; - if (mono_thread_notify_pending_exc_fn) { - MONO_OBJECT_SETREF (thread, pending_exception, exc); + 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); - } + mono_thread_request_interruption (FALSE); } /** @@ -4098,26 +4351,27 @@ mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test) return ret; } -static MonoClassField *execution_context_field; +//static MonoClassField *execution_context_field; static MonoObject** get_execution_context_addr (void) { MonoDomain *domain = mono_domain_get (); - guint32 offset; + guint32 offset = domain->execution_context_field_offset; - if (!execution_context_field) { - execution_context_field = mono_class_get_field_from_name (mono_defaults.thread_class, - "_ec"); - g_assert (execution_context_field); - } + if (!offset) { + MonoClassField *field = mono_class_get_field_from_name (mono_defaults.thread_class, "_ec"); + g_assert (field); - g_assert (mono_class_try_get_vtable (domain, mono_defaults.appdomain_class)); + g_assert (mono_class_try_get_vtable (domain, mono_defaults.appdomain_class)); - mono_domain_lock (domain); - offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, execution_context_field)); - mono_domain_unlock (domain); - g_assert (offset); + mono_domain_lock (domain); + offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field)); + mono_domain_unlock (domain); + g_assert (offset); + + domain->execution_context_field_offset = offset; + } return (MonoObject**) mono_get_special_static_data (offset); } @@ -4147,3 +4401,274 @@ mono_runtime_has_tls_get (void) { return has_tls_get; } + +int +mono_thread_kill (MonoInternalThread *thread, int signal) +{ +#ifdef HOST_WIN32 + /* Win32 uses QueueUserAPC and callers of this are guarded */ + g_assert_not_reached (); +#else +# ifdef PTHREAD_POINTER_ID + return pthread_kill ((gpointer)(gsize)(thread->tid), mono_thread_get_abort_signal ()); +# else +# ifdef PLATFORM_ANDROID + if (thread->android_tid != 0) { + int ret; + int old_errno = errno; + + ret = tkill ((pid_t) thread->android_tid, signal); + if (ret < 0) { + ret = errno; + errno = old_errno; + } + + return ret; + } + else + return pthread_kill (thread->tid, mono_thread_get_abort_signal ()); +# else + return pthread_kill (thread->tid, mono_thread_get_abort_signal ()); +# endif +# endif +#endif +} + +static void +self_interrupt_thread (void *_unused) +{ + MonoThreadInfo *info = mono_thread_info_current (); + MonoException *exc = mono_thread_execute_interruption (mono_thread_internal_current ()); + if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/ + mono_raise_exception_with_context (exc, &info->suspend_state.ctx); + g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/ +} + +static gboolean +mono_jit_info_match (MonoJitInfo *ji, gpointer ip) +{ + if (!ji) + return FALSE; + return ji->code_start <= ip && (char*)ip < (char*)ji->code_start + ji->code_size; +} + +static gboolean +last_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + MonoJitInfo **dest = data; + *dest = frame->ji; + return TRUE; +} + +static MonoJitInfo* +mono_thread_info_get_last_managed (MonoThreadInfo *info) +{ + MonoJitInfo *ji = NULL; + mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, &ji); + return ji; +} + +static void +abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception, gboolean install_async_abort) +{ + MonoJitInfo *ji; + MonoThreadInfo *info = NULL; + gboolean protected_wrapper; + gboolean running_managed; + + if (!mono_thread_info_new_interrupt_enabled ()) { + signal_thread_state_change (thread); + return; + } + + /* + FIXME this is insanely broken, it doesn't cause interruption to happen + synchronously since passing FALSE to mono_thread_request_interruption makes sure it returns NULL + */ + if (thread == mono_thread_internal_current ()) { + /* Do it synchronously */ + MonoException *exc = mono_thread_request_interruption (can_raise_exception); + if (exc) + mono_raise_exception (exc); +#ifndef HOST_WIN32 + wapi_interrupt_thread (thread->handle); +#endif + return; + } + + /*FIXME we need to check 2 conditions here, request to interrupt this thread or if the target died*/ + if (!(info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gsize)thread->tid, TRUE))) { + return; + } + + if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (&info->suspend_state)) { + mono_thread_info_resume (mono_thread_info_get_tid (info)); + return; + } + + /*someone is already interrupting it*/ + if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1) { + mono_thread_info_resume (mono_thread_info_get_tid (info)); + return; + } + InterlockedIncrement (&thread_interruption_requested); + + ji = mono_thread_info_get_last_managed (info); + protected_wrapper = ji && mono_threads_is_critical_method (ji->method); + running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); + + if (!protected_wrapper && running_managed) { + /*We are in managed code*/ + /*Set the thread to call */ + if (install_async_abort) + mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL); + mono_thread_info_resume (mono_thread_info_get_tid (info)); + } else { + gpointer interrupt_handle; + /* + * 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. + */ +#ifndef HOST_WIN32 + interrupt_handle = wapi_prepare_interrupt_thread (thread->handle); +#endif + mono_thread_info_resume (mono_thread_info_get_tid (info)); +#ifndef HOST_WIN32 + wapi_finish_interrupt_thread (interrupt_handle); +#endif + } + /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/ +} + +static void +transition_to_suspended (MonoInternalThread *thread) +{ + if ((thread->state & ThreadState_SuspendRequested) == 0) { + g_assert (0); /*FIXME we should not reach this */ + /*Make sure we balance the suspend count.*/ + mono_thread_info_resume ((MonoNativeThreadId)(gpointer)(gsize)thread->tid); + } else { + thread->state &= ~ThreadState_SuspendRequested; + thread->state |= ThreadState_Suspended; + } + LeaveCriticalSection (thread->synch_cs); +} + +static void +suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt) +{ + if (!mono_thread_info_new_interrupt_enabled ()) { + signal_thread_state_change (thread); + return; + } + + EnterCriticalSection (thread->synch_cs); + if (thread == mono_thread_internal_current ()) { + transition_to_suspended (thread); + mono_thread_info_self_suspend (); + } else { + MonoThreadInfo *info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gsize)thread->tid, interrupt); + MonoJitInfo *ji = mono_thread_info_get_last_managed (info); + gboolean protected_wrapper = ji && mono_threads_is_critical_method (ji->method); + gboolean running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); + + if (running_managed && !protected_wrapper) { + transition_to_suspended (thread); + } else { + gpointer interrupt_handle; + + if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0) + InterlockedIncrement (&thread_interruption_requested); +#ifndef HOST_WIN32 + if (interrupt) + interrupt_handle = wapi_prepare_interrupt_thread (thread->handle); +#endif + mono_thread_info_resume (mono_thread_info_get_tid (info)); +#ifndef HOST_WIN32 + if (interrupt) + wapi_finish_interrupt_thread (interrupt_handle); +#endif + LeaveCriticalSection (thread->synch_cs); + } + } +} + +/*This is called with @thread synch_cs held and it must release it*/ +static void +self_suspend_internal (MonoInternalThread *thread) +{ + if (!mono_thread_info_new_interrupt_enabled ()) { + thread->state &= ~ThreadState_SuspendRequested; + thread->state |= ThreadState_Suspended; + thread->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (thread->suspend_event == NULL) { + LeaveCriticalSection (thread->synch_cs); + return; + } + if (thread->suspended_event) + SetEvent (thread->suspended_event); + + LeaveCriticalSection (thread->synch_cs); + + if (shutting_down) { + /* After we left the lock, the runtime might shut down so everything becomes invalid */ + for (;;) + Sleep (1000); + } + + WaitForSingleObject (thread->suspend_event, INFINITE); + + EnterCriticalSection (thread->synch_cs); + + CloseHandle (thread->suspend_event); + thread->suspend_event = NULL; + thread->state &= ~ThreadState_Suspended; + + /* The thread that requested the resume will have replaced this event + * and will be waiting for it + */ + SetEvent (thread->resume_event); + + LeaveCriticalSection (thread->synch_cs); + return; + } + + transition_to_suspended (thread); + mono_thread_info_self_suspend (); +} + +/*This is called with @thread synch_cs held and it must release it*/ +static gboolean +resume_thread_internal (MonoInternalThread *thread) +{ + if (!mono_thread_info_new_interrupt_enabled ()) { + thread->resume_event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (thread->resume_event == NULL) { + LeaveCriticalSection (thread->synch_cs); + return FALSE; + } + + /* Awake the thread */ + SetEvent (thread->suspend_event); + + LeaveCriticalSection (thread->synch_cs); + + /* Wait for the thread to awake */ + WaitForSingleObject (thread->resume_event, INFINITE); + CloseHandle (thread->resume_event); + thread->resume_event = NULL; + return TRUE; + } + + LeaveCriticalSection (thread->synch_cs); + /* Awake the thread */ + if (!mono_thread_info_resume ((MonoNativeThreadId)(gpointer)(gsize)thread->tid)) + return FALSE; + EnterCriticalSection (thread->synch_cs); + thread->state &= ~ThreadState_Suspended; + LeaveCriticalSection (thread->synch_cs); + return TRUE; +}