diff --git a/acceptance-tests/profiler-stress/runner.cs b/acceptance-tests/profiler-stress/runner.cs index 84bb9846a561..bd9912659a2f 100644 --- a/acceptance-tests/profiler-stress/runner.cs +++ b/acceptance-tests/profiler-stress/runner.cs @@ -40,7 +40,6 @@ sealed class TestResult { public int? ExitCode { get; set; } public string StandardOutput { get; set; } public string StandardError { get; set; } - public string CombinedOutput { get; set; } } static class Program { @@ -191,22 +190,17 @@ static int Main () var stdout = new StringBuilder (); var stderr = new StringBuilder (); - var combined = new StringBuilder (); proc.OutputDataReceived += (sender, args) => { if (args.Data != null) - lock (result) { + lock (result) stdout.AppendLine (args.Data); - combined.AppendLine (args.Data); - } }; proc.ErrorDataReceived += (sender, args) => { if (args.Data != null) - lock (result) { + lock (result) stderr.AppendLine (args.Data); - combined.AppendLine (args.Data); - } }; result.Stopwatch.Start (); @@ -233,7 +227,6 @@ static int Main () lock (result) { result.StandardOutput = stdout.ToString (); result.StandardError = stderr.ToString (); - result.CombinedOutput = combined.ToString (); } } @@ -245,10 +238,16 @@ static int Main () if (result.ExitCode != 0) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine ("===== stdout + stderr ====="); + Console.WriteLine ("===== stdout ====="); Console.ResetColor (); - Console.WriteLine (result.CombinedOutput); + Console.WriteLine (result.StandardOutput); + + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine ("===== stderr ====="); + Console.ResetColor (); + + Console.WriteLine (result.StandardError); } results.Add (result); diff --git a/libgc/include/gc.h b/libgc/include/gc.h index e7929918ad0b..c9c20e65a6c3 100644 --- a/libgc/include/gc.h +++ b/libgc/include/gc.h @@ -835,6 +835,18 @@ typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data)); GC_API GC_PTR GC_call_with_alloc_lock GC_PROTO((GC_fn_type fn, GC_PTR client_data)); +/* + * These are similar to GC_do_blocking () in upstream bdwgc. The design is + * simpler in that there is no distinction between active and inactive stack + * frames; instead, while a thread is in blocking state, it promises to not + * interact with the GC at all, and to not keep any pointers to GC memory + * around from before entering blocking state. Additionally, these can be + * called unbalanced (they simply set a flag internally). + */ +GC_API void GC_start_blocking GC_PROTO((void)); + +GC_API void GC_end_blocking GC_PROTO((void)); + /* The following routines are primarily intended for use with a */ /* preprocessor which inserts calls to check C pointer arithmetic. */ /* They indicate failure by invoking the corresponding _print_proc. */ diff --git a/libgc/pthread_support.c b/libgc/pthread_support.c index 6d240f65f0ee..3ff89b292220 100644 --- a/libgc/pthread_support.c +++ b/libgc/pthread_support.c @@ -1245,7 +1245,6 @@ void GC_start_blocking(void) { GC_thread me; LOCK(); me = GC_lookup_thread(pthread_self()); - GC_ASSERT(!(me -> thread_blocked)); # ifdef SPARC me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack(); # else @@ -1273,7 +1272,6 @@ void GC_end_blocking(void) { GC_thread me; LOCK(); /* This will block if the world is stopped. */ me = GC_lookup_thread(pthread_self()); - GC_ASSERT(me -> thread_blocked); me -> thread_blocked = FALSE; UNLOCK(); } diff --git a/libgc/win32_threads.c b/libgc/win32_threads.c index 5533b8f2e252..425d41a98922 100644 --- a/libgc/win32_threads.c +++ b/libgc/win32_threads.c @@ -48,6 +48,7 @@ struct GC_thread_Rep { /* 0 ==> entry not valid. */ /* !in_use ==> stack_base == 0 */ GC_bool suspended; + GC_bool thread_blocked; # ifdef CYGWIN32 void *status; /* hold exit value until join in case it's a pointer */ @@ -174,6 +175,7 @@ static GC_thread GC_new_thread(void) { /* the world, wait here. Hopefully this can't happen on any */ /* systems that don't allow us to block here. */ while (GC_please_stop) Sleep(20); + GC_ASSERT(!thread_table[i]->thread_blocked); return thread_table + i; } @@ -223,7 +225,6 @@ static void GC_delete_thread(DWORD thread_id) { } } - #ifdef CYGWIN32 /* Return a GC_thread corresponding to a given pthread_t. */ @@ -245,6 +246,20 @@ static GC_thread GC_lookup_thread(pthread_t id) return thread_table + i; } +#else + +static GC_thread GC_lookup_thread(DWORD id) +{ + int i; + LONG max = GC_get_max_thread_index(); + + for (i = 0; i <= max; i++) + if (thread_table[i].in_use && thread_table[i].id == id) + return &thread_table[i]; + + return NULL; +} + #endif /* CYGWIN32 */ void GC_push_thread_structures GC_PROTO((void)) @@ -264,6 +279,35 @@ void GC_push_thread_structures GC_PROTO((void)) # endif } +/* Wrappers for functions that are likely to block for an appreciable */ +/* length of time. Must be called in pairs, if at all. */ +/* Nothing much beyond the system call itself should be executed */ +/* between these. */ + +void GC_start_blocking(void) { + GC_thread me; + LOCK(); +#ifdef CYGWIN32 + me = GC_lookup_thread(pthread_self()); +#else + me = GC_lookup_thread(GetCurrentThreadId()); +#endif + me->thread_blocked = TRUE; + UNLOCK(); +} + +void GC_end_blocking(void) { + GC_thread me; + LOCK(); /* This will block if the world is stopped. */ +#ifdef CYGWIN32 + me = GC_lookup_thread(pthread_self()); +#else + me = GC_lookup_thread(GetCurrentThreadId()); +#endif + me->thread_blocked = FALSE; + UNLOCK(); +} + /* Defined in misc.c */ extern CRITICAL_SECTION GC_write_cs; @@ -281,6 +325,8 @@ void GC_stop_world() for (i = 0; i <= GC_get_max_thread_index(); i++) if (thread_table[i].stack_base != 0 && thread_table[i].id != thread_id) { + if (thread_table [i].thread_blocked) + continue; # ifdef MSWINCE /* SuspendThread will fail if thread is running kernel code */ while (SuspendThread(thread_table[i].handle) == (DWORD)-1) diff --git a/mono/metadata/boehm-gc.c b/mono/metadata/boehm-gc.c index 60bcbd0e8815..ff6057ff9c4d 100644 --- a/mono/metadata/boehm-gc.c +++ b/mono/metadata/boehm-gc.c @@ -611,7 +611,7 @@ static void mono_push_other_roots (void) { g_hash_table_foreach (roots, push_root, NULL); - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { HandleStack* stack = (HandleStack*)info->handle_stack; if (stack) push_handle_stack (stack); @@ -1478,7 +1478,22 @@ mono_gc_set_stack_end (void *stack_end) { } -void mono_gc_set_skip_thread (gboolean value) +void +mono_gc_skip_thread_changing (gboolean skip) +{ + /* + * Unlike SGen, Boehm doesn't respect our thread info flags. We need to + * inform Boehm manually to skip/not skip the current thread. + */ + + if (skip) + GC_start_blocking (); + else + GC_end_blocking (); +} + +void +mono_gc_skip_thread_changed (gboolean skip) { } diff --git a/mono/metadata/gc-internals.h b/mono/metadata/gc-internals.h index 34885e37d7eb..8273912284af 100644 --- a/mono/metadata/gc-internals.h +++ b/mono/metadata/gc-internals.h @@ -319,7 +319,9 @@ gboolean mono_gc_card_table_nursery_check (void); void* mono_gc_get_nursery (int *shift_bits, size_t *size); -void mono_gc_set_skip_thread (gboolean skip); +// Don't use directly; set/unset MONO_THREAD_INFO_FLAGS_NO_GC instead. +void mono_gc_skip_thread_changing (gboolean skip); +void mono_gc_skip_thread_changed (gboolean skip); #ifndef HOST_WIN32 int mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 32e7ba6228e1..041667d260c4 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -893,7 +893,7 @@ finalizer_thread (gpointer unused) */ g_assert (mono_domain_get () == mono_get_root_domain ()); - mono_gc_set_skip_thread (TRUE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC); if (wait) { /* An alertable wait is required so this thread can be suspended on windows */ @@ -901,7 +901,7 @@ finalizer_thread (gpointer unused) } wait = TRUE; - mono_gc_set_skip_thread (FALSE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); mono_runtime_do_background_work (); diff --git a/mono/metadata/null-gc.c b/mono/metadata/null-gc.c index 6e35625cbeb9..20f3441bdf6a 100644 --- a/mono/metadata/null-gc.c +++ b/mono/metadata/null-gc.c @@ -520,7 +520,14 @@ mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void } #endif -void mono_gc_set_skip_thread (gboolean value) +void +mono_gc_skip_thread_changing (gboolean skip) +{ + // No STW, nothing needs to be done. +} + +void +mono_gc_skip_thread_changed (gboolean skip) { } diff --git a/mono/metadata/object-internals.h b/mono/metadata/object-internals.h index 4fe3aa48138f..74c38c28c22a 100644 --- a/mono/metadata/object-internals.h +++ b/mono/metadata/object-internals.h @@ -1849,7 +1849,7 @@ mono_ldstr_checked (MonoDomain *domain, MonoImage *image, uint32_t str_index, Mo MonoString* mono_string_new_len_checked (MonoDomain *domain, const char *text, guint length, MonoError *error); -MonoString* +MONO_PROFILER_API MonoString* mono_string_new_checked (MonoDomain *domain, const char *text, MonoError *merror); MonoString* diff --git a/mono/metadata/sgen-client-mono.h b/mono/metadata/sgen-client-mono.h index 2792d99fc7c8..e20022579977 100644 --- a/mono/metadata/sgen-client-mono.h +++ b/mono/metadata/sgen-client-mono.h @@ -40,15 +40,6 @@ struct _SgenClientThreadInfo { gboolean skip, suspend_done; volatile int in_critical_region; - /* - This is set the argument of mono_gc_set_skip_thread. - - A thread that knowingly holds no managed state can call this - function around blocking loops to reduce the GC burden by not - been scanned. - */ - gboolean gc_disabled; - #ifdef SGEN_POSIX_STW /* This is -1 until the first suspend. */ int signal; diff --git a/mono/metadata/sgen-mono.c b/mono/metadata/sgen-mono.c index 281d57f9c673..f85bcb4356bb 100644 --- a/mono/metadata/sgen-mono.c +++ b/mono/metadata/sgen-mono.c @@ -879,7 +879,7 @@ mono_gc_clear_domain (MonoDomain * domain) sgen_clear_nursery_fragments (); - FOREACH_THREAD (info) { + FOREACH_THREAD_ALL (info) { mono_handle_stack_free_domain ((HandleStack*)info->client_info.info.handle_stack, domain); } FOREACH_THREAD_END @@ -2074,13 +2074,11 @@ static void report_stack_roots (void) { GCRootReport report = {0}; - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { void *aligned_stack_start; if (info->client_info.skip) { continue; - } else if (info->client_info.gc_disabled) { - continue; } else if (!mono_thread_info_is_live (info)) { continue; } else if (!info->client_info.stack_start) { @@ -2450,7 +2448,7 @@ sgen_client_thread_attach (SgenThreadInfo* info) { mono_tls_set_sgen_thread_info (info); - info->client_info.skip = 0; + info->client_info.skip = FALSE; info->client_info.stack_start = NULL; @@ -2502,23 +2500,32 @@ sgen_client_thread_detach_with_lock (SgenThreadInfo *p) } void -mono_gc_set_skip_thread (gboolean skip) +mono_gc_skip_thread_changing (gboolean skip) { - SgenThreadInfo *info = mono_thread_info_current (); - + /* + * SGen's STW will respect the thread info flags, but we do need to take + * the GC lock when changing them. If we don't do this, SGen might end up + * trying to resume a thread that wasn't suspended because it had + * MONO_THREAD_INFO_FLAGS_NO_GC set when STW began. + */ LOCK_GC; - info->client_info.gc_disabled = skip; - UNLOCK_GC; if (skip) { - /* If we skip scanning a thread with a non-empty handle stack, we may move an + /* + * If we skip scanning a thread with a non-empty handle stack, we may move an * object but fail to update the reference in the handle. */ - HandleStack *stack = info->client_info.info.handle_stack; + HandleStack *stack = mono_thread_info_current ()->client_info.info.handle_stack; g_assert (stack == NULL || mono_handle_stack_is_empty (stack)); } } +void +mono_gc_skip_thread_changed (gboolean skip) +{ + UNLOCK_GC; +} + gboolean mono_gc_thread_in_critical_region (SgenThreadInfo *info) { @@ -2591,16 +2598,13 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p return; #endif - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { int skip_reason = 0; void *aligned_stack_start; if (info->client_info.skip) { SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start); skip_reason = 1; - } else if (info->client_info.gc_disabled) { - SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start); - skip_reason = 2; } else if (!mono_thread_info_is_live (info)) { SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %zd (state %x)", info, info->client_info.stack_start, info->client_info.info.stack_end, (char*)info->client_info.info.stack_end - (char*)info->client_info.stack_start, info->client_info.info.thread_state); skip_reason = 3; diff --git a/mono/metadata/sgen-stw.c b/mono/metadata/sgen-stw.c index 14eedd749680..f9405b82e87d 100644 --- a/mono/metadata/sgen-stw.c +++ b/mono/metadata/sgen-stw.c @@ -157,7 +157,7 @@ sgen_client_restart_world (int generation, gboolean serial_collection, gint64 *s MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_START_WORLD, generation)); MONO_PROFILER_RAISE (gc_event2, (MONO_GC_EVENT_PRE_START_WORLD, generation, serial_collection)); - FOREACH_THREAD (info) { + FOREACH_THREAD_ALL (info) { info->client_info.stack_start = NULL; memset (&info->client_info.ctx, 0, sizeof (MonoContext)); } FOREACH_THREAD_END @@ -208,15 +208,9 @@ static gboolean sgen_is_thread_in_current_stw (SgenThreadInfo *info, int *reason) { /* - A thread explicitly asked to be skiped because it holds no managed state. - This is used by TP and finalizer threads. - FIXME Use an atomic variable for this to avoid everyone taking the GC LOCK. - */ - if (info->client_info.gc_disabled) { - if (reason) - *reason = 1; - return FALSE; - } + * No need to check MONO_THREAD_INFO_FLAGS_NO_GC here as we rely on the + * FOREACH_THREAD_EXCLUDE macro to skip such threads for us. + */ /* We have detected that this thread is failing/dying, ignore it. @@ -268,7 +262,7 @@ sgen_unified_suspend_stop_world (void) mono_threads_begin_global_suspend (); THREADS_STW_DEBUG ("[GC-STW-BEGIN][%p] *** BEGIN SUSPEND *** \n", mono_thread_info_get_tid (mono_thread_info_current ())); - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { info->client_info.skip = FALSE; info->client_info.suspend_done = FALSE; @@ -289,7 +283,7 @@ sgen_unified_suspend_stop_world (void) for (;;) { gint restart_counter = 0; - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { gint suspend_count; int reason = 0; @@ -335,7 +329,7 @@ sgen_unified_suspend_stop_world (void) sleep_duration += 10; } - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); @@ -355,7 +349,7 @@ sgen_unified_suspend_stop_world (void) mono_threads_wait_pending_operations (); } - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { gpointer stopped_ip; int reason = 0; @@ -396,7 +390,7 @@ static void sgen_unified_suspend_restart_world (void) { THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n"); - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { int reason = 0; if (sgen_is_thread_in_current_stw (info, &reason)) { g_assert (mono_thread_info_begin_resume (info)); diff --git a/mono/metadata/threadpool-io-epoll.c b/mono/metadata/threadpool-io-epoll.c index 67d555707a59..40efee6b42d8 100644 --- a/mono/metadata/threadpool-io-epoll.c +++ b/mono/metadata/threadpool-io-epoll.c @@ -85,13 +85,13 @@ epoll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), g memset (epoll_events, 0, sizeof (struct epoll_event) * EPOLL_NEVENTS); - mono_gc_set_skip_thread (TRUE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC); MONO_ENTER_GC_SAFE; ready = epoll_wait (epoll_fd, epoll_events, EPOLL_NEVENTS, -1); MONO_EXIT_GC_SAFE; - mono_gc_set_skip_thread (FALSE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); if (ready == -1) { switch (errno) { diff --git a/mono/metadata/threadpool-io-kqueue.c b/mono/metadata/threadpool-io-kqueue.c index 657fad7e3597..89d46ae40553 100644 --- a/mono/metadata/threadpool-io-kqueue.c +++ b/mono/metadata/threadpool-io-kqueue.c @@ -82,13 +82,13 @@ kqueue_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), memset (kqueue_events, 0, sizeof (struct kevent) * KQUEUE_NEVENTS); - mono_gc_set_skip_thread (TRUE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC); MONO_ENTER_GC_SAFE; ready = kevent (kqueue_fd, NULL, 0, kqueue_events, KQUEUE_NEVENTS, NULL); MONO_EXIT_GC_SAFE; - mono_gc_set_skip_thread (FALSE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); if (ready == -1) { switch (errno) { diff --git a/mono/metadata/threadpool-io-poll.c b/mono/metadata/threadpool-io-poll.c index 8ff6b13c6975..e086835577cd 100644 --- a/mono/metadata/threadpool-io-poll.c +++ b/mono/metadata/threadpool-io-poll.c @@ -140,13 +140,13 @@ poll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gp for (i = 0; i < poll_fds_size; ++i) poll_fds [i].revents = 0; - mono_gc_set_skip_thread (TRUE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC); MONO_ENTER_GC_SAFE; ready = mono_poll (poll_fds, poll_fds_size, -1); MONO_EXIT_GC_SAFE; - mono_gc_set_skip_thread (FALSE); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); if (ready == -1) { /* diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h index afac049c09b4..5c79075506f6 100644 --- a/mono/metadata/threads-types.h +++ b/mono/metadata/threads-types.h @@ -195,7 +195,7 @@ mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error); void ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error); void ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error); -MonoInternalThread *mono_thread_internal_current (void); +MONO_PROFILER_API MonoInternalThread *mono_thread_internal_current (void); void mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload); void mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread); @@ -225,7 +225,7 @@ gunichar2* mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len MONO_API MonoException* mono_thread_get_undeniable_exception (void); void mono_thread_self_abort (void); -void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error); +MONO_PROFILER_API void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error); void mono_thread_suspend_all_other_threads (void); gboolean mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout); diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 4cc459437e6a..142a48e13e6e 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -3110,6 +3110,18 @@ ip_in_critical_region (MonoDomain *domain, gpointer ip) return mono_gc_is_critical_method (method); } +static void +thread_flags_changing (MonoThreadInfoFlags old, MonoThreadInfoFlags new_) +{ + mono_gc_skip_thread_changing (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC)); +} + +static void +thread_flags_changed (MonoThreadInfoFlags old, MonoThreadInfoFlags new_) +{ + mono_gc_skip_thread_changed (!!(new_ & MONO_THREAD_INFO_FLAGS_NO_GC)); +} + void mono_thread_callbacks_init (void) { @@ -3121,6 +3133,8 @@ mono_thread_callbacks_init (void) cb.thread_detach_with_lock = thread_detach_with_lock; cb.ip_in_critical_region = ip_in_critical_region; cb.thread_in_critical_region = thread_in_critical_region; + cb.thread_flags_changing = thread_flags_changing; + cb.thread_flags_changed = thread_flags_changed; mono_thread_info_callbacks_init (&cb); } diff --git a/mono/mini/mini-posix.c b/mono/mini/mini-posix.c index a15921949ac7..a23912d4bafe 100644 --- a/mono/mini/mini-posix.c +++ b/mono/mini/mini-posix.c @@ -58,6 +58,7 @@ #include #include #include +#include #include "mini.h" #include @@ -606,12 +607,23 @@ clock_sleep_ns_abs (guint64 ns_abs) static int profiler_signal; static volatile gint32 sampling_thread_exiting; +static MonoOSEvent sampling_thread_exited; -static mono_native_thread_return_t -sampling_thread_func (void *data) +static gsize +sampling_thread_func (gpointer unused) { - mono_threads_attach_tools_thread (); - mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler sampler"); + MonoInternalThread *thread = mono_thread_internal_current (); + + thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE; + + ERROR_DECL (error); + + MonoString *name = mono_string_new_checked (mono_get_root_domain (), "Profiler Sampler", error); + mono_error_assert_ok (error); + mono_thread_set_name_internal (thread, name, FALSE, FALSE, error); + mono_error_assert_ok (error); + + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE); int old_policy; struct sched_param old_sched; @@ -663,9 +675,8 @@ sampling_thread_func (void *data) sleep += 1000000000 / freq; - FOREACH_THREAD_SAFE (info) { - /* info should never be this thread as we're a tools thread. */ - g_assert (mono_thread_info_get_tid (info) != mono_native_thread_id_get ()); + FOREACH_THREAD_SAFE_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_SAMPLE) { + g_assert (mono_thread_info_get_tid (info) != sampling_thread); /* * Require an ack for the last sampling signal sent to the thread @@ -687,9 +698,11 @@ sampling_thread_func (void *data) pthread_setschedparam (pthread_self (), old_policy, &old_sched); - mono_thread_info_detach (); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); - return NULL; + mono_os_event_set (&sampling_thread_exited); + + return 0; } void @@ -727,7 +740,8 @@ mono_runtime_shutdown_stat_profiler (void) } #endif - mono_native_thread_join (sampling_thread); + mono_os_event_wait_one (&sampling_thread_exited, MONO_INFINITE_WAIT, FALSE); + mono_os_event_destroy (&sampling_thread_exited); /* * We can't safely remove the signal handler because we have no guarantee @@ -769,8 +783,15 @@ mono_runtime_setup_stat_profiler (void) mono_counters_register ("Sampling signals accepted", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &profiler_signals_accepted); mono_counters_register ("Shutdown signals received", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &profiler_interrupt_signals_received); + mono_os_event_init (&sampling_thread_exited, FALSE); + mono_atomic_store_i32 (&sampling_thread_running, 1); - mono_native_thread_create (&sampling_thread, sampling_thread_func, NULL); + + MonoError error; + MonoInternalThread *thread = mono_thread_create_internal (mono_get_root_domain (), sampling_thread_func, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error); + mono_error_assert_ok (&error); + + sampling_thread = MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid); } #else diff --git a/mono/profiler/log.c b/mono/profiler/log.c index 84fe2240ce28..99ea14b7b418 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -19,7 +19,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -37,6 +40,7 @@ #include #include #include +#include #include "log.h" #ifdef HAVE_DLFCN_H @@ -146,6 +150,9 @@ typedef struct { // Was this thread added to the LLS? gboolean attached; + // Did this thread detach from the runtime? Only used for internal profiler threads. + gboolean did_detach; + // The current log buffer for this thread. LogBuffer *buffer; @@ -317,12 +324,18 @@ struct _MonoProfiler { volatile gint32 buffer_lock_exclusive_intent; volatile gint32 runtime_inited; + volatile gint32 detach_threads; volatile gint32 in_shutdown; + MonoSemType attach_threads_sem; + MonoSemType detach_threads_sem; + MonoNativeThreadId helper_thread; + MonoOSEvent helper_thread_exited; MonoNativeThreadId writer_thread; volatile gint32 run_writer_thread; + MonoOSEvent writer_thread_exited; MonoLockFreeQueue writer_queue; MonoSemType writer_queue_sem; @@ -334,6 +347,7 @@ struct _MonoProfiler { MonoNativeThreadId dumper_thread; volatile gint32 run_dumper_thread; + MonoOSEvent dumper_thread_exited; MonoLockFreeQueue dumper_queue; MonoSemType dumper_queue_sem; @@ -520,6 +534,7 @@ init_thread (gboolean add_to_lls) thread = g_malloc (sizeof (MonoProfilerThread)); thread->node.key = thread_id (); thread->attached = add_to_lls; + thread->did_detach = FALSE; thread->call_depth = 0; thread->busy = FALSE; thread->ended = FALSE; @@ -1088,15 +1103,6 @@ dump_buffer (LogBuffer *buf) free_buffer (buf, buf->size); } -static void -dump_buffer_threadless (LogBuffer *buf) -{ - for (LogBuffer *iter = buf; iter; iter = iter->next) - iter->thread_id = 0; - - dump_buffer (buf); -} - // Only valid if init_thread () was called with add_to_lls = FALSE. static void send_log_unsafe (gboolean if_needed) @@ -1104,10 +1110,6 @@ send_log_unsafe (gboolean if_needed) MonoProfilerThread *thread = get_thread (); if (!if_needed || (if_needed && thread->buffer->next)) { - if (!thread->attached) - for (LogBuffer *iter = thread->buffer; iter; iter = iter->next) - iter->thread_id = 0; - send_buffer (thread); init_buffer_state (thread); } @@ -2084,10 +2086,13 @@ thread_end (MonoProfiler *prof, uintptr_t tid) MonoProfilerThread *thread = get_thread (); - thread->ended = TRUE; - remove_thread (thread); + // Internal profiler threads will clean up manually. + if (thread->attached) { + thread->ended = TRUE; + remove_thread (thread); - PROF_TLS_SET (MONO_PROFILER_THREAD_DEAD); + PROF_TLS_SET (MONO_PROFILER_THREAD_DEAD); + } } static void @@ -2851,6 +2856,15 @@ cleanup_reusable_samples (void) mono_thread_hazardous_try_free (sample, free_sample_hit); } +static void +signal_helper_thread (char c) +{ + if (write (log_profiler.pipes [1], &c, 1) != 1) { + mono_profiler_printf_err ("Could not write to log profiler pipe: %s", g_strerror (errno)); + exit (1); + } +} + static void log_early_shutdown (MonoProfiler *prof) { @@ -2858,6 +2872,23 @@ log_early_shutdown (MonoProfiler *prof) mono_atomic_store_i32 (&log_profiler.heapshot_requested, 1); mono_gc_collect (mono_gc_max_generation ()); } + + /* + * We need to detach the internal threads early on. log_shutdown () is + * called after the threading subsystem has been cleaned up, so detaching + * there would crash. + */ + mono_os_sem_init (&log_profiler.detach_threads_sem, 0); + mono_atomic_store_i32 (&log_profiler.detach_threads, 1); + + signal_helper_thread (2); + mono_os_sem_post (&prof->dumper_queue_sem); + mono_os_sem_post (&prof->writer_queue_sem); + + for (int i = 0; i < 3; i++) + mono_os_sem_wait (&log_profiler.detach_threads_sem, MONO_SEM_FLAGS_NONE); + + mono_os_sem_destroy (&log_profiler.detach_threads_sem); } static void @@ -2868,14 +2899,9 @@ log_shutdown (MonoProfiler *prof) if (ENABLED (PROFLOG_COUNTER_EVENTS)) counters_and_perfcounters_sample (); - char c = 1; - - if (write (prof->pipes [1], &c, 1) != 1) { - mono_profiler_printf_err ("Could not write to log profiler pipe: %s", g_strerror (errno)); - exit (1); - } - - mono_native_thread_join (prof->helper_thread); + signal_helper_thread (1); + mono_os_event_wait_one (&prof->helper_thread_exited, MONO_INFINITE_WAIT, FALSE); + mono_os_event_destroy (&prof->helper_thread_exited); mono_os_mutex_destroy (&log_profiler.counters_mutex); @@ -2914,12 +2940,14 @@ log_shutdown (MonoProfiler *prof) mono_atomic_store_i32 (&prof->run_dumper_thread, 0); mono_os_sem_post (&prof->dumper_queue_sem); - mono_native_thread_join (prof->dumper_thread); + mono_os_event_wait_one (&prof->dumper_thread_exited, MONO_INFINITE_WAIT, FALSE); + mono_os_event_destroy (&prof->dumper_thread_exited); mono_os_sem_destroy (&prof->dumper_queue_sem); mono_atomic_store_i32 (&prof->run_writer_thread, 0); mono_os_sem_post (&prof->writer_queue_sem); - mono_native_thread_join (prof->writer_thread); + mono_os_event_wait_one (&prof->writer_thread_exited, MONO_INFINITE_WAIT, FALSE); + mono_os_event_destroy (&prof->writer_thread_exited); mono_os_sem_destroy (&prof->writer_queue_sem); /* @@ -3021,6 +3049,62 @@ new_filename (const char* filename) return res; } +static MonoProfilerThread * +profiler_thread_begin (const char *name) +{ + MonoProfilerThread *thread = init_thread (FALSE); + + mono_thread_attach (mono_get_root_domain ()); + + MonoInternalThread *internal = mono_thread_internal_current (); + + /* + * Don't let other threads try to suspend internal profiler threads during + * shutdown. This can happen if a program calls Environment.Exit () which + * calls mono_thread_suspend_all_other_threads (). + */ + internal->flags |= MONO_THREAD_FLAG_DONT_MANAGE; + + ERROR_DECL (error); + + MonoString *name_str = mono_string_new_checked (mono_get_root_domain (), name, error); + mono_error_assert_ok (error); + mono_thread_set_name_internal (internal, name_str, FALSE, FALSE, error); + mono_error_assert_ok (error); + + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE); + + mono_os_sem_post (&log_profiler.attach_threads_sem); + + return thread; +} + +static void +profiler_thread_end (MonoProfilerThread *thread, MonoOSEvent *event, gboolean send) +{ + if (send) + send_log_unsafe (FALSE); + else + dump_buffer (thread->buffer); + + deinit_thread (thread); + + mono_os_event_set (event); +} + +static void +profiler_thread_check_detach (MonoProfilerThread *thread) +{ + if (mono_atomic_load_i32 (&log_profiler.detach_threads) && !thread->did_detach) { + thread->did_detach = TRUE; + + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE); + mono_thread_detach (mono_thread_current ()); + + mono_os_sem_post (&log_profiler.detach_threads_sem); + } +} + static void add_to_fd_set (fd_set *set, int fd, int *max_fd) { @@ -3044,10 +3128,7 @@ add_to_fd_set (fd_set *set, int fd, int *max_fd) static void * helper_thread (void *arg) { - mono_threads_attach_tools_thread (); - mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper"); - - MonoProfilerThread *thread = init_thread (FALSE); + MonoProfilerThread *thread = profiler_thread_begin ("Profiler Helper"); GArray *command_sockets = g_array_new (FALSE, FALSE, sizeof (int)); @@ -3083,11 +3164,14 @@ helper_thread (void *arg) buffer_unlock_excl (); - // Are we shutting down? + // Did we get a shutdown or detach signal? if (FD_ISSET (log_profiler.pipes [0], &rfds)) { char c; + read (log_profiler.pipes [0], &c, 1); - break; + + if (c == 1) + break; } for (gint i = 0; i < command_sockets->len; i++) { @@ -3126,6 +3210,8 @@ helper_thread (void *arg) g_array_append_val (command_sockets, fd); } } + + profiler_thread_check_detach (thread); } for (gint i = 0; i < command_sockets->len; i++) @@ -3133,10 +3219,7 @@ helper_thread (void *arg) g_array_free (command_sockets, TRUE); - send_log_unsafe (FALSE); - deinit_thread (thread); - - mono_thread_info_detach (); + profiler_thread_end (thread, &log_profiler.helper_thread_exited, TRUE); return NULL; } @@ -3276,7 +3359,7 @@ handle_writer_queue_entry (void) if (wrote_methods) { MonoProfilerThread *thread = get_thread (); - dump_buffer_threadless (thread->buffer); + dump_buffer (thread->buffer); init_buffer_state (thread); } @@ -3294,25 +3377,21 @@ handle_writer_queue_entry (void) static void * writer_thread (void *arg) { - mono_threads_attach_tools_thread (); - mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer"); - dump_header (); - MonoProfilerThread *thread = init_thread (FALSE); + MonoProfilerThread *thread = profiler_thread_begin ("Profiler Writer"); while (mono_atomic_load_i32 (&log_profiler.run_writer_thread)) { mono_os_sem_wait (&log_profiler.writer_queue_sem, MONO_SEM_FLAGS_NONE); handle_writer_queue_entry (); + + profiler_thread_check_detach (thread); } /* Drain any remaining entries on shutdown. */ while (handle_writer_queue_entry ()); - free_buffer (thread->buffer, thread->buffer->size); - deinit_thread (thread); - - mono_thread_info_detach (); + profiler_thread_end (thread, &log_profiler.writer_thread_exited, FALSE); return NULL; } @@ -3401,10 +3480,7 @@ handle_dumper_queue_entry (void) static void * dumper_thread (void *arg) { - mono_threads_attach_tools_thread (); - mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper"); - - MonoProfilerThread *thread = init_thread (FALSE); + MonoProfilerThread *thread = profiler_thread_begin ("Profiler Dumper"); while (mono_atomic_load_i32 (&log_profiler.run_dumper_thread)) { /* @@ -3415,15 +3491,14 @@ dumper_thread (void *arg) send_log_unsafe (FALSE); handle_dumper_queue_entry (); + + profiler_thread_check_detach (thread); } /* Drain any remaining entries on shutdown. */ while (handle_dumper_queue_entry ()); - send_log_unsafe (FALSE); - deinit_thread (thread); - - mono_thread_info_detach (); + profiler_thread_end (thread, &log_profiler.dumper_thread_exited, TRUE); return NULL; } @@ -3445,6 +3520,11 @@ register_counter (const char *name, gint32 *counter) mono_counters_register (name, MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, counter); } +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#endif + ICALL_EXPORT gint32 proflog_icall_GetMaxStackTraceFrames (void) { @@ -3770,6 +3850,10 @@ proflog_icall_SetJitEvents (MonoBoolean value) mono_coop_mutex_unlock (&log_profiler.api_mutex); } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + static void runtime_initialized (MonoProfiler *profiler) { @@ -3825,15 +3909,27 @@ runtime_initialized (MonoProfiler *profiler) counters_init (); + mono_os_sem_init (&log_profiler.attach_threads_sem, 0); + /* * We must start the helper thread before the writer thread. This is - * because the helper thread sets up the command port which is written to - * the log header by the writer thread. + * because start_helper_thread () sets up the command port which is written + * to the log header by the writer thread. */ start_helper_thread (); start_writer_thread (); start_dumper_thread (); + /* + * Wait for all the internal threads to be started. If we don't do this, we + * might shut down before they finish initializing, which could lead to + * various deadlocks when waiting for them to exit during shutdown. + */ + for (int i = 0; i < 3; i++) + mono_os_sem_wait (&log_profiler.attach_threads_sem, MONO_SEM_FLAGS_NONE); + + mono_os_sem_destroy (&log_profiler.attach_threads_sem); + mono_coop_mutex_init (&log_profiler.api_mutex); #define ADD_ICALL(NAME) \ @@ -3934,9 +4030,13 @@ create_profiler (const char *args, const char *filename, GPtrArray *filters) mono_lock_free_allocator_init_size_class (&log_profiler.writer_entry_size_class, sizeof (WriterQueueEntry), WRITER_ENTRY_BLOCK_SIZE); mono_lock_free_allocator_init_allocator (&log_profiler.writer_entry_allocator, &log_profiler.writer_entry_size_class, MONO_MEM_ACCOUNT_PROFILER); + mono_os_event_init (&log_profiler.helper_thread_exited, FALSE); + + mono_os_event_init (&log_profiler.writer_thread_exited, FALSE); mono_lock_free_queue_init (&log_profiler.writer_queue); mono_os_sem_init (&log_profiler.writer_queue_sem, 0); + mono_os_event_init (&log_profiler.dumper_thread_exited, FALSE); mono_lock_free_queue_init (&log_profiler.dumper_queue); mono_os_sem_init (&log_profiler.dumper_queue_sem, 0); diff --git a/mono/sgen/sgen-alloc.c b/mono/sgen/sgen-alloc.c index 0e6278fea23e..8c9d202fa1ce 100644 --- a/mono/sgen/sgen-alloc.c +++ b/mono/sgen/sgen-alloc.c @@ -477,7 +477,7 @@ sgen_alloc_obj_mature (GCVTable vtable, size_t size) void sgen_clear_tlabs (void) { - FOREACH_THREAD (info) { + FOREACH_THREAD_ALL (info) { /* A new TLAB will be allocated when the thread does its first allocation */ info->tlab_start = NULL; info->tlab_next = NULL; diff --git a/mono/sgen/sgen-debug.c b/mono/sgen/sgen-debug.c index f8d3aaa05011..bb32843f8b90 100644 --- a/mono/sgen/sgen-debug.c +++ b/mono/sgen/sgen-debug.c @@ -501,10 +501,10 @@ find_pinning_ref_from_thread (char *obj, size_t size) #ifndef SGEN_WITHOUT_MONO char *endobj = obj + size; - FOREACH_THREAD (info) { + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { mword *ctxstart, *ctxcurrent, *ctxend; char **start = (char**)info->client_info.stack_start; - if (info->client_info.skip || info->client_info.gc_disabled) + if (info->client_info.skip) continue; while (start < (char**)info->client_info.info.stack_end) { if (*start >= obj && *start < endobj) diff --git a/mono/utils/mono-linked-list-set.h b/mono/utils/mono-linked-list-set.h index ca441ffc487a..890f0cad8dca 100644 --- a/mono/utils/mono-linked-list-set.h +++ b/mono/utils/mono-linked-list-set.h @@ -61,7 +61,7 @@ MONO_API gpointer mono_lls_get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index); static inline gboolean -mono_lls_filter_accept_all (gpointer elem) +mono_lls_filter_accept_all (gpointer elem, gpointer dummy) { return TRUE; } @@ -70,13 +70,13 @@ mono_lls_filter_accept_all (gpointer elem) * These macros assume that no other threads are actively modifying the list. */ -#define MONO_LLS_FOREACH_FILTERED(list, type, elem, filter) \ +#define MONO_LLS_FOREACH_FILTERED(list, type, elem, filter, ...) \ do { \ MonoLinkedListSet *list__ = (list); \ for (MonoLinkedListSetNode *cur__ = list__->head; cur__; cur__ = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (cur__->next)) { \ if (!mono_lls_pointer_get_mark (cur__->next)) { \ type *elem = (type *) cur__; \ - if (filter (elem)) { + if (filter (elem, __VA_ARGS__)) { #define MONO_LLS_FOREACH_END \ } \ @@ -85,18 +85,18 @@ mono_lls_filter_accept_all (gpointer elem) } while (0); #define MONO_LLS_FOREACH(list, type, elem) \ - MONO_LLS_FOREACH_FILTERED ((list), type, elem, mono_lls_filter_accept_all) + MONO_LLS_FOREACH_FILTERED ((list), type, elem, mono_lls_filter_accept_all, NULL) /* * These macros can be used while other threads are potentially modifying the * list, but they only provide a snapshot of the list as a result. * * NOTE: Do NOT break out of the loop through any other means than a break - * statement, as other ways of breaking the loop will skip past important - * cleanup work. + * statement, as other ways of breaking the loop (return, goto, etc) will skip + * past important cleanup work. */ -#define MONO_LLS_FOREACH_FILTERED_SAFE(list, type, elem, filter) \ +#define MONO_LLS_FOREACH_FILTERED_SAFE(list, type, elem, filter, ...) \ do { \ /* NOTE: Keep this macro's code in sync with the mono_lls_find () logic. */ \ MonoLinkedListSet *list__ = (list); \ @@ -125,7 +125,7 @@ mono_lls_filter_accept_all (gpointer elem) progress__ = TRUE; \ hkey__ = ckey__; \ type *elem = (type *) cur__; \ - if (filter (elem)) { \ + if (filter (elem, __VA_ARGS__)) { \ gboolean broke__ = TRUE; \ gboolean done__ = FALSE; \ do { \ @@ -169,6 +169,6 @@ mono_lls_filter_accept_all (gpointer elem) } while (0); #define MONO_LLS_FOREACH_SAFE(list, type, elem) \ - MONO_LLS_FOREACH_FILTERED_SAFE ((list), type, elem, mono_lls_filter_accept_all) + MONO_LLS_FOREACH_FILTERED_SAFE ((list), type, elem, mono_lls_filter_accept_all, NULL) #endif /* __MONO_SPLIT_ORDERED_LIST_H__ */ diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index 3f21b370236f..2ab8071f5799 100644 --- a/mono/utils/mono-threads.c +++ b/mono/utils/mono-threads.c @@ -217,7 +217,7 @@ dump_threads (void) MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n"); MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n"); - FOREACH_THREAD_SAFE (info) { + FOREACH_THREAD_SAFE_ALL (info) { #ifdef TARGET_MACH char thread_name [256] = { 0 }; pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255); @@ -597,37 +597,7 @@ mono_thread_info_list_head (void) return &thread_list; } -/** - * mono_threads_attach_tools_thread - * - * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS. - * - * A tools thread is a very special kind of thread that needs access to core runtime facilities but should - * not be counted as a regular thread for high order facilities such as executing managed code or accessing - * the managed heap. - * - * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when - * doing things like resolving backtraces in their background processing thread. - */ -void -mono_threads_attach_tools_thread (void) -{ - MonoThreadInfo *info; - - /* Must only be called once */ - g_assert (!mono_native_tls_get_value (thread_info_key)); - - while (!mono_threads_inited) { - mono_thread_info_usleep (10); - } - - info = mono_thread_info_attach (); - g_assert (info); - - info->tools_thread = TRUE; -} - -MonoThreadInfo* +MonoThreadInfo * mono_thread_info_attach (void) { MonoThreadInfo *info; @@ -738,6 +708,27 @@ thread_info_key_dtor (void *arg) } #endif +MonoThreadInfoFlags +mono_thread_info_get_flags (MonoThreadInfo *info) +{ + return mono_atomic_load_i32 (&info->flags); +} + +void +mono_thread_info_set_flags (MonoThreadInfoFlags flags) +{ + MonoThreadInfo *info = mono_thread_info_current (); + MonoThreadInfoFlags old = mono_atomic_load_i32 (&info->flags); + + if (threads_callbacks.thread_flags_changing) + threads_callbacks.thread_flags_changing (old, flags); + + mono_atomic_store_i32 (&info->flags, flags); + + if (threads_callbacks.thread_flags_changed) + threads_callbacks.thread_flags_changed (old, flags); +} + void mono_thread_info_init (size_t info_size) { diff --git a/mono/utils/mono-threads.h b/mono/utils/mono-threads.h index ade9380d5123..178ede51a1d8 100644 --- a/mono/utils/mono-threads.h +++ b/mono/utils/mono-threads.h @@ -148,18 +148,43 @@ enum { typedef struct _MonoThreadInfoInterruptToken MonoThreadInfoInterruptToken; +/* + * These flags control how the rest of the runtime will see and interact with + * a thread. + */ +typedef enum { + /* + * No flags means it's a normal thread that takes part in all runtime + * functionality. + */ + MONO_THREAD_INFO_FLAGS_NONE = 0, + /* + * The thread will not be suspended by the STW machinery. The thread is not + * allowed to allocate or access managed memory at all, nor execute managed + * code. + */ + MONO_THREAD_INFO_FLAGS_NO_GC = 1, + /* + * The thread will not be subject to profiler sampling signals. + */ + MONO_THREAD_INFO_FLAGS_NO_SAMPLE = 2, +} MonoThreadInfoFlags; + typedef struct { MonoLinkedListSetNode node; guint32 small_id; /*Used by hazard pointers */ MonoNativeThreadHandle native_handle; /* Valid on mach and android */ int thread_state; + /* + * Must not be changed directly, and especially not by other threads. Use + * mono_thread_info_get/set_flags () to manipulate this. + */ + volatile gint32 flags; + /*Tells if this thread was created by the runtime or not.*/ gboolean runtime_thread; - /* Tells if this thread should be ignored or not by runtime services such as GC and profiling */ - gboolean tools_thread; - /* Max stack bounds, all valid addresses must be between [stack_start_limit, stack_end[ */ void *stack_start_limit, *stack_end; @@ -255,6 +280,10 @@ typedef struct { void (*thread_detach_with_lock)(THREAD_INFO_TYPE *info); gboolean (*ip_in_critical_region) (MonoDomain *domain, gpointer ip); gboolean (*thread_in_critical_region) (THREAD_INFO_TYPE *info); + + // Called on the affected thread. + void (*thread_flags_changing) (MonoThreadInfoFlags old, MonoThreadInfoFlags new_); + void (*thread_flags_changed) (MonoThreadInfoFlags old, MonoThreadInfoFlags new_); } MonoThreadInfoCallbacks; typedef struct { @@ -272,26 +301,41 @@ typedef enum { typedef SuspendThreadResult (*MonoSuspendThreadCallback) (THREAD_INFO_TYPE *info, gpointer user_data); +MONO_API MonoThreadInfoFlags +mono_thread_info_get_flags (THREAD_INFO_TYPE *info); + +/* + * Sets the thread info flags for the current thread. This function may invoke + * callbacks containing arbitrary code (e.g. locks) so it must be assumed to be + * async unsafe. + */ +MONO_API void +mono_thread_info_set_flags (MonoThreadInfoFlags flags); + static inline gboolean -mono_threads_filter_tools_threads (THREAD_INFO_TYPE *info) +mono_threads_filter_exclude_flags (THREAD_INFO_TYPE *info, MonoThreadInfoFlags flags) { - return !((MonoThreadInfo*)info)->tools_thread; + return !(mono_thread_info_get_flags (info) & flags); } -/* -Requires the world to be stoped -*/ -#define FOREACH_THREAD(thread) \ - MONO_LLS_FOREACH_FILTERED (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_threads_filter_tools_threads) +/* Normal iteration; requires the world to be stopped. */ + +#define FOREACH_THREAD_ALL(thread) \ + MONO_LLS_FOREACH_FILTERED (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_lls_filter_accept_all, NULL) + +#define FOREACH_THREAD_EXCLUDE(thread, not_flags) \ + MONO_LLS_FOREACH_FILTERED (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_threads_filter_exclude_flags, not_flags) #define FOREACH_THREAD_END \ MONO_LLS_FOREACH_END -/* -Snapshot iteration. -*/ -#define FOREACH_THREAD_SAFE(thread) \ - MONO_LLS_FOREACH_FILTERED_SAFE (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_threads_filter_tools_threads) +/* Snapshot iteration; can be done anytime but is slower. */ + +#define FOREACH_THREAD_SAFE_ALL(thread) \ + MONO_LLS_FOREACH_FILTERED_SAFE (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_lls_filter_accept_all, NULL) + +#define FOREACH_THREAD_SAFE_EXCLUDE(thread, not_flags) \ + MONO_LLS_FOREACH_FILTERED_SAFE (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_threads_filter_exclude_flags, not_flags) #define FOREACH_THREAD_SAFE_END \ MONO_LLS_FOREACH_SAFE_END @@ -444,10 +488,6 @@ mono_threads_open_thread_handle (MonoThreadHandle *handle); void mono_threads_close_thread_handle (MonoThreadHandle *handle); -MONO_API void -mono_threads_attach_tools_thread (void); - - #if !defined(HOST_WIN32) /*Use this instead of pthread_kill */ diff --git a/mono/utils/os-event.h b/mono/utils/os-event.h index 9ab5524f0a57..16a421aa6216 100644 --- a/mono/utils/os-event.h +++ b/mono/utils/os-event.h @@ -8,6 +8,7 @@ #include #include +#include #include "mono-os-mutex.h" #ifndef MONO_INFINITE_WAIT @@ -35,22 +36,22 @@ struct _MonoOSEvent { #endif }; -void +MONO_API void mono_os_event_init (MonoOSEvent *event, gboolean initial); -void +MONO_API void mono_os_event_destroy (MonoOSEvent *event); -void +MONO_API void mono_os_event_set (MonoOSEvent *event); -void +MONO_API void mono_os_event_reset (MonoOSEvent *event); -MonoOSEventWaitRet +MONO_API MonoOSEventWaitRet mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout, gboolean alertable); -MonoOSEventWaitRet +MONO_API MonoOSEventWaitRet mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout, gboolean alertable); #endif /* _MONO_UTILS_OS_EVENT_H_ */