More disambiguation of Python in makefiles (#18284)
[mono-project.git] / mono / utils / mono-threads-windows.c
bloba3ec0e9592ebf42191469b3bb084b7d499cc3c8c
1 /**
2 * \file
3 * Low-level threading, windows version
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * (C) 2011 Novell, Inc
9 */
11 #include <mono/utils/mono-threads.h>
13 #if defined(USE_WINDOWS_BACKEND)
15 #include <glib.h>
16 #include <mono/utils/mono-compiler.h>
17 #include <mono/utils/mono-threads-coop.h>
18 #include <mono/utils/mono-threads-debug.h>
19 #include <mono/utils/mono-os-wait.h>
20 #include <mono/metadata/w32subset.h>
21 #include <limits.h>
23 enum Win32APCInfo {
24 WIN32_APC_INFO_CLEARED = 0,
25 WIN32_APC_INFO_ALERTABLE_WAIT_SLOT = 1 << 0,
26 WIN32_APC_INFO_BLOCKING_IO_SLOT = 1 << 1,
27 WIN32_APC_INFO_PENDING_INTERRUPT_SLOT = 1 << 2,
28 WIN32_APC_INFO_PENDING_ABORT_SLOT = 1 << 3
31 static void
32 request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
35 * On Windows platforms, an async interrupt/abort request queues an APC
36 * that needs to be processed by target thread before it can return from an
37 * alertable OS wait call and complete the mono interrupt/abort request.
38 * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
39 * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete.
40 * This check makes sure that only one APC per type gets queued, preventing potential flooding
41 * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
42 * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
43 * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
44 * return scenarios and restart the alertable wait operation if needed or take other actions
45 * (like service the interrupt/abort request).
47 MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
48 gint32 old_apc_info, new_apc_info;
50 do {
51 old_apc_info = mono_atomic_load_i32 (&info->win32_apc_info);
52 if (old_apc_info & pending_apc_slot)
53 return;
55 new_apc_info = old_apc_info | pending_apc_slot;
56 } while (mono_atomic_cas_i32 (&info->win32_apc_info, new_apc_info, old_apc_info) != old_apc_info);
58 THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
59 QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
62 static void CALLBACK
63 interrupt_apc (ULONG_PTR param)
65 THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
68 void
69 mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
71 request_interrupt (thread_info, native_thread_handle, WIN32_APC_INFO_PENDING_INTERRUPT_SLOT, interrupt_apc, tid);
74 static void CALLBACK
75 abort_apc (ULONG_PTR param)
77 THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
79 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
80 if (info) {
81 // Check if pending interrupt is still relevant and current thread has not left alertable wait region.
82 // NOTE, can only be reset by current thread, currently running this APC.
83 gint32 win32_apc_info = mono_atomic_load_i32 (&info->win32_apc_info);
84 if (win32_apc_info & WIN32_APC_INFO_BLOCKING_IO_SLOT) {
85 // Check if current thread registered an IO handle when entering alertable wait (blocking IO call).
86 // No need for CAS on win32_apc_info_io_handle since its only loaded/stored by current thread
87 // currently running APC.
88 HANDLE io_handle = (HANDLE)info->win32_apc_info_io_handle;
89 if (io_handle != INVALID_HANDLE_VALUE) {
90 // In order to break IO waits, cancel all outstanding IO requests.
91 // Start to cancel IO requests for the registered IO handle issued by current thread.
92 // NOTE, this is NOT a blocking call.
93 CancelIo (io_handle);
99 // Attempt to cancel sync blocking IO on abort syscall requests.
100 // NOTE, the effect of the canceled IO operation is unknown so the caller need
101 // to close used resources (file, socket) to get back to a known state. The need
102 // to abort blocking IO calls is normally part of doing a thread abort, then the
103 // thread is going away meaning that no more IO calls will be issued against the
104 // same resource that was part of the cancelation. Current implementation of
105 // .NET Framework and .NET Core currently don't support the ability to abort a thread
106 // blocked on sync IO calls, see https://github.com/dotnet/corefx/issues/5749.
107 // Since there is no solution covering all scenarios aborting blocking syscall this
108 // will be on best effort and there might still be a slight risk that the blocking call
109 // won't abort (depending on overlapped IO support for current file, socket).
110 static void
111 suspend_abort_syscall (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
113 request_interrupt (thread_info, native_thread_handle, WIN32_APC_INFO_PENDING_ABORT_SLOT, abort_apc, tid);
116 static void
117 enter_alertable_wait_ex (MonoThreadInfo *info, HANDLE io_handle)
119 // Only loaded/stored by current thread, here or in APC (also running on current thread).
120 g_assert (info->win32_apc_info_io_handle == (gpointer)INVALID_HANDLE_VALUE);
121 info->win32_apc_info_io_handle = io_handle;
123 //Set alertable wait flag.
124 mono_atomic_xchg_i32 (&info->win32_apc_info, (io_handle == INVALID_HANDLE_VALUE) ? WIN32_APC_INFO_ALERTABLE_WAIT_SLOT : WIN32_APC_INFO_BLOCKING_IO_SLOT);
127 static void
128 leave_alertable_wait_ex (MonoThreadInfo *info, HANDLE io_handle)
130 // Clear any previous flags. Thread is exiting alertable wait region, and info around pending interrupt/abort APC's
131 // can now be discarded, thread is out of wait operation and can proceed execution.
132 mono_atomic_xchg_i32 (&info->win32_apc_info, WIN32_APC_INFO_CLEARED);
134 // Only loaded/stored by current thread, here or in APC (also running on current thread).
135 g_assert (info->win32_apc_info_io_handle == io_handle);
136 info->win32_apc_info_io_handle = (gpointer)INVALID_HANDLE_VALUE;
139 void
140 mono_win32_enter_alertable_wait (THREAD_INFO_TYPE *info)
142 if (info)
143 enter_alertable_wait_ex (info, INVALID_HANDLE_VALUE);
146 void
147 mono_win32_leave_alertable_wait (THREAD_INFO_TYPE *info)
149 if (info)
150 leave_alertable_wait_ex (info, INVALID_HANDLE_VALUE);
153 void
154 mono_win32_enter_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle)
156 if (info)
157 enter_alertable_wait_ex (info, io_handle);
160 void
161 mono_win32_leave_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle)
163 if (info)
164 leave_alertable_wait_ex (info, io_handle);
167 void
168 mono_threads_suspend_init (void)
172 gboolean
173 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
175 DWORD id = mono_thread_info_get_tid (info);
176 HANDLE handle;
177 DWORD result;
179 handle = info->native_handle;
180 g_assert (handle);
182 result = SuspendThread (handle);
183 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %u\n", GUINT_TO_POINTER (id), result);
184 if (result == (DWORD)-1) {
185 if (!mono_threads_transition_abort_async_suspend (info)) {
186 /* We raced with self suspend and lost so suspend can continue. */
187 g_assert (mono_threads_is_hybrid_suspension_enabled ());
188 info->suspend_can_continue = TRUE;
189 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (info));
190 return TRUE;
192 THREADS_SUSPEND_DEBUG ("SUSPEND FAILED, id=%p, err=%u\n", GUINT_TO_POINTER (id), GetLastError ());
193 return FALSE;
196 /* Suspend logic assumes thread is really suspended before continuing below. Surprisingly SuspendThread */
197 /* is just an async request to the scheduler, meaning that the suspended thread can continue to run */
198 /* user mode code until scheduler gets around and process the request. This will cause a thread state race */
199 /* in mono's thread state machine implementation on Windows. By requesting a threads context after issuing a */
200 /* suspended request, this will wait until thread is suspended and thread context has been collected */
201 /* and returned. */
202 CONTEXT context;
203 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
204 if (!GetThreadContext (handle, &context)) {
205 result = ResumeThread (handle);
206 g_assert (result == 1);
207 if (!mono_threads_transition_abort_async_suspend (info)) {
208 /* We raced with self suspend and lost so suspend can continue. */
209 g_assert (mono_threads_is_hybrid_suspension_enabled ());
210 info->suspend_can_continue = TRUE;
211 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (info));
212 return TRUE;
214 THREADS_SUSPEND_DEBUG ("SUSPEND FAILED (GetThreadContext), id=%p, err=%u\n", GUINT_TO_POINTER (id), GetLastError ());
215 return FALSE;
218 if (!mono_threads_transition_finish_async_suspend (info)) {
219 /* We raced with self-suspend and lost. Resume the native
220 * thread. It is still self-suspended, waiting to be resumed.
221 * So suspend can continue.
223 result = ResumeThread (handle);
224 g_assert (result == 1);
225 info->suspend_can_continue = TRUE;
226 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", GUINT_TO_POINTER (id));
227 g_assert (mono_threads_is_hybrid_suspension_enabled ());
228 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
229 return TRUE;
231 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, &context);
232 THREADS_SUSPEND_DEBUG ("thread state %p -> %u\n", GUINT_TO_POINTER (id), result);
233 if (info->suspend_can_continue) {
234 if (interrupt_kernel)
235 suspend_abort_syscall (info, handle, id);
236 } else {
237 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %u\n", GUINT_TO_POINTER (id), 0);
240 return TRUE;
243 gboolean
244 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
246 return info->suspend_can_continue;
249 void
250 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
252 DWORD id = mono_thread_info_get_tid(info);
253 g_assert (info->native_handle);
254 suspend_abort_syscall (info, info->native_handle, id);
257 void
258 mono_win32_abort_blocking_io_call (MonoThreadInfo *info)
260 #if HAVE_API_SUPPORT_WIN32_CANCEL_SYNCHRONOUS_IO
261 // In case thread is blocked on sync IO preventing it from running above queued APC, cancel
262 // all outputstanding sync IO for target thread. If its not blocked on a sync IO request, below
263 // call will just fail and nothing will be canceled. If thread is waiting on overlapped IO,
264 // the queued APC will take care of cancel specific outstanding IO requests.
265 gint32 win32_apc_info = mono_atomic_load_i32 (&info->win32_apc_info);
266 if (win32_apc_info & WIN32_APC_INFO_BLOCKING_IO_SLOT) {
267 CancelSynchronousIo (info->native_handle);
269 #endif
272 gboolean
273 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
275 DWORD id = mono_thread_info_get_tid (info);
276 HANDLE handle;
277 DWORD result;
279 handle = info->native_handle;
280 g_assert (handle);
282 if (info->async_target) {
283 #if HAVE_API_SUPPORT_WIN32_SET_THREAD_CONTEXT
284 MonoContext ctx;
285 CONTEXT context;
286 gboolean res;
288 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
289 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
290 info->async_target = NULL;
291 info->user_data = NULL;
293 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
295 if (!GetThreadContext (handle, &context)) {
296 THREADS_SUSPEND_DEBUG ("RESUME FAILED (GetThreadContext), id=%p, err=%u\n", GUINT_TO_POINTER (id), GetLastError ());
297 return FALSE;
300 g_assert (context.ContextFlags & CONTEXT_INTEGER);
301 g_assert (context.ContextFlags & CONTEXT_CONTROL);
303 mono_monoctx_to_sigctx (&ctx, &context);
305 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
306 res = SetThreadContext (handle, &context);
307 if (!res) {
308 THREADS_SUSPEND_DEBUG ("RESUME FAILED (SetThreadContext), id=%p, err=%u\n", GUINT_TO_POINTER (id), GetLastError ());
309 return FALSE;
311 #else
312 g_error ("Not implemented due to lack of SetThreadContext");
313 #endif
316 result = ResumeThread (handle);
317 THREADS_SUSPEND_DEBUG ("RESUME %p -> %u\n", GUINT_TO_POINTER (id), result);
319 return result != (DWORD)-1;
323 void
324 mono_threads_suspend_register (MonoThreadInfo *info)
326 g_assert (!info->native_handle);
327 info->native_handle = mono_threads_open_native_thread_handle (GetCurrentThread ());
330 void
331 mono_threads_suspend_free (MonoThreadInfo *info)
333 mono_threads_close_native_thread_handle (info->native_handle);
334 info->native_handle = NULL;
337 void
338 mono_threads_suspend_init_signals (void)
342 gint
343 mono_threads_suspend_search_alternative_signal (void)
345 g_assert_not_reached ();
348 gint
349 mono_threads_suspend_get_suspend_signal (void)
351 return -1;
354 gint
355 mono_threads_suspend_get_restart_signal (void)
357 return -1;
360 gint
361 mono_threads_suspend_get_abort_signal (void)
363 return -1;
366 #endif
368 #if defined (HOST_WIN32)
370 #define MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE (1024 * 1024)
372 gboolean
373 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
375 HANDLE result;
376 DWORD thread_id;
377 gsize set_stack_size = MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE;
379 if (stack_size && *stack_size)
380 set_stack_size = *stack_size;
382 result = CreateThread (NULL, set_stack_size, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
383 if (!result)
384 return FALSE;
386 /* A new handle is open when attaching
387 * the thread, so we don't need this one */
388 CloseHandle (result);
390 if (tid)
391 *tid = thread_id;
393 if (stack_size) {
394 // TOOD: Use VirtualQuery to get correct value
395 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
396 *stack_size = set_stack_size;
399 return TRUE;
403 MonoNativeThreadId
404 mono_native_thread_id_get (void)
406 return GetCurrentThreadId ();
409 guint64
410 mono_native_thread_os_id_get (void)
412 return (guint64)GetCurrentThreadId ();
415 gboolean
416 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
418 return id1 == id2;
421 gboolean
422 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
424 return CreateThread (NULL, MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE, (LPTHREAD_START_ROUTINE)func, arg, 0, tid) != NULL;
427 gboolean
428 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle)
430 DWORD res = WaitForSingleObject (thread_handle, INFINITE);
432 if (close_handle)
433 CloseHandle (thread_handle);
435 return res != WAIT_FAILED;
439 * Can't OpenThread on UWP until SDK 15063 (our minspec today is 10240),
440 * but this function doesn't seem to be used on Windows anyway
442 #if HAVE_API_SUPPORT_WIN32_OPEN_THREAD
443 gboolean
444 mono_native_thread_join (MonoNativeThreadId tid)
446 HANDLE handle;
448 if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
449 return FALSE;
451 return mono_native_thread_join_handle (handle, TRUE);
453 #endif
455 void
456 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
458 #if _WIN32_WINNT >= 0x0602 // Windows 8 or newer and very fast, just a few instructions, no syscall.
459 ULONG_PTR low;
460 ULONG_PTR high;
461 GetCurrentThreadStackLimits (&low, &high);
462 *staddr = (guint8*)low;
463 *stsize = high - low;
464 #else // Win7 and older (or newer, still works, but much slower).
465 MEMORY_BASIC_INFORMATION info;
466 // Windows stacks are commited on demand, one page at time.
467 // teb->StackBase is the top from which it grows down.
468 // teb->StackLimit is commited, the lowest it has gone so far.
469 // info.AllocationBase is reserved, the lowest it can go.
471 VirtualQuery (&info, &info, sizeof (info));
472 *staddr = (guint8*)info.AllocationBase;
473 // TEB starts with TIB. TIB is public, TEB is not.
474 *stsize = (size_t)((NT_TIB*)NtCurrentTeb ())->StackBase - (size_t)info.AllocationBase;
475 #endif
478 #if SIZEOF_VOID_P == 4 && HAVE_API_SUPPORT_WIN32_IS_WOW64_PROCESS
479 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
480 static gboolean is_wow64 = FALSE;
481 #endif
483 /* We do this at init time to avoid potential races with module opening */
484 void
485 mono_threads_platform_init (void)
487 #if SIZEOF_VOID_P == 4 && HAVE_API_SUPPORT_WIN32_IS_WOW64_PROCESS
488 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
489 if (is_wow64_func)
490 is_wow64_func (GetCurrentProcess (), &is_wow64);
491 #endif
494 static gboolean
495 thread_is_cooperative_suspend_aware (MonoThreadInfo *info)
497 return (mono_threads_is_cooperative_suspension_enabled () || mono_atomic_load_i32 (&(info->coop_aware_thread)));
501 * When running x86 process under x64 system syscalls are done through WoW64. This
502 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
503 * Apparently this transition invalidates the ESP that we would get from calling
504 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
505 * to query whether the thread is in such a transition so we try to restart it later.
506 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
508 gboolean
509 mono_threads_platform_in_critical_region (THREAD_INFO_TYPE *info)
511 gboolean ret = FALSE;
512 #if SIZEOF_VOID_P == 4 && HAVE_API_SUPPORT_WIN32_OPEN_THREAD
513 /* FIXME On cygwin these are not defined */
514 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
515 if (is_wow64 && thread_is_cooperative_suspend_aware (info)) {
516 /* Cooperative suspended threads will block at well-defined locations. */
517 return FALSE;
518 } else if (is_wow64 && mono_threads_is_hybrid_suspension_enabled ()) {
519 /* If thread is cooperative suspended, we shouldn't validate context */
520 /* since thread could still be running towards it's wait state. Calling */
521 /* GetThreadContext on a running thread (before calling SuspendThread) */
522 /* could return incorrect results and since cooperative suspended threads */
523 /* will block at well defined-locations, no need to do so. */
524 int thread_state = mono_thread_info_current_state (info);
525 if (thread_state == STATE_SELF_SUSPENDED || thread_state == STATE_BLOCKING_SELF_SUSPENDED)
526 return FALSE;
529 if (is_wow64) {
530 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, mono_thread_info_get_tid (info));
531 if (handle) {
532 CONTEXT context;
533 ZeroMemory (&context, sizeof (CONTEXT));
534 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
535 if (GetThreadContext (handle, &context)) {
536 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
537 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
538 ret = TRUE;
540 CloseHandle (handle);
543 #endif
544 #endif
545 return ret;
548 gboolean
549 mono_threads_platform_yield (void)
551 return SwitchToThread ();
554 void
555 mono_threads_platform_exit (gsize exit_code)
557 ExitThread (exit_code);
561 mono_thread_info_get_system_max_stack_size (void)
563 //FIXME
564 return INT_MAX;
567 void
568 mono_memory_barrier_process_wide (void)
570 FlushProcessWriteBuffers ();
573 #else
575 #include <mono/utils/mono-compiler.h>
577 MONO_EMPTY_SOURCE_FILE (mono_threads_windows);
579 #endif