void 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 = (MonoThreadInfoInterruptToken *)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 inline void request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid) { /* * On Windows platforms, an async interrupt/abort request queues an APC * that needs to be processed by target thread before it can return from an * alertable OS wait call and complete the mono interrupt/abort request. * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete * This check makes sure that only one APC per type gets queued, preventing potential flooding * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in * an alertable wait or not. This is done to prevent races between interrupt/abort requests and * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION * return scenarios and restart the alertable wait operation if needed or take other actions * (like service the interrupt/abort request). */ MonoThreadInfo *info = (MonoThreadInfo *)thread_info; gint32 old_wait_info, new_wait_info; do { old_wait_info = mono_atomic_load_i32 (&info->thread_wait_info); if (old_wait_info & pending_apc_slot) return; new_wait_info = old_wait_info | pending_apc_slot; } while (mono_atomic_cas_i32 (&info->thread_wait_info, new_wait_info, old_wait_info) != old_wait_info); THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid); QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL); }
/* * 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_threads_suspend_abort_syscall (MonoThreadInfo *info) { DWORD id = mono_thread_info_get_tid (info); HANDLE handle; handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id); g_assert (handle); THREADS_INTERRUPT_DEBUG ("%06d - Aborting syscall in thread %06d", GetCurrentThreadId (), id); QueueUserAPC ((PAPCFUNC)abort_apc, handle, (ULONG_PTR)NULL); CloseHandle (handle); }
/* 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_self_interrupt () { MonoThreadInfo *info; MonoThreadInfoInterruptToken *previous_token; info = mono_thread_info_current (); g_assert (info); previous_token = (MonoThreadInfoInterruptToken *)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); }
void mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token) { THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token); if (token == NULL) return; g_assert (token->callback); token->callback (token->data); g_free (token); }
void mono_thread_info_self_interrupt (void) { 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)); }
/* * mono_thread_info_install_interrupt: install an interruption token for the current thread. * * - @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 */ void mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted) { 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 = (MonoThreadInfoInterruptToken *)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"); }
static void CALLBACK abort_apc (ULONG_PTR param) { THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ()); }