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_handle (HANDLE thread_handle, gboolean close_handle)
224 DWORD res = WaitForSingleObject (thread_handle, INFINITE);
227 CloseHandle (thread_handle);
229 return res != WAIT_FAILED;
233 mono_native_thread_join (MonoNativeThreadId tid)
237 if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
240 return mono_native_thread_join_handle (handle, TRUE);
243 #if HAVE_DECL___READFSDWORD==0
244 static MONO_ALWAYS_INLINE unsigned long long
245 __readfsdword (unsigned long offset)
248 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
249 __asm__ volatile ("movl %%fs:%1,%0"
250 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
256 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
258 MEMORY_BASIC_INFORMATION meminfo;
261 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
262 guint8 *stackTop = (guint8*)tib->StackBase;
263 guint8 *stackBottom = (guint8*)tib->StackLimit;
265 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
266 void* tib = (void*)__readfsdword(0x18);
267 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
268 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
271 Windows stacks are expanded on demand, one page at time. The TIB reports
272 only the currently allocated amount.
273 VirtualQuery will return the actual limit for the bottom, which is what we want.
275 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
276 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
278 *staddr = stackBottom;
279 *stsize = stackTop - stackBottom;
283 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
284 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
285 static gboolean is_wow64 = FALSE;
288 /* We do this at init time to avoid potential races with module opening */
290 mono_threads_platform_init (void)
292 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
293 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
295 is_wow64_func (GetCurrentProcess (), &is_wow64);
300 * When running x86 process under x64 system syscalls are done through WoW64. This
301 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
302 * Apparently this transition invalidates the ESP that we would get from calling
303 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
304 * to query whether the thread is in such a transition so we try to restart it later.
305 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
308 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
310 gboolean ret = FALSE;
311 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
312 /* FIXME On cygwin these are not defined */
313 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
315 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
318 ZeroMemory (&context, sizeof (CONTEXT));
319 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
320 if (GetThreadContext (handle, &context)) {
321 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
322 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
325 CloseHandle (handle);
334 mono_threads_platform_yield (void)
336 return SwitchToThread ();
340 mono_threads_platform_exit (gsize exit_code)
342 ExitThread (exit_code);
346 mono_threads_get_max_stack_size (void)
352 #if defined(_MSC_VER)
353 const DWORD MS_VC_EXCEPTION=0x406D1388;
355 typedef struct tagTHREADNAME_INFO
357 DWORD dwType; // Must be 0x1000.
358 LPCSTR szName; // Pointer to name (in user addr space).
359 DWORD dwThreadID; // Thread ID (-1=caller thread).
360 DWORD dwFlags; // Reserved for future use, must be zero.
366 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
368 #if defined(_MSC_VER)
369 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
370 THREADNAME_INFO info;
371 info.dwType = 0x1000;
373 info.dwThreadID = tid;
377 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
379 __except(EXCEPTION_EXECUTE_HANDLER) {