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>
17 #include <mono/utils/mono-os-wait.h>
21 mono_threads_suspend_init (void)
26 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
28 DWORD id = mono_thread_info_get_tid (info);
32 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
35 result = SuspendThread (handle);
36 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
37 if (result == (DWORD)-1) {
42 /* We're in the middle of a self-suspend, resume and register */
43 if (!mono_threads_transition_finish_async_suspend (info)) {
44 mono_threads_add_to_pending_operation_set (info);
45 result = ResumeThread (handle);
46 g_assert (result == 1);
48 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
49 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
52 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
53 THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
54 if (info->suspend_can_continue) {
56 mono_win32_interrupt_wait (info, handle, id);
58 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
66 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
68 return info->suspend_can_continue;
74 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
76 DWORD id = mono_thread_info_get_tid (info);
79 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
82 mono_win32_abort_wait (info, handle, id);
88 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
90 DWORD id = mono_thread_info_get_tid (info);
94 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
97 if (info->async_target) {
102 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
103 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
104 info->async_target = info->user_data = NULL;
106 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
108 if (!GetThreadContext (handle, &context)) {
109 CloseHandle (handle);
113 g_assert (context.ContextFlags & CONTEXT_INTEGER);
114 g_assert (context.ContextFlags & CONTEXT_CONTROL);
116 mono_monoctx_to_sigctx (&ctx, &context);
118 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
119 res = SetThreadContext (handle, &context);
121 CloseHandle (handle);
126 result = ResumeThread (handle);
127 CloseHandle (handle);
129 return result != (DWORD)-1;
134 mono_threads_suspend_register (MonoThreadInfo *info)
139 mono_threads_suspend_free (MonoThreadInfo *info)
144 mono_threads_suspend_init_signals (void)
149 mono_threads_suspend_search_alternative_signal (void)
151 g_assert_not_reached ();
155 mono_threads_suspend_get_suspend_signal (void)
161 mono_threads_suspend_get_restart_signal (void)
167 mono_threads_suspend_get_abort_signal (void)
174 #if defined (HOST_WIN32)
177 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
182 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
186 /* A new handle is open when attaching
187 * the thread, so we don't need this one */
188 CloseHandle (result);
194 // TOOD: Use VirtualQuery to get correct value
195 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
196 *stack_size = 2 * 1024 * 1024;
204 mono_native_thread_id_get (void)
206 return GetCurrentThreadId ();
210 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
216 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
218 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
222 mono_native_thread_join (MonoNativeThreadId tid)
226 if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
229 DWORD res = WaitForSingleObject (handle, INFINITE);
231 CloseHandle (handle);
233 return res != WAIT_FAILED;
236 #if HAVE_DECL___READFSDWORD==0
237 static MONO_ALWAYS_INLINE unsigned long long
238 __readfsdword (unsigned long offset)
241 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
242 __asm__ volatile ("movl %%fs:%1,%0"
243 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
249 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
251 MEMORY_BASIC_INFORMATION meminfo;
254 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
255 guint8 *stackTop = (guint8*)tib->StackBase;
256 guint8 *stackBottom = (guint8*)tib->StackLimit;
258 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
259 void* tib = (void*)__readfsdword(0x18);
260 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
261 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
264 Windows stacks are expanded on demand, one page at time. The TIB reports
265 only the currently allocated amount.
266 VirtualQuery will return the actual limit for the bottom, which is what we want.
268 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
269 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
271 *staddr = stackBottom;
272 *stsize = stackTop - stackBottom;
276 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
277 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
278 static gboolean is_wow64 = FALSE;
281 /* We do this at init time to avoid potential races with module opening */
283 mono_threads_platform_init (void)
285 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
286 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
288 is_wow64_func (GetCurrentProcess (), &is_wow64);
293 * When running x86 process under x64 system syscalls are done through WoW64. This
294 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
295 * Apparently this transition invalidates the ESP that we would get from calling
296 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
297 * to query whether the thread is in such a transition so we try to restart it later.
298 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
301 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
303 gboolean ret = FALSE;
304 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
305 /* FIXME On cygwin these are not defined */
306 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
308 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
311 ZeroMemory (&context, sizeof (CONTEXT));
312 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
313 if (GetThreadContext (handle, &context)) {
314 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
315 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
318 CloseHandle (handle);
327 mono_threads_platform_yield (void)
329 return SwitchToThread ();
333 mono_threads_platform_exit (gsize exit_code)
335 ExitThread (exit_code);
339 mono_threads_get_max_stack_size (void)
345 #if defined(_MSC_VER)
346 const DWORD MS_VC_EXCEPTION=0x406D1388;
348 typedef struct tagTHREADNAME_INFO
350 DWORD dwType; // Must be 0x1000.
351 LPCSTR szName; // Pointer to name (in user addr space).
352 DWORD dwThreadID; // Thread ID (-1=caller thread).
353 DWORD dwFlags; // Reserved for future use, must be zero.
359 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
361 #if defined(_MSC_VER)
362 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
363 THREADNAME_INFO info;
364 info.dwType = 0x1000;
366 info.dwThreadID = tid;
370 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
372 __except(EXCEPTION_EXECUTE_HANDLER) {