2 * mono-threads-windows.c: Low-level threading, windows version
5 * Rodrigo Kumpera (kumpera@gmail.com)
10 #include <mono/utils/mono-threads.h>
12 #if defined(USE_WINDOWS_BACKEND)
14 #include <mono/utils/mono-compiler.h>
15 #include <mono/utils/mono-threads-debug.h>
20 mono_threads_suspend_init (void)
25 interrupt_apc (ULONG_PTR param)
30 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
32 DWORD id = mono_thread_info_get_tid (info);
36 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
39 result = SuspendThread (handle);
40 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
41 if (result == (DWORD)-1) {
46 /* We're in the middle of a self-suspend, resume and register */
47 if (!mono_threads_transition_finish_async_suspend (info)) {
48 mono_threads_add_to_pending_operation_set (info);
49 result = ResumeThread (handle);
50 g_assert (result == 1);
52 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
53 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
56 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
57 THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
58 if (info->suspend_can_continue) {
59 //FIXME do we need to QueueUserAPC on this case?
61 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
63 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
71 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
73 return info->suspend_can_continue;
77 abort_apc (ULONG_PTR param)
79 THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
83 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
85 DWORD id = mono_thread_info_get_tid (info);
88 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
91 THREADS_INTERRUPT_DEBUG ("%06d - Aborting syscall in thread %06d", GetCurrentThreadId (), id);
92 QueueUserAPC ((PAPCFUNC)abort_apc, handle, (ULONG_PTR)NULL);
98 mono_threads_suspend_needs_abort_syscall (void)
104 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
106 DWORD id = mono_thread_info_get_tid (info);
110 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
113 if (info->async_target) {
118 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
119 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
120 info->async_target = info->user_data = NULL;
122 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
124 if (!GetThreadContext (handle, &context)) {
125 CloseHandle (handle);
129 g_assert (context.ContextFlags & CONTEXT_INTEGER);
130 g_assert (context.ContextFlags & CONTEXT_CONTROL);
132 mono_monoctx_to_sigctx (&ctx, &context);
134 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
135 res = SetThreadContext (handle, &context);
137 CloseHandle (handle);
142 result = ResumeThread (handle);
143 CloseHandle (handle);
145 return result != (DWORD)-1;
150 mono_threads_suspend_register (MonoThreadInfo *info)
155 mono_threads_suspend_free (MonoThreadInfo *info)
160 mono_threads_suspend_init_signals (void)
165 mono_threads_suspend_search_alternative_signal (void)
167 g_assert_not_reached ();
171 mono_threads_suspend_get_suspend_signal (void)
177 mono_threads_suspend_get_restart_signal (void)
183 mono_threads_suspend_get_abort_signal (void)
190 #if defined (HOST_WIN32)
193 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid)
198 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
202 /* A new handle is open when attaching
203 * the thread, so we don't need this one */
204 CloseHandle (result);
207 *out_tid = thread_id;
210 // TOOD: Use VirtualQuery to get correct value
211 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
212 *stack_size = 2 * 1024 * 1024;
220 mono_native_thread_id_get (void)
222 return GetCurrentThreadId ();
226 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
232 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
234 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
238 mono_native_thread_join (MonoNativeThreadId tid)
242 if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
245 DWORD res = WaitForSingleObject (handle, INFINITE);
247 CloseHandle (handle);
249 return res != WAIT_FAILED;
252 #if HAVE_DECL___READFSDWORD==0
253 static MONO_ALWAYS_INLINE unsigned long long
254 __readfsdword (unsigned long offset)
257 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
258 __asm__ volatile ("movl %%fs:%1,%0"
259 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
265 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
267 MEMORY_BASIC_INFORMATION meminfo;
270 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
271 guint8 *stackTop = (guint8*)tib->StackBase;
272 guint8 *stackBottom = (guint8*)tib->StackLimit;
274 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
275 void* tib = (void*)__readfsdword(0x18);
276 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
277 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
280 Windows stacks are expanded on demand, one page at time. The TIB reports
281 only the currently allocated amount.
282 VirtualQuery will return the actual limit for the bottom, which is what we want.
284 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
285 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
287 *staddr = stackBottom;
288 *stsize = stackTop - stackBottom;
292 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
293 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
294 static gboolean is_wow64 = FALSE;
297 /* We do this at init time to avoid potential races with module opening */
299 mono_threads_platform_init (void)
301 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
302 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
304 is_wow64_func (GetCurrentProcess (), &is_wow64);
309 * When running x86 process under x64 system syscalls are done through WoW64. This
310 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
311 * Apparently this transition invalidates the ESP that we would get from calling
312 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
313 * to query whether the thread is in such a transition so we try to restart it later.
314 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
317 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
319 gboolean ret = FALSE;
320 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
321 /* FIXME On cygwin these are not defined */
322 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
324 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
327 ZeroMemory (&context, sizeof (CONTEXT));
328 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
329 if (GetThreadContext (handle, &context)) {
330 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
331 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
334 CloseHandle (handle);
343 mono_threads_platform_yield (void)
345 return SwitchToThread ();
349 mono_threads_platform_exit (gsize exit_code)
351 ExitThread (exit_code);
355 mono_threads_get_max_stack_size (void)
361 #if defined(_MSC_VER)
362 const DWORD MS_VC_EXCEPTION=0x406D1388;
364 typedef struct tagTHREADNAME_INFO
366 DWORD dwType; // Must be 0x1000.
367 LPCSTR szName; // Pointer to name (in user addr space).
368 DWORD dwThreadID; // Thread ID (-1=caller thread).
369 DWORD dwFlags; // Reserved for future use, must be zero.
375 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
377 #if defined(_MSC_VER)
378 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
379 THREADNAME_INFO info;
380 info.dwType = 0x1000;
382 info.dwThreadID = tid;
386 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
388 __except(EXCEPTION_EXECUTE_HANDLER) {