static MonoNativeTlsKey small_id_key;
#endif
static MonoLinkedListSet thread_list;
-static gboolean disable_new_interrupt = FALSE;
static gboolean mono_threads_inited = FALSE;
static MonoSemType suspend_semaphore;
/*abort at 1 sec*/
#define SLEEP_DURATION_BEFORE_ABORT 200
-static int suspend_posts, resume_posts, waits_done, pending_ops;
+static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
+
+void
+mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
+{
+ THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
+ InterlockedIncrement (&abort_posts);
+ MONO_SEM_POST (&suspend_semaphore);
+}
void
mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
{
THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
- MONO_SEM_POST (&suspend_semaphore);
InterlockedIncrement (&suspend_posts);
+ MONO_SEM_POST (&suspend_semaphore);
}
void
mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
{
THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
- MONO_SEM_POST (&suspend_semaphore);
InterlockedIncrement (&resume_posts);
+ MONO_SEM_POST (&suspend_semaphore);
}
static void
mono_threads_begin_global_suspend (void)
{
g_assert (pending_suspends == 0);
- THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d wd %d po %d\n", suspend_posts, resume_posts, waits_done, pending_ops);
+ THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
+ abort_posts, waits_done, pending_ops);
+ g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
+ mono_threads_core_begin_global_suspend ();
}
void
mono_threads_end_global_suspend (void)
{
g_assert (pending_suspends == 0);
- THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d wd %d po %d\n", suspend_posts, resume_posts, waits_done, pending_ops);
+ THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
+ abort_posts, waits_done, pending_ops);
+ g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
+ mono_threads_core_end_global_suspend ();
}
static void
MonoThreadInfo *info;
MonoThreadInfo *cur = mono_thread_info_current ();
- MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2)\n");
+ MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
- MOSTLY_ASYNC_SAFE_PRINTF ("\t0x07\t- blocking (GOOD)\n");
+ 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) {
- MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
+ MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
} END_FOREACH_THREAD_SAFE
}
int c = pending_suspends;
/* Wait threads to park */
+ THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
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");
InterlockedIncrement (&waits_done);
}
mono_hazard_pointer_clear_all (hp, 1);
- return mono_hazard_pointer_get_val (hp, 1);
+ return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
}
static gboolean
static void
free_thread_info (gpointer mem)
{
- MonoThreadInfo *info = mem;
+ MonoThreadInfo *info = (MonoThreadInfo *) mem;
MONO_SEM_DESTROY (&info->resume_semaphore);
mono_threads_platform_free (info);
static void
unregister_thread (void *arg)
{
- MonoThreadInfo *info = arg;
+ MonoThreadInfo *info = (MonoThreadInfo *) arg;
int small_id = info->small_id;
g_assert (info);
MonoThreadInfo*
mono_thread_info_current_unchecked (void)
{
- return (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
+ return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
}
/* Must only be called once */
g_assert (!mono_native_tls_get_value (thread_info_key));
+
+ while (!mono_threads_inited) {
+ g_usleep (10);
+ }
info = mono_thread_info_attach (&dummy);
+ g_assert (info);
+
info->tools_thread = TRUE;
}
MonoThreadInfo *info;
if (!mono_threads_inited)
{
+#ifdef HOST_WIN32
/* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
* thread is created before an embedding API user initialized Mono. */
THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
return NULL;
+#else
+ g_assert (mono_threads_inited);
+#endif
}
- info = mono_native_tls_get_value (thread_info_key);
+ info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
if (!info) {
- info = g_malloc0 (thread_info_size);
+ info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
THREADS_DEBUG ("attaching %p\n", info);
if (!register_thread (info, baseptr))
return NULL;
THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
return;
}
- info = mono_native_tls_get_value (thread_info_key);
+ info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
if (info) {
THREADS_DEBUG ("detaching %p\n", info);
unregister_thread (info);
res = mono_native_tls_alloc (&thread_info_key, NULL);
res = mono_native_tls_alloc (&thread_exited_key, NULL);
#else
- res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
- res = mono_native_tls_alloc (&thread_exited_key, thread_exited_dtor);
+ res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
+ res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
#endif
+
g_assert (res);
#ifndef HAVE_KW_THREAD
runtime_callbacks = *callbacks;
}
-MonoThreadInfoCallbacks *
-mono_threads_get_callbacks (void)
-{
- return &threads_callbacks;
-}
-
MonoThreadInfoRuntimeCallbacks *
mono_threads_get_runtime_callbacks (void)
{
return;
THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
- g_assert (mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL));
+ mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
/* commit the saved state and notify others if needed */
switch (mono_threads_transition_state_poll (info)) {
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 TRUE;
ji = mono_jit_info_table_find (
- state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
- MONO_CONTEXT_GET_IP (&state->ctx));
+ (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
+ (char *) MONO_CONTEXT_GET_IP (&state->ctx));
if (!ji)
return FALSE;
/*FIXME: unify this with self-suspend*/
g_assert (id != mono_native_thread_id_get ());
+ /* This can block during stw */
mono_thread_info_suspend_lock ();
mono_threads_begin_global_suspend ();
void
mono_thread_info_suspend_lock (void)
{
+ MONO_TRY_BLOCKING;
MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
+ MONO_FINISH_TRY_BLOCKING;
}
void
MONO_SEM_POST (&global_suspend_semaphore);
}
-void
-mono_thread_info_disable_new_interrupt (gboolean disable)
-{
- disable_new_interrupt = disable;
-}
-
/*
* This is a very specific function whose only purpose is to
* break a given thread from socket syscalls.
}
mono_thread_info_suspend_lock ();
+ mono_threads_begin_global_suspend ();
mono_threads_core_abort_syscall (info);
+ mono_threads_wait_pending_operations ();
mono_hazard_pointer_clear (hp, 1);
+
+ mono_threads_end_global_suspend ();
mono_thread_info_suspend_unlock ();
}
return unified_suspend_enabled;
}
-/*
-Disabled by default for now.
-To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
-which means mono-context and setup_async_callback, and we need a mono-threads backend.
-*/
-gboolean
-mono_thread_info_new_interrupt_enabled (void)
-{
- /*We need STW gc events to work correctly*/
-#if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
- return FALSE;
-#endif
-#if defined(HOST_WIN32)
- return !disable_new_interrupt;
-#endif
-#if defined (__i386__) || defined(__x86_64__)
- return !disable_new_interrupt;
-#endif
-#if defined(__arm__) || defined(__aarch64__)
- return !disable_new_interrupt;
-#endif
- return FALSE;
-}
-
/*
* mono_thread_info_set_is_async_context:
*
mono_threads_core_set_name (tid, name);
}
+#define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
+
+struct _MonoThreadInfoInterruptToken {
+ void (*callback) (gpointer data);
+ gpointer data;
+};
+
/*
- * mono_thread_info_prepare_interrupt:
+ * mono_thread_info_install_interrupt: install an interruption token for the current thread.
*
- * See wapi_prepare_interrupt ().
+ * - @callback: must be able to be called from another thread and always cancel the wait
+ * - @data: passed to the callback
+ * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
+ * if set to TRUE, it must mean that the thread is in interrupted state
*/
-gpointer
-mono_thread_info_prepare_interrupt (HANDLE thread_handle)
+void
+mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
{
- return mono_threads_core_prepare_interrupt (thread_handle);
+ MonoThreadInfo *info;
+ MonoThreadInfoInterruptToken *previous_token, *token;
+
+ g_assert (callback);
+
+ g_assert (interrupted);
+ *interrupted = FALSE;
+
+ info = mono_thread_info_current ();
+ g_assert (info);
+
+ /* The memory of this token can be freed at 2 places:
+ * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
+ * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
+ * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
+ * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
+ token = g_new0 (MonoThreadInfoInterruptToken, 1);
+ token->callback = callback;
+ token->data = data;
+
+ previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
+
+ if (previous_token) {
+ if (previous_token != INTERRUPT_STATE)
+ g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
+
+ g_free (token);
+
+ *interrupted = TRUE;
+ }
+
+ THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
+ mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
}
void
-mono_thread_info_finish_interrupt (gpointer wait_handle)
+mono_thread_info_uninstall_interrupt (gboolean *interrupted)
+{
+ MonoThreadInfo *info;
+ MonoThreadInfoInterruptToken *previous_token;
+
+ g_assert (interrupted);
+ *interrupted = FALSE;
+
+ info = mono_thread_info_current ();
+ g_assert (info);
+
+ previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
+
+ /* only the installer can uninstall the token */
+ g_assert (previous_token);
+
+ if (previous_token == INTERRUPT_STATE) {
+ /* if it is interrupted, then it is going to be freed in finish interrupt */
+ *interrupted = TRUE;
+ } else {
+ g_free (previous_token);
+ }
+
+ THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
+ mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
+}
+
+static MonoThreadInfoInterruptToken*
+set_interrupt_state (MonoThreadInfo *info)
{
- mono_threads_core_finish_interrupt (wait_handle);
+ MonoThreadInfoInterruptToken *token, *previous_token;
+
+ g_assert (info);
+
+ /* Atomically obtain the token the thread is
+ * waiting on, and change it to a flag value. */
+
+ do {
+ previous_token = info->interrupt_token;
+
+ /* Already interrupted */
+ if (previous_token == INTERRUPT_STATE) {
+ token = NULL;
+ break;
+ }
+
+ token = previous_token;
+ } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
+
+ return token;
+}
+
+/*
+ * mono_thread_info_prepare_interrupt:
+ *
+ * The state of the thread info interrupt token is set to 'interrupted' which means that :
+ * - if the thread calls one of the WaitFor functions, the function will return with
+ * WAIT_IO_COMPLETION instead of waiting
+ * - if the thread was waiting when this function was called, the wait will be broken
+ *
+ * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
+ * didn't receive the interrupt signal yet, in this case it should call the wait function
+ * again. This essentially means that the target thread will busy wait until it is ready to
+ * process the interruption.
+ */
+MonoThreadInfoInterruptToken*
+mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
+{
+ MonoThreadInfoInterruptToken *token;
+
+ token = set_interrupt_state (info);
+
+ THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
+ mono_thread_info_get_tid (info), token);
+
+ return token;
}
void
-mono_thread_info_interrupt (HANDLE thread_handle)
+mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
{
- gpointer wait_handle;
+ THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
+
+ if (token == NULL)
+ return;
+
+ g_assert (token->callback);
- wait_handle = mono_thread_info_prepare_interrupt (thread_handle);
- mono_thread_info_finish_interrupt (wait_handle);
+ token->callback (token->data);
+
+ g_free (token);
}
-
+
void
mono_thread_info_self_interrupt (void)
{
- mono_threads_core_self_interrupt ();
+ MonoThreadInfo *info;
+ MonoThreadInfoInterruptToken *token;
+
+ info = mono_thread_info_current ();
+ g_assert (info);
+
+ token = set_interrupt_state (info);
+ g_assert (!token);
+
+ THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
+ mono_thread_info_get_tid (info));
}
+/* Clear the interrupted flag of the current thread, set with
+ * mono_thread_info_self_interrupt, so it can wait again */
void
-mono_thread_info_clear_interruption (void)
+mono_thread_info_clear_self_interrupt ()
{
- mono_threads_core_clear_interruption ();
+ MonoThreadInfo *info;
+ MonoThreadInfoInterruptToken *previous_token;
+
+ info = mono_thread_info_current ();
+ g_assert (info);
+
+ previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
+ g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
+
+ THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
+}
+
+gboolean
+mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
+{
+ g_assert (info);
+ return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
+}
+
+void
+mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
+{
+ g_assert (info);
+
+ if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
+ g_string_append_printf (text, "not waiting");
+ else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
+ g_string_append_printf (text, "interrupted state");
+ else
+ g_string_append_printf (text, "waiting");
}
/* info must be self or be held in a hazard pointer. */
{
MonoAsyncJob old_job;
do {
- old_job = info->service_requests;
+ old_job = (MonoAsyncJob) info->service_requests;
if (old_job & job)
return FALSE;
} while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
if (!info)
- return 0;
+ return (MonoAsyncJob) 0;
- return InterlockedExchange (&info->service_requests, 0);
+ return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);
}