#include <mono/utils/mono-lazy-init.h>
#include <mono/utils/mono-coop-mutex.h>
#include <mono/utils/mono-coop-semaphore.h>
+#include <mono/utils/mono-threads-coop.h>
#include <errno.h>
The GC has to acquire this lock before starting a STW to make sure
a runtime suspend won't make it wronly see a thread in a safepoint
when it is in fact not.
+
+This has to be a naked locking primitive, and not a coop aware one, as
+it needs to be usable when destroying thread_info_key, the TLS key for
+the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
+(which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
+to an assertion error. We then simply switch state manually in
+mono_thread_info_suspend_lock_with_info.
*/
-static MonoCoopSem global_suspend_semaphore;
+static MonoSemType global_suspend_semaphore;
static size_t thread_info_size;
static MonoThreadInfoCallbacks threads_callbacks;
/*abort at 1 sec*/
#define SLEEP_DURATION_BEFORE_ABORT 200
+static long sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
+ sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
+
static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
void
for (i = 0; i < pending_suspends; ++i) {
THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
InterlockedIncrement (&waits_done);
- if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE))
+ if (!mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE))
continue;
mono_stopwatch_stop (&suspension_time);
dump_threads ();
MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
- g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), SLEEP_DURATION_BEFORE_ABORT);
+ g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
}
mono_stopwatch_stop (&suspension_time);
THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
return info;
}
+static void
+mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
+
static void
unregister_thread (void *arg)
{
- MonoThreadInfo *info = (MonoThreadInfo *) arg;
- int small_id = info->small_id;
+ gpointer gc_unsafe_stackdata;
+ MonoThreadInfo *info;
+ int small_id;
+
+ info = (MonoThreadInfo *) arg;
g_assert (info);
+ small_id = info->small_id;
+
+ /* We only enter the GC unsafe region, as when exiting this function, the thread
+ * will be detached, and the current MonoThreadInfo* will be destroyed. */
+ mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
+
THREADS_DEBUG ("unregistering info %p\n", info);
mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
if (threads_callbacks.thread_detach)
threads_callbacks.thread_detach (info);
- mono_thread_info_suspend_lock ();
+ mono_thread_info_suspend_lock_with_info (info);
/*
Now perform the callback that must be done under locks.
gboolean res;
threads_callbacks = *callbacks;
thread_info_size = info_size;
+ char *sleepLimit;
#ifdef HOST_WIN32
res = mono_native_tls_alloc (&thread_info_key, NULL);
res = mono_native_tls_alloc (&thread_exited_key, NULL);
g_assert (res);
unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
+
+ if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
+ long threshold = strtol(sleepLimit, NULL, 10);
+ if ((errno == 0) && (threshold >= 40)) {
+ sleepAbortDuration = threshold;
+ sleepWarnDuration = threshold / 20;
+ } else
+ g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
+ }
- mono_coop_sem_init (&global_suspend_semaphore, 1);
+ mono_os_sem_init (&global_suspend_semaphore, 1);
mono_os_sem_init (&suspend_semaphore, 0);
mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
A GC that has safepoints must take this lock as part of its
STW to make sure no unsafe pending suspend is in progress.
*/
+
+static void
+mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
+{
+ g_assert (info);
+
+ MONO_ENTER_GC_SAFE_WITH_INFO(info);
+
+ int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
+ g_assert (res != -1);
+
+ MONO_EXIT_GC_SAFE_WITH_INFO;
+}
+
void
mono_thread_info_suspend_lock (void)
{
- int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
- g_assert (res != -1);
+ mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
}
void
mono_thread_info_suspend_unlock (void)
{
- mono_coop_sem_post (&global_suspend_semaphore);
+ mono_os_sem_post (&global_suspend_semaphore);
}
/*
else
g_string_append_printf (text, "waiting");
}
+
+gboolean
+mono_thread_info_is_current (MonoThreadInfo *info)
+{
+ return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
+}