{
struct sigaction sinfo;
- if (mono_thread_info_unified_management_enabled ())
- return;
-
suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
MONO_SEM_INIT (&suspend_ack_semaphore, 0);
#include "utils/mono-time.h"
#include "utils/dtrace.h"
#include "utils/mono-counters.h"
-#include "utils/mono-threads.h"
#define TV_DECLARE SGEN_TV_DECLARE
#define TV_GETTIME SGEN_TV_GETTIME
#define TV_ELAPSED SGEN_TV_ELAPSED
-static int sgen_unified_suspend_restart_world (void);
-static int sgen_unified_suspend_stop_world (void);
-
inline static void*
align_pointer (void *ptr)
{
sgen_global_stop_count++;
SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ());
TV_GETTIME (stop_world_time);
-
- if (mono_thread_info_unified_management_enabled ()) {
- count = sgen_unified_suspend_stop_world ();
- } else {
- count = sgen_thread_handshake (FALSE);
- dead = restart_threads_until_none_in_managed_allocator ();
- if (count < dead)
- g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
- count -= dead;
- }
+ count = sgen_thread_handshake (TRUE);
+ dead = restart_threads_until_none_in_managed_allocator ();
+ if (count < dead)
+ g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
+ count -= dead;
SGEN_LOG (3, "world stopped %d thread(s)", count);
mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
} END_FOREACH_THREAD
TV_GETTIME (start_handshake);
-
- if (mono_thread_info_unified_management_enabled ())
- count = sgen_unified_suspend_restart_world ();
- else
- count = sgen_thread_handshake (FALSE);
-
-
+ count = sgen_thread_handshake (FALSE);
TV_GETTIME (end_sw);
time_restart_world += TV_ELAPSED (start_handshake, end_sw);
usec = TV_ELAPSED (stop_world_time, end_sw);
mono_counters_register ("World restart", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_restart_world);
}
-/* Unified suspend code */
-
-static gboolean
-sgen_is_thread_in_current_stw (SgenThreadInfo *info)
-{
- /*
- 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->gc_disabled) {
- return FALSE;
- }
-
- /*
- We have detected that this thread is failing/dying, ignore it.
- FIXME: can't we merge this with thread_is_dying?
- */
- if (info->skip) {
- return FALSE;
- }
-
- /*
- Suspending the current thread will deadlock us, bad idea.
- */
- if (info == mono_thread_info_current ()) {
- return FALSE;
- }
-
- /*
- We can't suspend the workers that will do all the heavy lifting.
- FIXME Use some state bit in SgenThreadInfo for this.
- */
- if (sgen_is_worker_thread (mono_thread_info_get_tid (info))) {
- return FALSE;
- }
-
- /*
- The thread has signaled that it started to detach, ignore it.
- FIXME: can't we merge this with skip
- */
- if (!mono_thread_info_is_live (info)) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-update_sgen_info (SgenThreadInfo *info)
-{
- char *stack_start;
-
- /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */
- info->stopped_domain = mono_thread_info_tls_get (info, TLS_KEY_DOMAIN);
- info->stopped_ip = (gpointer) MONO_CONTEXT_GET_IP (&info->info.suspend_state.ctx);
- stack_start = (char*)MONO_CONTEXT_GET_SP (&info->info.suspend_state.ctx) - REDZONE_SIZE;
-
- /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */
- if (stack_start < (char*)info->stack_start_limit || stack_start >= (char*)info->stack_end)
- g_error ("BAD STACK");
-
- info->stack_start = stack_start;
- info->ctx = info->info.suspend_state.ctx;
-}
-
-static int
-sgen_unified_suspend_stop_world (void)
-{
- int restart_counter;
- SgenThreadInfo *info;
- int count = 0;
- int sleep_duration = -1;
-
- mono_threads_begin_global_suspend ();
- THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n");
-
- FOREACH_THREAD_SAFE (info) {
- info->skip = FALSE;
- info->suspend_done = FALSE;
- if (sgen_is_thread_in_current_stw (info)) {
- info->skip = !mono_thread_info_begin_suspend (info, FALSE);
- THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %d\n", info, info->skip);
- if (!info->skip)
- ++count;
- } else {
- THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d\n", info, info->skip);
- }
- } END_FOREACH_THREAD_SAFE
-
- mono_thread_info_current ()->suspend_done = TRUE;
- mono_threads_wait_pending_operations ();
-
- for (;;) {
- restart_counter = 0;
- FOREACH_THREAD_SAFE (info) {
- if (info->suspend_done || !sgen_is_thread_in_current_stw (info)) {
- THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE thread %p not been processed done %d current %d\n", info, info->suspend_done, !sgen_is_thread_in_current_stw (info));
- continue;
- }
-
- /*
- All threads that reach here are pristine suspended. This means the following:
-
- - We haven't accepted the previous suspend as good.
- - We haven't gave up on it for this STW (it's either bad or asked not to)
- */
- if (!mono_threads_core_check_suspend_result (info)) {
- THREADS_STW_DEBUG ("[GC-STW-RESTART] SKIP thread %p failed to finish to suspend\n", info);
- info->skip = TRUE;
- } else if (mono_thread_info_in_critical_location (info)) {
- gboolean res;
- g_assert (mono_thread_info_suspend_count (info) == 1);
- res = mono_thread_info_begin_resume (info);
- THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %d\n", info, res);
- if (res)
- ++restart_counter;
- else
- info->skip = TRUE;
- } else {
- THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", info);
- g_assert (!info->in_critical_region);
- info->suspend_done = TRUE;
- }
- } END_FOREACH_THREAD_SAFE
-
- if (restart_counter == 0)
- break;
- mono_threads_wait_pending_operations ();
-
- if (sleep_duration < 0) {
-#ifdef HOST_WIN32
- SwitchToThread ();
-#else
- sched_yield ();
-#endif
- sleep_duration = 0;
- } else {
- g_usleep (sleep_duration);
- sleep_duration += 10;
- }
-
- FOREACH_THREAD_SAFE (info) {
- if (sgen_is_thread_in_current_stw (info) && mono_thread_info_is_running (info)) {
- gboolean res = mono_thread_info_begin_suspend (info, FALSE);
- THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", info, res);
- if (!res)
- info->skip = TRUE;
- }
- } END_FOREACH_THREAD_SAFE
-
- mono_threads_wait_pending_operations ();
- }
-
- FOREACH_THREAD_SAFE (info) {
- if (sgen_is_thread_in_current_stw (info)) {
- THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", info);
- g_assert (info->suspend_done);
- update_sgen_info (info);
- } else {
- g_assert (!info->suspend_done);
- }
- } END_FOREACH_THREAD_SAFE
-
- return count;
-}
-
-static int
-sgen_unified_suspend_restart_world (void)
-{
- SgenThreadInfo *info;
- int count = 0;
-
- THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n");
- FOREACH_THREAD_SAFE (info) {
- if (sgen_is_thread_in_current_stw (info)) {
- g_assert (mono_thread_info_begin_resume (info));
- THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", info);
- ++count;
- } else {
- THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p\n", info);
- }
- } END_FOREACH_THREAD_SAFE
-
- mono_threads_wait_pending_operations ();
- mono_threads_end_global_suspend ();
- return count;
-}
#endif
LOCK_THREAD (thread);
if (thread == mono_thread_internal_current ()) {
- mono_thread_info_begin_self_suspend ();
transition_to_suspended (thread, NULL);
- mono_thread_info_end_self_suspend ();
+ mono_thread_info_self_suspend ();
} else {
MonoThreadInfo *info;
MonoJitInfo *ji;
return;
}
- mono_thread_info_begin_self_suspend ();
transition_to_suspended (thread, NULL);
- mono_thread_info_end_self_suspend ();
+ mono_thread_info_self_suspend ();
}
/*This is called with @thread synch_cs held and it must release it*/
mono-linked-list-set.c \
mono-linked-list-set.h \
mono-threads.c \
- mono-threads-state-machine.c \
mono-threads-posix.c \
mono-threads-mach.c \
mono-threads-mach-helper.c \
#include "config.h"
+#if defined(__MACH__)
+
/* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
-#if defined (__MACH__)
#define _DARWIN_C_SOURCE 1
-#endif
-
-#include <mono/utils/mono-threads.h>
-#include <mono/utils/mono-mmap.h>
-
-#if defined (USE_MACH_BACKEND)
#include <mono/utils/mach-support.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-semaphore.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/hazard-pointer.h>
+#include <mono/utils/mono-mmap.h>
void
mono_threads_init_platform (void)
mono_threads_init_dead_letter ();
}
+void
+mono_threads_core_interrupt (MonoThreadInfo *info)
+{
+ thread_abort (info->native_handle);
+}
+
void
mono_threads_core_abort_syscall (MonoThreadInfo *info)
{
}
gboolean
-mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
+mono_threads_core_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
kern_return_t ret;
gboolean res;
g_assert (info);
ret = thread_suspend (info->native_handle);
- THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)info->native_handle, ret);
if (ret != KERN_SUCCESS)
return FALSE;
-
- /* We're in the middle of a self-suspend, resume and register */
- if (!mono_threads_transition_finish_async_suspend (info)) {
- mono_threads_add_to_pending_operation_set (info);
- g_assert (thread_resume (info->native_handle) == KERN_SUCCESS);
- THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)info->native_handle, 0);
- //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
- return TRUE;
- }
res = mono_threads_get_runtime_callbacks ()->
thread_state_init_from_handle (&info->suspend_state, info);
- THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)info->native_handle, res);
- if (res) {
- if (interrupt_kernel)
- thread_abort (info->native_handle);
- } else {
- mono_threads_transition_async_suspend_compensation (info);
- g_assert (thread_resume (info->native_handle) == KERN_SUCCESS);
- THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
- }
+ if (!res)
+ thread_resume (info->native_handle);
return res;
}
gboolean
-mono_threads_core_check_suspend_result (MonoThreadInfo *info)
-{
- return TRUE;
-}
-
-gboolean
-mono_threads_core_begin_async_resume (MonoThreadInfo *info)
+mono_threads_core_resume (MonoThreadInfo *info)
{
kern_return_t ret;
return FALSE;
}
- ret = thread_resume (info->native_handle);
- THREADS_SUSPEND_DEBUG ("RESUME %p -> %d\n", (void*)info->native_handle, ret);
+ ret = thread_resume (info->native_handle);
return ret == KERN_SUCCESS;
}
{
/* pthread_setnmae_np() on Mac is not documented and doesn't receive thread id. */
}
-#endif /* USE_MACH_BACKEND */
-#ifdef __MACH__
void
mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
{
#include <config.h>
-/* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
-#if defined (__MACH__)
-#define _DARWIN_C_SOURCE 1
-#endif
-
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-semaphore.h>
#include <mono/utils/mono-threads.h>
HANDLE handle;
} StartInfo;
+#ifdef PLATFORM_ANDROID
+static int no_interrupt_signo;
+#endif
+
static void*
inner_start_thread (void *arg)
{
}
-#if defined (USE_POSIX_BACKEND)
-
-static int suspend_signal_num;
-static int restart_signal_num;
-static int abort_signal_num;
-static sigset_t suspend_signal_mask;
-static sigset_t suspend_ack_signal_mask;
-
-
-#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#define DEFAULT_SUSPEND_SIGNAL SIGXFSZ
-#else
-#define DEFAULT_SUSPEND_SIGNAL SIGPWR
-#endif
-#define DEFAULT_RESTART_SIGNAL SIGXCPU
-#define DEFAULT_ABORT_SIGNAL SIGWINCH
-
-static int
-mono_thread_search_alt_signal (int min_signal)
-{
-#if !defined (SIGRTMIN)
- g_error ("signal search only works with RTMIN");
-#else
- static int abort_signum = -1;
- int i;
- if (abort_signum != -1)
- return abort_signum;
- /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
- for (i = MAX (min_signal, SIGRTMIN) + 1; i < SIGRTMAX; ++i) {
- struct sigaction sinfo;
- sigaction (i, NULL, &sinfo);
- if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
- abort_signum = i;
- return i;
- }
- }
- g_error ("Could not find an available signal");
-#endif
-}
-
-static int
-mono_thread_get_alt_suspend_signal (void)
-{
-#if defined(PLATFORM_ANDROID)
- return SIGUNUSED;
-#elif !defined (SIGRTMIN)
-#ifdef SIGUSR1
- return SIGUSR1;
-#else
- return -1;
-#endif /* SIGUSR1 */
-#else
- static int suspend_signum = -1;
- int i;
- if (suspend_signum == -1)
- suspend_signum = mono_thread_search_alt_signal (-1);
- return suspend_signum;
-#endif /* SIGRTMIN */
-}
-
-static int
-mono_thread_get_alt_resume_signal (void)
-{
-#if defined(PLATFORM_ANDROID)
- return SIGTTOU;
-#elif !defined (SIGRTMIN)
-#ifdef SIGUSR2
- return SIGUSR2;
-#else
- return -1;
-#endif /* SIGUSR1 */
-#else
- static int resume_signum = -1;
- int i;
- if (resume_signum == -1)
- resume_signum = mono_thread_search_alt_signal (mono_thread_get_alt_suspend_signal ());
- return resume_signum;
-#endif /* SIGRTMIN */
-}
-
+#if !defined (__MACH__)
#if !defined(__native_client__)
-static void
-restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
-{
- MonoThreadInfo *info;
- int old_errno = errno;
-
- info = mono_thread_info_current ();
- info->signal = restart_signal_num;
- errno = old_errno;
-}
-
static void
suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
{
- int old_errno = errno;
- int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
-
-
MonoThreadInfo *current = mono_thread_info_current ();
gboolean ret;
-
- THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current, (void*)current->native_handle);
+
if (current->syscall_break_signal) {
current->syscall_break_signal = FALSE;
- THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current);
- goto done;
- }
-
- /* Have we raced with self suspend? */
- if (!mono_threads_transition_finish_async_suspend (current)) {
- current->suspend_can_continue = TRUE;
- THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current);
- goto done;
+ return;
}
ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->suspend_state, context);
/* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
current->suspend_can_continue = ret;
-
- /*
- Block the restart signal.
- We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
- which might miss the signal and get stuck.
- */
- pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
-
- /* We're done suspending */
- mono_threads_notify_initiator_of_suspend (current);
+ MONO_SEM_POST (¤t->begin_suspend_semaphore);
/* This thread is doomed, all we can do is give up and let the suspender recover. */
- if (!ret) {
- THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current);
- mono_threads_transition_async_suspend_compensation (current);
- /* Unblock the restart signal. */
- pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
+ if (!ret)
+ return;
- goto done;
+ while (MONO_SEM_WAIT (¤t->resume_semaphore) != 0) {
+ /*if (EINTR != errno) ABORT("sem_wait failed"); */
}
- do {
- current->signal = 0;
- sigsuspend (&suspend_signal_mask);
- } while (current->signal != restart_signal_num);
-
- /* Unblock the restart signal. */
- pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
-
if (current->async_target) {
#if MONO_ARCH_HAS_MONO_CONTEXT
MonoContext tmp = current->suspend_state.ctx;
#endif
}
- /* We're done resuming */
- mono_threads_notify_initiator_of_resume (current);
-
-done:
- mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
- errno = old_errno;
-}
-
-static void
-abort_signal_handler (int _dummy, siginfo_t *info, void *context)
-{
- suspend_signal_handler (_dummy, info, context);
+ MONO_SEM_POST (¤t->finish_resume_semaphore);
}
-
#endif
static void
int ret;
sa.sa_sigaction = handler;
- sigfillset (&sa.sa_mask);
-
+ sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | flags;
ret = sigaction (signo, &sa, &previous_sa);
void
mono_threads_init_platform (void)
{
- sigset_t signal_set;
-
- abort_signal_num = DEFAULT_ABORT_SIGNAL;
- if (mono_thread_info_unified_management_enabled ()) {
- suspend_signal_num = DEFAULT_SUSPEND_SIGNAL;
- restart_signal_num = DEFAULT_RESTART_SIGNAL;
- } else {
- suspend_signal_num = mono_thread_get_alt_suspend_signal ();
- restart_signal_num = mono_thread_get_alt_resume_signal ();
- }
-
- sigfillset (&suspend_signal_mask);
- sigdelset (&suspend_signal_mask, restart_signal_num);
+#if !defined(__native_client__)
+ int abort_signo;
- sigemptyset (&suspend_ack_signal_mask);
- sigaddset (&suspend_ack_signal_mask, restart_signal_num);
+ /*
+ FIXME we should use all macros from mini to make this more portable
+ FIXME it would be very sweet if sgen could end up using this too.
+ */
+ if (!mono_thread_info_new_interrupt_enabled ())
+ return;
+ abort_signo = mono_thread_get_abort_signal ();
+ mono_posix_add_signal_handler (abort_signo, suspend_signal_handler, 0);
- mono_posix_add_signal_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
- mono_posix_add_signal_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
- mono_posix_add_signal_handler (abort_signal_num, abort_signal_handler, 0);
+#ifdef PLATFORM_ANDROID
+ /*
+ * Lots of android native code can't handle the EINTR caused by
+ * the normal abort signal, so use a different signal for the
+ * no interruption case, which is used by sdb.
+ * FIXME: Use this on all platforms.
+ * SIGUSR1 is used by dalvik/art.
+ */
+ no_interrupt_signo = SIGWINCH;
+ g_assert (abort_signo != no_interrupt_signo);
+ mono_posix_add_signal_handler (no_interrupt_signo, suspend_signal_handler, SA_RESTART);
+#endif
+#endif
+}
- /* ensure all the new signals are unblocked */
- sigemptyset (&signal_set);
- sigaddset (&signal_set, suspend_signal_num);
- sigaddset (&signal_set, restart_signal_num);
- sigaddset (&signal_set, abort_signal_num);
- sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
+void
+mono_threads_core_interrupt (MonoThreadInfo *info)
+{
+ /* Handled in mono_threads_core_suspend () */
}
void
This signal should not be interpreted as a suspend request.
*/
info->syscall_break_signal = TRUE;
- mono_threads_pthread_kill (info, abort_signal_num);
+ mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
}
gboolean
}
gboolean
-mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
+mono_threads_core_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
- int sig = interrupt_kernel ? abort_signal_num : suspend_signal_num;
-
- if (!mono_threads_pthread_kill (info, sig)) {
- mono_threads_add_to_pending_operation_set (info);
- return TRUE;
+ /*FIXME, check return value*/
+#ifdef PLATFORM_ANDROID
+ if (!interrupt_kernel)
+ mono_threads_pthread_kill (info, no_interrupt_signo);
+ else
+ mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
+#else
+ mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
+#endif
+ while (MONO_SEM_WAIT (&info->begin_suspend_semaphore) != 0) {
+ /* g_assert (errno == EINTR); */
}
- return FALSE;
-}
-
-gboolean
-mono_threads_core_check_suspend_result (MonoThreadInfo *info)
-{
return info->suspend_can_continue;
}
-/*
-This begins async resume. This function must do the following:
-
-- Install an async target if one was requested.
-- Notify the target to resume.
-*/
gboolean
-mono_threads_core_begin_async_resume (MonoThreadInfo *info)
+mono_threads_core_resume (MonoThreadInfo *info)
{
- mono_threads_add_to_pending_operation_set (info);
- return mono_threads_pthread_kill (info, restart_signal_num) == 0;
+ MONO_SEM_POST (&info->resume_semaphore);
+ while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
+ /* g_assert (errno == EINTR); */
+ }
+
+ return TRUE;
}
void
mono_threads_platform_register (MonoThreadInfo *info)
{
+ MONO_SEM_INIT (&info->begin_suspend_semaphore, 0);
+
#if defined (PLATFORM_ANDROID)
info->native_handle = gettid ();
#endif
void
mono_threads_platform_free (MonoThreadInfo *info)
{
+ MONO_SEM_DESTROY (&info->begin_suspend_semaphore);
}
MonoNativeThreadId
void
mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
{
-#if defined (HAVE_PTHREAD_SETNAME_NP) && !defined (__MACH__)
+#ifdef HAVE_PTHREAD_SETNAME_NP
if (!name) {
pthread_setname_np (tid, "");
} else {
#endif
}
-#endif /*defined (USE_POSIX_BACKEND)*/
+#endif /*!defined (__MACH__)*/
#endif
+++ /dev/null
-#include <config.h>
-
-#include <mono/utils/mono-compiler.h>
-#include <mono/utils/mono-threads.h>
-#include <mono/utils/mono-tls.h>
-#include <mono/utils/mono-memory-model.h>
-#include <mono/utils/atomic.h>
-
-#include <errno.h>
-
-/*thread state helpers*/
-static inline int
-get_thread_state (int thread_state)
-{
- return thread_state & THREAD_STATE_MASK;
-}
-
-static inline int
-get_thread_suspend_count (int thread_state)
-{
- return (thread_state & THREAD_SUSPEND_COUNT_MASK) >> THREAD_SUSPEND_COUNT_SHIFT;
-}
-
-static inline int
-build_thread_state (int thread_state, int suspend_count)
-{
- g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
- g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
-
- return thread_state | (suspend_count << THREAD_SUSPEND_COUNT_SHIFT);
-}
-
-static const char*
-state_name (int state)
-{
- static const char *state_names [] = {
- "STARTING",
- "RUNNING",
- "DETACHED",
- "ASYNC_SUSPENDED",
- "SELF_SUSPENDED",
- "ASYNC_SUSPEND_REQUESTED",
- "SELF_SUSPEND_REQUESTED",
- "SUSPEND_IN_PROGRESS",
- "SUSPEND_PROMOTED_TO_ASYNC",
- };
- return state_names [get_thread_state (state)];
-}
-
-#define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,INFO) do { \
- RAW = (INFO)->thread_state; \
- CUR = get_thread_state (RAW); \
- COUNT = get_thread_suspend_count (RAW); \
-} while (0)
-
-static void
-check_thread_state (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_STARTING:
- case STATE_RUNNING:
- case STATE_DETACHED:
- g_assert (suspend_count == 0);
- break;
- case STATE_ASYNC_SUSPENDED:
- case STATE_SELF_SUSPENDED:
- case STATE_ASYNC_SUSPEND_REQUESTED:
- case STATE_SELF_SUSPEND_REQUESTED:
- case STATE_SUSPEND_IN_PROGRESS:
- case STATE_SUSPEND_PROMOTED_TO_ASYNC:
- g_assert (suspend_count > 0);
- break;
- default:
- g_error ("Invalid state %d", cur_state);
- }
-}
-
-static inline void
-trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta)
-{
- check_thread_state (info);
- THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s -> %s (%d -> %d)\n",
- transition,
- mono_thread_info_get_tid (info),
- state_name (get_thread_state (cur_raw_state)),
- state_name (next_state),
- get_thread_suspend_count (cur_raw_state),
- get_thread_suspend_count (cur_raw_state) + suspend_count_delta);
-}
-
-/*
-This is the transition that signals that a thread is functioning.
-Its main goal is to catch threads been witnessed before been fully registered.
-*/
-void
-mono_threads_transition_attach (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_STARTING:
- g_assert (suspend_count == 0);
- if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
- break;
- default:
- g_error ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
- }
-}
-
-/*
-This is the transition that signals that a thread is no longer registered with the runtime.
-Its main goal is to catch threads been witnessed after they detach.
-
-This returns TRUE is the transition succeeded.
-If it returns false it means that there's a pending suspend that should be acted upon.
-*/
-gboolean
-mono_threads_transition_detach (MonoThreadInfo *info)
-{
- int raw_state, cur_state, suspend_count;
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_RUNNING:
- g_assert (suspend_count == 0);
- if (InterlockedCompareExchange (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
- return TRUE;
- case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
- return FALSE;
-/*
-STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
-STATE_SUSPEND_IN_PROGRESS: This is an internal state of suspension
-STATE_SUSPEND_PROMOTED_TO_ASYNC: This is an internal state of suspension
-*/
- default:
- g_error ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
- }
-}
-
-/*
-This transition initiates the suspension of the current thread.
-*/
-void
-mono_threads_transition_request_self_suspension (MonoThreadInfo *info)
-{
- int raw_state, cur_state, suspend_count;
- g_assert (info == mono_thread_info_current ());
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
-
- switch (cur_state) {
- case STATE_RUNNING: //Post a self suspend request
- g_assert (suspend_count == 0);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("SELF_SUSPEND_REQUEST", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 1);
- break;
-
- case STATE_ASYNC_SUSPEND_REQUESTED: //Bump the suspend count but don't change the request type as async takes preference
- g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("SUSPEND_REQUEST", info, raw_state, cur_state, 1);
- break;
-/*
-Other states:
-STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPEND_REQUESTED: Self suspends should not nest as begin/end should be paired. [1]
-STATE_SUSPEND_IN_PROGRESS: This in an internal state of the self suspend finish protocol. A new self suspend request must not happen during it
-STATE_SUSPEND_PROMOTED_TO_ASYNC: This in an internal state of the self suspend finish protocol. A new self suspend request must not happen during it
-
-[1] This won't trap this sequence of requests: self suspend, async suspend and self suspend.
-If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
-*/
- default:
- g_error ("Cannot transition thread %p from %s with SUSPEND_REQUEST", info, state_name (cur_state));
- }
-}
-
-/*
-This transition initiates the suspension of another thread.
-
-Returns TRUE if suspension notification will happen and FALSE if the target is already suspended.
-*/
-gboolean
-mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
-{
- int raw_state, cur_state, suspend_count;
- g_assert (info != mono_thread_info_current ());
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
-
- switch (cur_state) {
- case STATE_RUNNING: //Post an async suspend request
- g_assert (suspend_count == 0);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
- return TRUE; //This is the first async suspend request against the target
-
- case STATE_ASYNC_SUSPENDED:
- case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
- g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
- return FALSE; //Thread is already suspended so we don't need to wait it to suspend
-
- case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
- g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
- return TRUE; //This is the first async suspend request against the target
-
- case STATE_SUSPEND_IN_PROGRESS: //Self suspend has already initiated, we need to tell it to inform us in the end
- g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
- g_assert (info != mono_thread_info_current ()); // if this is a self suspend request, which can't happen in this state, as this is the middle of the self suspend protocol.
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count + 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 1);
- return TRUE; //This is a self suspend in progress that now needs to notify the initiator
- break;
-/*
-STATE_ASYNC_SUSPEND_REQUESTED: Since there can only be one async suspend in progress and it must finish, it should not be possible to witness this.
-STATE_SUSPEND_PROMOTED_TO_ASYNC: This is a self suspend that was promoted to an async suspend, which should not be possible to witness due to async suspends happening one at a time.
-*/
- default:
- g_error ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", info, state_name (cur_state));
- }
- return FALSE;
-}
-
-/*
-Check the current state of the thread and try to init a self suspend.
-
-Returns TRUE is self suspend should start.
-
-*/
-gboolean
-mono_threads_transition_state_poll (MonoThreadInfo *info)
-{
- int raw_state, cur_state, suspend_count;
- g_assert (info == mono_thread_info_current ());
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_RUNNING:
- g_assert (suspend_count == 0);
- trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
- return FALSE; //We're fine, don't suspend
-
- case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
- g_assert (suspend_count > 0);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("STATE_POLL", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 0);
- return TRUE;
-
- case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
- g_assert (suspend_count > 0);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_IN_PROGRESS, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("STATE_POLL", info, raw_state, STATE_SUSPEND_IN_PROGRESS, 0);
- return TRUE;
-
-/*
-STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPENDED: Code should not be running while suspended.
-STATE_SUSPEND_IN_PROGRESS: State polling should not happen while self suspend is finishing
-STATE_SUSPEND_PROMOTED_TO_ASYNC: State polling should not happen while self suspend is finishing
-*/
- default:
- g_error ("Cannot transition thread %p from %s with STATE_POLL", info, state_name (cur_state));
- }
-}
-
-/*
-Try to resume a suspended thread.
-
-Returns one of the following values:
-- Sucess: The thread was resumed.
-- Error: The thread was not suspended in the first place. [2]
-- InitSelfResume: The thread is blocked on self suspend and should be resumed
-- InitAsycResume: The thread is blocked on async suspend and should be resumed
-
-[2] This threading system uses an unsigned suspend count. Which means a resume cannot be
-used as a suspend permit and cancel each other.
-
-Suspend permits are really useful to implement managed synchronization structures that
-don't consume native resources. The downside is that they further complicate the design of this
-system as the RUNNING state now has a non zero suspend counter.
-
-It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
-
-One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
-This would make permits really harder to add.
-*/
-MonoResumeResult
-mono_threads_transition_request_resume (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
- g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_RUNNING: //Thread already running.
- trace_state_change ("RESUME", info, raw_state, cur_state, 0);
- return ResumeError; //Resume failed because thread was not blocked
-
- case STATE_ASYNC_SUSPENDED:
- case STATE_SELF_SUSPENDED: //Decrease the suspend_count and maybe resume
- g_assert (suspend_count > 0);
- if (suspend_count > 1) {
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("RESUME", info, raw_state, cur_state, -1);
-
- return ResumeOk; //Resume worked and there's nothing for the caller to do.
- } else {
- if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
-
- if (cur_state == STATE_ASYNC_SUSPENDED)
- return ResumeInitAsyncResume; //Resume worked and caller must do async resume
- else
- return ResumeInitSelfResume; //Resume worked and caller must do self resume
- }
-
- case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
- case STATE_SUSPEND_IN_PROGRESS: //Self suspend is in progress but another thread decided to resume it. [4]
- g_assert (suspend_count > 0);
- if (suspend_count > 1) {
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("RESUME", info, raw_state, cur_state, -1);
- } else {
- if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
- }
- return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
-
-/*
-
-STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
-STATE_SUSPEND_PROMOTED_TO_ASYNC: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
-
-[3] A self-resume makes no sense given it requires the thread to be running, which means its suspend count must be zero. A self resume would make
-sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
-
-[4] It's questionable on whether a resume (an async operation) should be able to cancel a self suspend. The scenario where this would happen
-is similar to the one described in [2] when this is used for as a synchronization primitive.
-
-If this turns to be a problem we should either implement [2] or make this an invalid transition.
-
-*/
- default:
- g_error ("Cannot transition thread %p from %s with REQUEST_RESUME", info, state_name (cur_state));
- }
-}
-
-/*
-Last part of the self suspend protocol.
-
-This must only happens in the context of a self suspend request. This means that the thread must be on one of the
-valid self suspend states.
-
-Returns one of the following values:
-
-- Resumed: Async resume happened and current thread should keep running
-- Suspend: Caller should wait for a resume signal
-- SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
-*/
-MonoSelfSupendResult
-mono_threads_transition_finish_self_suspend (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
- g_assert (info == mono_thread_info_current ());
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_RUNNING: //An async resume hit us and we should keep running
- trace_state_change ("FINISH_SELF_SUSPEND", info, raw_state, cur_state, 0);
- return SelfSuspendResumed; //Caller should not suspend
-
- case STATE_SUSPEND_IN_PROGRESS: //Self suspend finished
- case STATE_SUSPEND_PROMOTED_TO_ASYNC: //Async suspend happened during the second step of self suspend so the caller needs to notify the initiator.
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("FINISH_SELF_SUSPEND", info, raw_state, STATE_SELF_SUSPENDED, 0);
- if (cur_state == STATE_SUSPEND_IN_PROGRESS)
- return SelfSuspendWait; //Caller should wait for resume
- else
- return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
-/*
-STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPENDED: Code should not be running while suspended.
-STATE_ASYNC_SUSPEND_REQUESTED: This state should one be witnessed by the state poll transition
-STATE_SELF_SUSPEND_REQUESTED: This state should one be witnessed by the state poll transition
-*/
- default:
- g_error ("Cannot transition thread %p from %s with FINISH_SELF_SUSPEND", info, state_name (cur_state));
-
- }
-}
-
-/*
-This performs the last step of async suspend.
-
-Returns TRUE if the caller should wait for resume.
-*/
-gboolean
-mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
-
- case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
- trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
- return FALSE; //let self suspend wait
-
- case STATE_ASYNC_SUSPEND_REQUESTED:
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
- return TRUE; //Async suspend worked, now wait for resume
-
- case STATE_SUSPEND_IN_PROGRESS:
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 0);
- return FALSE; //async suspend race with self suspend and lost, let the other finish it
-/*
-STATE_RUNNING: A thread cannot escape suspension once requested.
-STATE_ASYNC_SUSPENDED: There can be only one suspend initiator at a given time, meaning this state should have been visible on the first stage of suspend.
-STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
-STATE_SUSPEND_PROMOTED_TO_ASYNC: Given there's a single initiator this cannot happen.
-*/
- default:
- g_error ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", info, state_name (cur_state));
-
- }
-}
-
-/*
-This the compensatory transition for failed async suspend.
-
-Async suspend can land on a thread as it began cleaning up and is no longer
-functional. This happens as cleanup is a racy process from the async suspend
-perspective. The thread could have cleaned up its domain or jit_tls, for example.
-
-It can only transition the state as left by a sucessfull finish async suspend transition.
-
-*/
-void
-mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
-
- case STATE_ASYNC_SUSPENDED:
- /*
- Must be one since if a self suspend is in progress the thread should still be async suspendable.
- If count > 1 and no self suspend is in progress then it means one of the following two.
- - the thread was previously suspended, which means we should never reach end suspend in the first place.
- - another suspend happened concurrently, which means the global suspend lock didn't happen.
- */
- g_assert (suspend_count == 1);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count - 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("COMPENSATE_FINISH_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, -1);
- break;
-/*
-STATE_RUNNING
-STATE_SELF_SUSPENDED
-STATE_ASYNC_SUSPEND_REQUESTED
-STATE_SELF_SUSPEND_REQUESTED
-STATE_SUSPEND_PROMOTED_TO_ASYNC:
-STATE_SUSPEND_IN_PROGRESS: All those are invalid end states of a sucessfull finish async suspend
-*/
- default:
- g_error ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", info, state_name (cur_state));
-
- }
-}
-
-
-
-
-// State checking code
-/**
- * Return TRUE is the thread is in a runnable state.
-*/
-gboolean
-mono_thread_info_is_running (MonoThreadInfo *info)
-{
- switch (get_thread_state (info->thread_state)) {
- case STATE_RUNNING:
- case STATE_ASYNC_SUSPEND_REQUESTED:
- case STATE_SELF_SUSPEND_REQUESTED:
- return TRUE;
- }
- return FALSE;
-}
-
-/**
- * Return TRUE is the thread is in an usable (suspendable) state
- */
-gboolean
-mono_thread_info_is_live (MonoThreadInfo *info)
-{
- switch (get_thread_state (info->thread_state)) {
- case STATE_STARTING:
- case STATE_DETACHED:
- return FALSE;
- }
- return TRUE;
-}
-
-int
-mono_thread_info_suspend_count (MonoThreadInfo *info)
-{
- return get_thread_suspend_count (info->thread_state);
-}
\ No newline at end of file
}
void
-mono_threads_core_abort_syscall (MonoThreadInfo *info)
+mono_threads_core_interrupt (MonoThreadInfo *info)
{
DWORD id = mono_thread_info_get_tid (info);
HANDLE handle;
return FALSE;
}
+void
+mono_threads_core_self_suspend (MonoThreadInfo *info)
+{
+ g_assert_not_reached ();
+}
+
gboolean
-mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
+mono_threads_core_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
DWORD id = mono_thread_info_get_tid (info);
HANDLE handle;
DWORD result;
gboolean res;
+ g_assert (id != GetCurrentThreadId ());
+
handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
g_assert (handle);
result = SuspendThread (handle);
- THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
if (result == (DWORD)-1) {
+ fprintf (stderr, "could not suspend thread %x (handle %p): %d\n", id, handle, GetLastError ()); fflush (stderr);
CloseHandle (handle);
return FALSE;
}
- /* We're in the middle of a self-suspend, resume and register */
- if (!mono_threads_transition_finish_async_suspend (info)) {
- mono_threads_add_to_pending_operation_set (info);
- g_assert (ResumeThread (handle) == KERN_SUCCESS);
- CloseHandle (handle);
- THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
- //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
- return TRUE;
- }
- res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->suspend_state, info);
- THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
- if (res) {
- //FIXME do we need to QueueUserAPC on this case?
- if (interrupt_kernel)
- QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
- } else {
- mono_threads_transition_async_suspend_compensation (info);
- g_assert (ResumeThread (handle) == KERN_SUCCESS);
- THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
- }
-
CloseHandle (handle);
- return res;
-}
-gboolean
-mono_threads_core_check_suspend_result (MonoThreadInfo *info)
-{
+ res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->suspend_state, info);
+ g_assert (res);
+
return TRUE;
}
gboolean
-mono_threads_core_begin_async_resume (MonoThreadInfo *info)
+mono_threads_core_resume (MonoThreadInfo *info)
{
DWORD id = mono_thread_info_get_tid (info);
HANDLE handle;
context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
res = SetThreadContext (handle, &context);
- if (!res) {
- CloseHandle (handle);
- return FALSE;
- }
+ g_assert (res);
}
result = ResumeThread (handle);
+ g_assert (result != (DWORD)-1);
+
CloseHandle (handle);
return result != (DWORD)-1;
}
-
void
mono_threads_platform_register (MonoThreadInfo *info)
{
#include <mono/utils/mono-memory-model.h>
#include <mono/utils/mono-mmap.h>
#include <mono/utils/atomic.h>
-#include <mono/utils/mono-time.h>
-
#include <errno.h>
#include <mono/utils/mach-support.h>
#endif
+#define THREADS_DEBUG(...)
+//#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
+
/*
Mutex that makes sure only a single thread can be suspending others.
Suspend is a very racy operation since it requires restarting until
static gboolean disable_new_interrupt = FALSE;
static gboolean mono_threads_inited = FALSE;
-static MonoSemType suspend_semaphore;
-static size_t pending_suspends;
-static gboolean unified_suspend_enabled;
-
-#define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
-
-/*warn at 50 ms*/
-#define SLEEP_DURATION_BEFORE_WARNING (50)
-/*abort at 1 sec*/
-#define SLEEP_DURATION_BEFORE_ABORT 1000
-
-static void
-wait_for_resume (MonoThreadInfo* info)
-{
- MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
-}
-
-void
-mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
-{
- THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", info);
- MONO_SEM_POST (&suspend_semaphore);
-}
-
-void
-mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
-{
- THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", info);
- MONO_SEM_POST (&suspend_semaphore);
-}
-
-static void
-resume_self_suspended (MonoThreadInfo* info)
-{
- THREADS_SUSPEND_DEBUG ("begin self-resume %p\n", info);
- MONO_SEM_POST (&info->resume_semaphore);
-}
-
-static void
-resume_async_suspended (MonoThreadInfo *info)
-{
- g_assert (mono_threads_core_begin_async_resume (info));
-}
-
-void
-mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
-{
- THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", info);
- ++pending_suspends;
-}
-
-void
-mono_threads_begin_global_suspend (void)
-{
- g_assert (pending_suspends == 0);
- THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP\n");
-}
-
-void
-mono_threads_end_global_suspend (void)
-{
- g_assert (pending_suspends == 0);
- THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP\n");
-}
-
-static void
-dump_threads (void)
-{
- MonoThreadInfo *info;
- FOREACH_THREAD_SAFE (info) {
- THREADS_SUSPEND_DEBUG ("--thread %p id %p state %x\n", info, mono_thread_info_get_tid (info), info->thread_state);
- } END_FOREACH_THREAD_SAFE
-}
-
-gboolean
-mono_threads_wait_pending_operations (void)
-{
- int i;
- int c = pending_suspends;
-
- /* Wait threads to park */
- if (pending_suspends) {
- MonoStopwatch suspension_time;
- mono_stopwatch_start (&suspension_time);
- THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
- for (i = 0; i < pending_suspends; ++i) {
- THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
- if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
- continue;
- mono_stopwatch_stop (&suspension_time);
-
- dump_threads ();
-
- THREADS_SUSPEND_DEBUG ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
- THREADS_SUSPEND_DEBUG ("cur thread is %p\n", pthread_self ());
- 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);
- }
- 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));
-
- }
-
- pending_suspends = 0;
-
- return c > 0;
-}
+static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
+#define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & RUN_STATE_MASK)
+#define mono_thread_info_suspend_state(info) (((MonoThreadInfo*)info)->thread_state & SUSPEND_STATE_MASK)
-//Thread initialization code
-
-static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
static inline void
mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
{
MonoThreadInfo *info = mem;
+ MONO_SEM_DESTROY (&info->suspend_semaphore);
MONO_SEM_DESTROY (&info->resume_semaphore);
+ MONO_SEM_DESTROY (&info->finish_resume_semaphore);
mono_threads_platform_free (info);
g_free (info);
mono_thread_info_set_tid (info, mono_native_thread_id_get ());
info->small_id = small_id;
+ MONO_SEM_INIT (&info->suspend_semaphore, 1);
MONO_SEM_INIT (&info->resume_semaphore, 0);
+ MONO_SEM_INIT (&info->finish_resume_semaphore, 0);
/*set TLS early so SMR works */
mono_native_tls_set_value (thread_info_key, info);
info->stack_end = staddr + stsize;
mono_threads_platform_register (info);
-
- /*
- Transition it before taking any locks or publishing itself to reduce the chance
- of others witnessing a detached thread.
- We can reasonably expect that until this thread gets published, no other thread will
- try to manipulate it.
- */
- mono_threads_transition_attach (info);
+ info->thread_state = STATE_RUNNING;
mono_thread_info_suspend_lock ();
/*If this fail it means a given thread has been registered twice, which doesn't make sense. */
result = mono_thread_info_insert (info);
mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
#endif
+ info->thread_state = STATE_SHUTTING_DOWN;
+
/*
First perform the callback that requires no locks.
This callback has the potential of taking other locks, so we do it before.
if (threads_callbacks.thread_unregister)
threads_callbacks.thread_unregister (info);
mono_threads_unregister_current_thread (info);
- mono_threads_transition_detach (info);
+ info->thread_state = STATE_DEAD;
mono_thread_info_suspend_unlock ();
/*now it's safe to free the thread info.*/
g_assert (result);
}
-static inline MonoThreadInfo*
-mono_thread_info_current_unchecked (void)
-{
- return (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
-}
-
-
MonoThreadInfo*
mono_thread_info_current (void)
{
#endif
g_assert (res);
- unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL;
-
MONO_SEM_INIT (&global_suspend_semaphore, 1);
- MONO_SEM_INIT (&suspend_semaphore, 0);
mono_lls_init (&thread_list, NULL);
mono_thread_smr_init ();
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
if (!info) {
-g_assert (pending_suspends == 0);
*error_condition = "Thread not found";
return NULL;
}
- g_assert (pending_suspends == 0); /* FIXME remove this assert by better delimiting global ops */
- // MONO_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
+ MONO_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
- if (!mono_threads_transition_request_async_suspension (info)) {
- g_assert (pending_suspends == 0);
- mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
- // MONO_SEM_POST (&info->suspend_semaphore);
+ /*thread is on the process of detaching*/
+ if (mono_thread_info_run_state (info) > STATE_RUNNING) {
+ mono_hazard_pointer_clear (hp, 1);
+ *error_condition = "Thread is detaching";
+ return NULL;
+ }
+
+ THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
+
+ if (info->suspend_count) {
+ ++info->suspend_count;
+ mono_hazard_pointer_clear (hp, 1);
+ MONO_SEM_POST (&info->suspend_semaphore);
return info;
}
- if (!mono_threads_core_begin_async_suspend (info, interrupt_kernel)) {
- g_assert (pending_suspends == 0);
- // MONO_SEM_POST (&info->suspend_semaphore);
+ if (!mono_threads_core_suspend (info, interrupt_kernel)) {
+ MONO_SEM_POST (&info->suspend_semaphore);
mono_hazard_pointer_clear (hp, 1);
*error_condition = "Could not suspend thread";
return NULL;
}
- //Wait for the pending suspend to finish
- mono_threads_wait_pending_operations ();
+ if (interrupt_kernel)
+ mono_threads_core_interrupt (info);
- if (!mono_threads_core_check_suspend_result (info)) {
- g_assert (pending_suspends == 0);
+ ++info->suspend_count;
+ info->thread_state |= STATE_SUSPENDED;
+ MONO_SEM_POST (&info->suspend_semaphore);
- mono_hazard_pointer_clear (hp, 1);
- *error_condition = "Post suspend failed";
- return NULL;
- }
- // MONO_SEM_POST (&info->suspend_semaphore);
return info;
}
-/*
-Signal that the current thread wants to be suspended.
-This function can be called without holding the suspend lock held.
-To finish suspending, call mono_suspend_check.
-*/
void
-mono_thread_info_begin_self_suspend (void)
+mono_thread_info_self_suspend (void)
{
- MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+ gboolean ret;
+ MonoThreadInfo *info = mono_thread_info_current ();
if (!info)
return;
- THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
- mono_threads_transition_request_self_suspension (info);
-}
+ MONO_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
-void
-mono_thread_info_end_self_suspend (void)
-{
- MonoThreadInfo *info;
+ THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
- info = mono_thread_info_current ();
- if (!info)
- return;
- THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
+ g_assert (info->suspend_count == 0);
+ ++info->suspend_count;
- /* Begin stage two which allows us to save our state */
- if (!mono_threads_transition_state_poll (info))
- return;
+ info->thread_state |= STATE_SELF_SUSPENDED;
- g_assert (mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->suspend_state, NULL));
+ ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->suspend_state, NULL);
+ g_assert (ret);
- /* commit the saved state and notify others if needed */
- switch (mono_threads_transition_finish_self_suspend (info)) {
- case SelfSuspendResumed:
- return;
- case SelfSuspendWait:
- wait_for_resume (info);
- break;
- case SelfSuspendNotifyAndWait:
- mono_threads_notify_initiator_of_suspend (info);
- wait_for_resume (info);
- mono_threads_notify_initiator_of_resume (info);
- break;
- }
+ MONO_SEM_POST (&info->suspend_semaphore);
+
+ MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
+
+ g_assert (!info->async_target); /*FIXME this should happen normally for suspend. */
+ MONO_SEM_POST (&info->finish_resume_semaphore);
}
static gboolean
mono_thread_info_core_resume (MonoThreadInfo *info)
{
- gboolean res = FALSE;
+ gboolean result;
+ MonoNativeThreadId tid = mono_thread_info_get_tid (info);
if (info->create_suspended) {
- MonoNativeThreadId tid = mono_thread_info_get_tid (info);
/* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
info->create_suspended = FALSE;
mono_threads_core_resume_created (info, tid);
return TRUE;
}
- switch (mono_threads_transition_request_resume (info)) {
- case ResumeError:
- res = FALSE;
- break;
- case ResumeOk:
- res = TRUE;
- break;
- case ResumeInitSelfResume:
- resume_self_suspended (info);
- res = TRUE;
- break;
- case ResumeInitAsyncResume:
- resume_async_suspended (info);
- res = TRUE;
- break;
+ MONO_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
+
+ THREADS_DEBUG ("resume %x IN COUNT %d\n", tid, info->suspend_count);
+
+ if (info->suspend_count <= 0) {
+ MONO_SEM_POST (&info->suspend_semaphore);
+ return FALSE;
}
- return res;
+ /*
+ * The theory here is that if we manage to suspend the thread it means it did not
+ * start cleanup since it take the same lock.
+ */
+ g_assert (mono_thread_info_get_tid (info));
+
+ if (--info->suspend_count == 0) {
+ if (mono_thread_info_suspend_state (info) == STATE_SELF_SUSPENDED) {
+ MONO_SEM_POST (&info->resume_semaphore);
+ MONO_SEM_WAIT_UNITERRUPTIBLE (&info->finish_resume_semaphore);
+ result = TRUE;
+ } else {
+ result = mono_threads_core_resume (info);
+ }
+ info->thread_state &= ~SUSPEND_STATE_MASK;
+ } else {
+ result = TRUE;
+ }
+
+ MONO_SEM_POST (&info->suspend_semaphore);
+ return result;
}
gboolean
{
gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- MonoThreadInfo *info;
-
- THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
-
-
- mono_thread_info_suspend_lock ();
+ MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
- g_assert (pending_suspends == 0);
- info = mono_thread_info_lookup (tid); /*info on HP1*/
if (!info) {
result = FALSE;
goto cleanup;
}
-
result = mono_thread_info_core_resume (info);
- //Wait for the pending resume to finish
- mono_threads_wait_pending_operations ();
-
cleanup:
- mono_thread_info_suspend_unlock ();
mono_hazard_pointer_clear (hp, 1);
- g_assert (pending_suspends == 0);
return result;
}
-gboolean
-mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
-{
- gboolean res;
- if (!mono_threads_transition_request_async_suspension (info))
- return TRUE; //already suspended
-
- res = mono_threads_core_begin_async_suspend (info, interrupt_kernel);
- return res;
-}
-
-gboolean
-mono_thread_info_begin_resume (MonoThreadInfo *info)
-{
- return mono_thread_info_core_resume (info);
-}
-
void
mono_thread_info_finish_suspend (MonoThreadInfo *info)
{
-g_assert (pending_suspends == 0);
mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, FALSE);
}
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- mono_thread_info_suspend_lock ();
-
-g_assert (pending_suspends == 0);
/*Resume can access info after the target has resumed, so we must ensure it won't touch freed memory. */
mono_hazard_pointer_set (hp, 1, info);
mono_thread_info_core_resume (info);
mono_hazard_pointer_clear (hp, 1);
- //Wait for the pending resume to finish
- mono_threads_wait_pending_operations ();
-
-g_assert (pending_suspends == 0);
-
mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, FALSE);
-
- mono_thread_info_suspend_unlock ();
}
/*
if (info->inside_critical_region)
return TRUE;
- if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
- return TRUE;
- }
-
/* Are we inside a GC critical region? */
if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
return TRUE;
return threads_callbacks.mono_method_is_critical (method);
}
-gboolean
-mono_thread_info_in_critical_location (MonoThreadInfo *info)
-{
- return is_thread_in_critical_region (info);
-}
-
/*
WARNING:
If we are trying to suspend a target that is on a critical region
MonoThreadInfo *info = NULL;
int sleep_duration = 0;
- THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
/*FIXME: unify this with self-suspend*/
g_assert (id != mono_native_thread_id_get ());
mono_thread_info_suspend_lock ();
- mono_threads_begin_global_suspend ();
for (;;) {
const char *suspend_error = "Unknown error";
if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
- g_assert (pending_suspends == 0);
- goto fail;
+ // g_warning ("failed to suspend thread %p due to %s, hopefully it is dead", (gpointer)id, suspend_error);
+ mono_thread_info_suspend_unlock ();
+ return NULL;
}
-
/*WARNING: We now are in interrupt context until we resume the thread. */
if (!is_thread_in_critical_region (info))
break;
if (!mono_thread_info_core_resume (info)) {
- g_assert (pending_suspends == 0);
+ // g_warning ("failed to resume thread %p, hopefully it is dead", (gpointer)id);
mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
mono_thread_info_suspend_unlock ();
- goto fail;
+ return NULL;
}
- THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
-
- /* Wait for the pending resume to finish */
- mono_threads_wait_pending_operations ();
+ THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
if (!sleep_duration) {
#ifdef HOST_WIN32
/* XXX this clears HP 1, so we restated it again */
mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
- mono_threads_end_global_suspend ();
mono_thread_info_suspend_unlock ();
return info;
-fail:
- mono_threads_end_global_suspend ();
- mono_thread_info_suspend_unlock ();
- return NULL;
}
/**
void
mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
{
- /* An async call can only be setup on an async suspended thread */
- g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
+ g_assert (info->suspend_count);
/*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
g_assert (!info->async_target);
info->async_target = target_func;
mono_thread_info_suspend_unlock ();
}
-gboolean
-mono_thread_info_unified_management_enabled (void)
-{
- return unified_suspend_enabled;
-}
-
/*
Disabled by default for now.
To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
return InterlockedExchange (&info->service_requests, 0);
}
+
+
+/**
+ * Return TRUE is the thread is in an usable (suspendable) state
+ */
+gboolean
+mono_thread_info_is_live (MonoThreadInfo *info)
+{
+ return mono_thread_info_run_state (info) == STATE_RUNNING;
+}
#define THREAD_INFO_TYPE MonoThreadInfo
#endif
-/* Mono Threads internal configuration knows*/
-
-/* Logging - enable them below if you need specific logging for the category you need */
-#define MOSTLY_ASYNC_SAFE_PRINTF(...) do { \
- char __buff[1024]; __buff [0] = '\0'; \
- snprintf (__buff, sizeof (__buff), __VA_ARGS__); \
- write (1, __buff, strlen (__buff)); \
-} while (0)
-
-
-#if 1
-#define THREADS_DEBUG(...)
-#else
-#define THREADS_DEBUG MOSTLY_ASYNC_SAFE_PRINTF
-#endif
-
-#if 1
-#define THREADS_STW_DEBUG(...)
-#else
-#define THREADS_STW_DEBUG MOSTLY_ASYNC_SAFE_PRINTF
-#endif
-
-#if 1
-#define THREADS_SUSPEND_DEBUG(...)
-#else
-#define THREADS_SUSPEND_DEBUG MOSTLY_ASYNC_SAFE_PRINTF
-#endif
-
-#if 1
-#define THREADS_STATE_MACHINE_DEBUG(...)
-#else
-#define THREADS_STATE_MACHINE_DEBUG MOSTLY_ASYNC_SAFE_PRINTF
-#endif
-
-/* If this is defined, use the signals backed on Mach. Debug only as signals can't be made usable on OSX. */
-// #define USE_SIGNALS_ON_MACH
-
-#if defined (_POSIX_VERSION) || defined (__native_client__)
-#if defined (__MACH__) && !defined (USE_SIGNALS_ON_MACH)
-#define USE_MACH_BACKEND
-#else
-#define USE_POSIX_BACKEND
-#endif
-#endif
-
-
enum {
- STATE_STARTING = 0x00,
- STATE_RUNNING = 0x01,
- STATE_DETACHED = 0x02,
-
- STATE_ASYNC_SUSPENDED = 0x03,
- STATE_SELF_SUSPENDED = 0x04,
- STATE_ASYNC_SUSPEND_REQUESTED = 0x05,
- STATE_SELF_SUSPEND_REQUESTED = 0x06,
- STATE_SUSPEND_IN_PROGRESS = 0x07,
- STATE_SUSPEND_PROMOTED_TO_ASYNC = 0x08,
- STATE_MAX = 0x08,
-
- THREAD_STATE_MASK = 0x00FF,
- THREAD_SUSPEND_COUNT_MASK = 0xFF00,
- THREAD_SUSPEND_COUNT_SHIFT = 8,
- THREAD_SUSPEND_COUNT_MAX = 0xFF
+ STATE_STARTING = 0x00,
+ STATE_RUNNING = 0x01,
+ STATE_SHUTTING_DOWN = 0x02,
+ STATE_DEAD = 0x03,
+ RUN_STATE_MASK = 0x0F,
+
+ STATE_SUSPENDED = 0x10,
+ STATE_SELF_SUSPENDED = 0x20,
+ SUSPEND_STATE_MASK = 0xF0,
};
/*
MonoSemType suspend_semaphore;
int suspend_count;
- // MonoSemType suspend_semaphore;
- MonoSemType resume_semaphore;
+ MonoSemType finish_resume_semaphore;
+ MonoSemType resume_semaphore;
/* only needed by the posix backend */
-#if defined(USE_POSIX_BACKEND)
- MonoSemType finish_resume_semaphore;
+#if (defined(_POSIX_VERSION) || defined(__native_client__)) && !defined (__MACH__)
+ MonoSemType begin_suspend_semaphore;
gboolean syscall_break_signal;
gboolean suspend_can_continue;
- int signal;
-
#endif
/*In theory, only the posix backend needs this, but having it on mach/win32 simplifies things a lot.*/
void
mono_thread_info_finish_suspend_and_resume (MonoThreadInfo *info) MONO_INTERNAL;
-//XXX new API, fix the world
-void
-mono_thread_info_begin_self_suspend (void) MONO_INTERNAL;
-
void
-mono_thread_info_end_self_suspend (void) MONO_INTERNAL;
-
-//END of new API
+mono_thread_info_self_suspend (void) MONO_INTERNAL;
gboolean
mono_thread_info_new_interrupt_enabled (void) MONO_INTERNAL;
-gboolean
-mono_thread_info_unified_management_enabled (void) MONO_INTERNAL;
-
void
mono_thread_info_setup_async_call (THREAD_INFO_TYPE *info, void (*target_func)(void*), void *user_data) MONO_INTERNAL;
#endif /* !defined(HOST_WIN32) */
-
-/* Internal API between mono-threads and its backends. */
-
-/* Backend functions - a backend must implement all of the following */
-/*
-This is called very early in the runtime, it cannot access any runtime facilities.
-
-*/
+/* Plartform specific functions DON'T use them */
void mono_threads_init_platform (void) MONO_INTERNAL; //ok
-
-/*
-This begins async suspend. This function must do the following:
-
--Ensure the target will EINTR any syscalls if @interrupt_kernel is true
--Call mono_threads_transition_finish_async_suspend as part of its async suspend.
--Register the thread for pending suspend with mono_threads_add_to_pending_operation_set if needed.
-
-If begin suspend fails the thread must be left uninterrupted and resumed.
-*/
-gboolean mono_threads_core_begin_async_suspend (THREAD_INFO_TYPE *info, gboolean interrupt_kernel) MONO_INTERNAL;
-
-/*
-This verifies the outcome of an async suspend operation.
-
-Some targets, such as posix, verify suspend results assynchronously. Suspend results must be
-available (in a non blocking way) after mono_threads_wait_pending_operations completes.
-*/
-gboolean mono_threads_core_check_suspend_result (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-
-/*
-This begins async resume. This function must do the following:
-
-- Install an async target if one was requested.
-- Notify the target to resume.
-- Register the thread for pending ack with mono_threads_add_to_pending_operation_set if needed.
-*/
-gboolean mono_threads_core_begin_async_resume (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-
+gboolean mono_threads_core_suspend (THREAD_INFO_TYPE *info, gboolean interrupt_kernel) MONO_INTERNAL;
+gboolean mono_threads_core_resume (THREAD_INFO_TYPE *info) MONO_INTERNAL;
void mono_threads_platform_register (THREAD_INFO_TYPE *info) MONO_INTERNAL; //ok
void mono_threads_platform_free (THREAD_INFO_TYPE *info) MONO_INTERNAL;
+void mono_threads_core_interrupt (THREAD_INFO_TYPE *info) MONO_INTERNAL;
void mono_threads_core_abort_syscall (THREAD_INFO_TYPE *info) MONO_INTERNAL;
gboolean mono_threads_core_needs_abort_syscall (void) MONO_INTERNAL;
HANDLE mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid) MONO_INTERNAL;
void mono_threads_init_dead_letter (void) MONO_INTERNAL;
void mono_threads_install_dead_letter (void) MONO_INTERNAL;
-/* mono-threads internal API used by the backends. */
-/*
-This tells the suspend initiator that we completed suspend and will now be waiting for resume.
-*/
-void mono_threads_notify_initiator_of_suspend (THREAD_INFO_TYPE* info) MONO_INTERNAL;
-/*
-This tells the resume initiator that we completed resume duties and will return to runnable state.
-*/
-void mono_threads_notify_initiator_of_resume (THREAD_INFO_TYPE* info) MONO_INTERNAL;
-
-/* Thread state machine functions */
-
-typedef enum {
- ResumeError,
- ResumeOk,
- ResumeInitSelfResume,
- ResumeInitAsyncResume,
-} MonoResumeResult;
-
-typedef enum {
- SelfSuspendResumed,
- SelfSuspendWait,
- SelfSuspendNotifyAndWait,
-} MonoSelfSupendResult;
-
-void mono_threads_transition_attach (THREAD_INFO_TYPE* info) MONO_INTERNAL;
-gboolean mono_threads_transition_detach (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-void mono_threads_transition_request_self_suspension (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-gboolean mono_threads_transition_request_async_suspension (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-gboolean mono_threads_transition_state_poll (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-MonoResumeResult mono_threads_transition_request_resume (THREAD_INFO_TYPE* info) MONO_INTERNAL;
-MonoSelfSupendResult mono_threads_transition_finish_self_suspend (THREAD_INFO_TYPE* info) MONO_INTERNAL;
-gboolean mono_threads_transition_finish_async_suspend (THREAD_INFO_TYPE* info) MONO_INTERNAL;
-void mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info) MONO_INTERNAL;
-
-
-/* Advanced suspend API, used for suspending multiple threads as once. */
-gboolean mono_thread_info_is_running (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-gboolean mono_thread_info_is_live (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-int mono_thread_info_suspend_count (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-gboolean mono_thread_info_in_critical_location (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-gboolean mono_thread_info_begin_suspend (THREAD_INFO_TYPE *info, gboolean interrupt_kernel) MONO_INTERNAL;
-gboolean mono_thread_info_begin_resume (THREAD_INFO_TYPE *info) MONO_INTERNAL;
-
-
-void mono_threads_add_to_pending_operation_set (THREAD_INFO_TYPE* info) MONO_INTERNAL; //XXX rename to something to reflect the fact that this is used for both suspend and resume
-gboolean mono_threads_wait_pending_operations (void) MONO_INTERNAL;
-void mono_threads_begin_global_suspend (void) MONO_INTERNAL;
-void mono_threads_end_global_suspend (void) MONO_INTERNAL;
-
#endif /* __MONO_THREADS_H__ */