3 * Low-level threading, windows version
6 * Rodrigo Kumpera (kumpera@gmail.com)
11 #include <mono/utils/mono-threads.h>
13 #if defined(USE_WINDOWS_BACKEND)
15 #include <mono/utils/mono-compiler.h>
16 #include <mono/utils/mono-threads-debug.h>
21 mono_threads_suspend_init (void)
26 interrupt_apc (ULONG_PTR param)
31 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
33 DWORD id = mono_thread_info_get_tid (info);
37 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
40 result = SuspendThread (handle);
41 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
42 if (result == (DWORD)-1) {
47 /* We're in the middle of a self-suspend, resume and register */
48 if (!mono_threads_transition_finish_async_suspend (info)) {
49 mono_threads_add_to_pending_operation_set (info);
50 result = ResumeThread (handle);
51 g_assert (result == 1);
53 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
54 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
57 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
58 THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
59 if (info->suspend_can_continue) {
60 //FIXME do we need to QueueUserAPC on this case?
62 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
64 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
72 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
74 return info->suspend_can_continue;
78 abort_apc (ULONG_PTR param)
80 THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
84 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
86 DWORD id = mono_thread_info_get_tid (info);
89 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
92 THREADS_INTERRUPT_DEBUG ("%06d - Aborting syscall in thread %06d", GetCurrentThreadId (), id);
93 QueueUserAPC ((PAPCFUNC)abort_apc, handle, (ULONG_PTR)NULL);
99 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
101 DWORD id = mono_thread_info_get_tid (info);
105 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
108 if (info->async_target) {
113 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
114 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
115 info->async_target = info->user_data = NULL;
117 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
119 if (!GetThreadContext (handle, &context)) {
120 CloseHandle (handle);
124 g_assert (context.ContextFlags & CONTEXT_INTEGER);
125 g_assert (context.ContextFlags & CONTEXT_CONTROL);
127 mono_monoctx_to_sigctx (&ctx, &context);
129 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
130 res = SetThreadContext (handle, &context);
132 CloseHandle (handle);
137 result = ResumeThread (handle);
138 CloseHandle (handle);
140 return result != (DWORD)-1;
145 mono_threads_suspend_register (MonoThreadInfo *info)
150 mono_threads_suspend_free (MonoThreadInfo *info)
155 mono_threads_suspend_init_signals (void)
160 mono_threads_suspend_search_alternative_signal (void)
162 g_assert_not_reached ();
166 mono_threads_suspend_get_suspend_signal (void)
172 mono_threads_suspend_get_restart_signal (void)
178 mono_threads_suspend_get_abort_signal (void)
185 #if defined (HOST_WIN32)
188 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
193 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
197 /* A new handle is open when attaching
198 * the thread, so we don't need this one */
199 CloseHandle (result);
205 // TOOD: Use VirtualQuery to get correct value
206 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
207 *stack_size = 2 * 1024 * 1024;
215 mono_native_thread_id_get (void)
217 return GetCurrentThreadId ();
221 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
227 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
229 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
233 mono_native_thread_join (MonoNativeThreadId tid)
237 if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
240 DWORD res = WaitForSingleObject (handle, INFINITE);
242 CloseHandle (handle);
244 return res != WAIT_FAILED;
247 #if HAVE_DECL___READFSDWORD==0
248 static MONO_ALWAYS_INLINE unsigned long long
249 __readfsdword (unsigned long offset)
252 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
253 __asm__ volatile ("movl %%fs:%1,%0"
254 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
260 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
262 MEMORY_BASIC_INFORMATION meminfo;
265 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
266 guint8 *stackTop = (guint8*)tib->StackBase;
267 guint8 *stackBottom = (guint8*)tib->StackLimit;
269 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
270 void* tib = (void*)__readfsdword(0x18);
271 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
272 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
275 Windows stacks are expanded on demand, one page at time. The TIB reports
276 only the currently allocated amount.
277 VirtualQuery will return the actual limit for the bottom, which is what we want.
279 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
280 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
282 *staddr = stackBottom;
283 *stsize = stackTop - stackBottom;
287 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
288 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
289 static gboolean is_wow64 = FALSE;
292 /* We do this at init time to avoid potential races with module opening */
294 mono_threads_platform_init (void)
296 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
297 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
299 is_wow64_func (GetCurrentProcess (), &is_wow64);
304 * When running x86 process under x64 system syscalls are done through WoW64. This
305 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
306 * Apparently this transition invalidates the ESP that we would get from calling
307 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
308 * to query whether the thread is in such a transition so we try to restart it later.
309 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
312 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
314 gboolean ret = FALSE;
315 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
316 /* FIXME On cygwin these are not defined */
317 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
319 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
322 ZeroMemory (&context, sizeof (CONTEXT));
323 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
324 if (GetThreadContext (handle, &context)) {
325 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
326 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
329 CloseHandle (handle);
338 mono_threads_platform_yield (void)
340 return SwitchToThread ();
344 mono_threads_platform_exit (gsize exit_code)
346 ExitThread (exit_code);
350 mono_threads_get_max_stack_size (void)
356 #if defined(_MSC_VER)
357 const DWORD MS_VC_EXCEPTION=0x406D1388;
359 typedef struct tagTHREADNAME_INFO
361 DWORD dwType; // Must be 0x1000.
362 LPCSTR szName; // Pointer to name (in user addr space).
363 DWORD dwThreadID; // Thread ID (-1=caller thread).
364 DWORD dwFlags; // Reserved for future use, must be zero.
370 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
372 #if defined(_MSC_VER)
373 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
374 THREADNAME_INFO info;
375 info.dwType = 0x1000;
377 info.dwThreadID = tid;
381 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
383 __except(EXCEPTION_EXECUTE_HANDLER) {