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_begin_async_resume (MonoThreadInfo *info)
100 DWORD id = mono_thread_info_get_tid (info);
104 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
107 if (info->async_target) {
112 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
113 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
114 info->async_target = info->user_data = NULL;
116 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
118 if (!GetThreadContext (handle, &context)) {
119 CloseHandle (handle);
123 g_assert (context.ContextFlags & CONTEXT_INTEGER);
124 g_assert (context.ContextFlags & CONTEXT_CONTROL);
126 mono_monoctx_to_sigctx (&ctx, &context);
128 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
129 res = SetThreadContext (handle, &context);
131 CloseHandle (handle);
136 result = ResumeThread (handle);
137 CloseHandle (handle);
139 return result != (DWORD)-1;
144 mono_threads_suspend_register (MonoThreadInfo *info)
149 mono_threads_suspend_free (MonoThreadInfo *info)
154 mono_threads_suspend_init_signals (void)
159 mono_threads_suspend_search_alternative_signal (void)
161 g_assert_not_reached ();
165 mono_threads_suspend_get_suspend_signal (void)
171 mono_threads_suspend_get_restart_signal (void)
177 mono_threads_suspend_get_abort_signal (void)
184 #if defined (HOST_WIN32)
187 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
192 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
196 /* A new handle is open when attaching
197 * the thread, so we don't need this one */
198 CloseHandle (result);
204 // TOOD: Use VirtualQuery to get correct value
205 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
206 *stack_size = 2 * 1024 * 1024;
214 mono_native_thread_id_get (void)
216 return GetCurrentThreadId ();
220 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
226 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
228 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
232 mono_native_thread_join (MonoNativeThreadId tid)
236 if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
239 DWORD res = WaitForSingleObject (handle, INFINITE);
241 CloseHandle (handle);
243 return res != WAIT_FAILED;
246 #if HAVE_DECL___READFSDWORD==0
247 static MONO_ALWAYS_INLINE unsigned long long
248 __readfsdword (unsigned long offset)
251 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
252 __asm__ volatile ("movl %%fs:%1,%0"
253 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
259 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
261 MEMORY_BASIC_INFORMATION meminfo;
264 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
265 guint8 *stackTop = (guint8*)tib->StackBase;
266 guint8 *stackBottom = (guint8*)tib->StackLimit;
268 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
269 void* tib = (void*)__readfsdword(0x18);
270 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
271 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
274 Windows stacks are expanded on demand, one page at time. The TIB reports
275 only the currently allocated amount.
276 VirtualQuery will return the actual limit for the bottom, which is what we want.
278 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
279 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
281 *staddr = stackBottom;
282 *stsize = stackTop - stackBottom;
286 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
287 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
288 static gboolean is_wow64 = FALSE;
291 /* We do this at init time to avoid potential races with module opening */
293 mono_threads_platform_init (void)
295 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
296 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
298 is_wow64_func (GetCurrentProcess (), &is_wow64);
303 * When running x86 process under x64 system syscalls are done through WoW64. This
304 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
305 * Apparently this transition invalidates the ESP that we would get from calling
306 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
307 * to query whether the thread is in such a transition so we try to restart it later.
308 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
311 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
313 gboolean ret = FALSE;
314 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
315 /* FIXME On cygwin these are not defined */
316 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
318 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
321 ZeroMemory (&context, sizeof (CONTEXT));
322 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
323 if (GetThreadContext (handle, &context)) {
324 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
325 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
328 CloseHandle (handle);
337 mono_threads_platform_yield (void)
339 return SwitchToThread ();
343 mono_threads_platform_exit (gsize exit_code)
345 ExitThread (exit_code);
349 mono_threads_get_max_stack_size (void)
355 #if defined(_MSC_VER)
356 const DWORD MS_VC_EXCEPTION=0x406D1388;
358 typedef struct tagTHREADNAME_INFO
360 DWORD dwType; // Must be 0x1000.
361 LPCSTR szName; // Pointer to name (in user addr space).
362 DWORD dwThreadID; // Thread ID (-1=caller thread).
363 DWORD dwFlags; // Reserved for future use, must be zero.
369 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
371 #if defined(_MSC_VER)
372 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
373 THREADNAME_INFO info;
374 info.dwType = 0x1000;
376 info.dwThreadID = tid;
380 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
382 __except(EXCEPTION_EXECUTE_HANDLER) {