[build] Skips RemoteExecuted bases tests on monodroid
[mono-project.git] / mono / utils / mono-threads-windows.c
blob1ddf9009eeab840869005ae3425386a81e1ac698
1 /**
2 * \file
3 * Low-level threading, windows version
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * (C) 2011 Novell, Inc
9 */
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>
18 #include <limits.h>
20 void
21 mono_threads_suspend_init (void)
25 gboolean
26 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
28 DWORD id = mono_thread_info_get_tid (info);
29 HANDLE handle;
30 DWORD result;
32 handle = info->native_handle;
33 g_assert (handle);
35 result = SuspendThread (handle);
36 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
37 if (result == (DWORD)-1) {
38 return FALSE;
41 /* Suspend logic assumes thread is really suspended before continuing below. Surprisingly SuspendThread */
42 /* is just an async request to the scheduler, meaning that the suspended thread can continue to run */
43 /* user mode code until scheduler gets around and process the request. This will cause a thread state race */
44 /* in mono's thread state machine implementation on Windows. By requesting a threads context after issuing a */
45 /* suspended request, this will wait until thread is suspended and thread context has been collected */
46 /* and returned. */
47 CONTEXT context;
48 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
49 if (!GetThreadContext (handle, &context)) {
50 return FALSE;
53 /* We're in the middle of a self-suspend, resume and register */
54 if (!mono_threads_transition_finish_async_suspend (info)) {
55 result = ResumeThread (handle);
56 g_assert (result == 1);
57 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
58 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
59 return TRUE;
61 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, &context);
62 THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
63 if (info->suspend_can_continue) {
64 if (interrupt_kernel)
65 mono_win32_interrupt_wait (info, handle, id);
66 } else {
67 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
70 return TRUE;
73 gboolean
74 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
76 return info->suspend_can_continue;
81 void
82 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
84 DWORD id = mono_thread_info_get_tid(info);
85 g_assert (info->native_handle);
86 mono_win32_abort_wait (info, info->native_handle, id);
89 gboolean
90 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
92 DWORD id = mono_thread_info_get_tid (info);
93 HANDLE handle;
94 DWORD result;
96 handle = info->native_handle;
97 g_assert (handle);
99 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
100 if (info->async_target) {
101 MonoContext ctx;
102 CONTEXT context;
103 gboolean res;
105 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
106 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
107 info->async_target = info->user_data = NULL;
109 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
111 if (!GetThreadContext (handle, &context)) {
112 return FALSE;
115 g_assert (context.ContextFlags & CONTEXT_INTEGER);
116 g_assert (context.ContextFlags & CONTEXT_CONTROL);
118 mono_monoctx_to_sigctx (&ctx, &context);
120 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
121 res = SetThreadContext (handle, &context);
122 if (!res) {
123 return FALSE;
126 #else
127 g_error ("Not implemented due to lack of SetThreadContext");
128 #endif
130 result = ResumeThread (handle);
132 return result != (DWORD)-1;
136 void
137 mono_threads_suspend_register (MonoThreadInfo *info)
139 BOOL success;
140 HANDLE currentThreadHandle = NULL;
142 success = DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), &currentThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
143 g_assertf (success, "Failed to duplicate current thread handle");
145 info->native_handle = currentThreadHandle;
148 void
149 mono_threads_suspend_free (MonoThreadInfo *info)
151 CloseHandle (info->native_handle);
152 info->native_handle = NULL;
155 void
156 mono_threads_suspend_init_signals (void)
160 gint
161 mono_threads_suspend_search_alternative_signal (void)
163 g_assert_not_reached ();
166 gint
167 mono_threads_suspend_get_suspend_signal (void)
169 return -1;
172 gint
173 mono_threads_suspend_get_restart_signal (void)
175 return -1;
178 gint
179 mono_threads_suspend_get_abort_signal (void)
181 return -1;
184 #endif
186 #if defined (HOST_WIN32)
188 gboolean
189 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
191 HANDLE result;
192 DWORD thread_id;
194 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
195 if (!result)
196 return FALSE;
198 /* A new handle is open when attaching
199 * the thread, so we don't need this one */
200 CloseHandle (result);
202 if (tid)
203 *tid = thread_id;
205 if (stack_size) {
206 // TOOD: Use VirtualQuery to get correct value
207 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
208 *stack_size = 2 * 1024 * 1024;
211 return TRUE;
215 MonoNativeThreadId
216 mono_native_thread_id_get (void)
218 return GetCurrentThreadId ();
221 gboolean
222 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
224 return id1 == id2;
227 gboolean
228 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
230 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
233 gboolean
234 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle)
236 DWORD res = WaitForSingleObject (thread_handle, INFINITE);
238 if (close_handle)
239 CloseHandle (thread_handle);
241 return res != WAIT_FAILED;
245 * Can't OpenThread on UWP until SDK 15063 (our minspec today is 10240),
246 * but this function doesn't seem to be used on Windows anyway
248 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
250 gboolean
251 mono_native_thread_join (MonoNativeThreadId tid)
253 HANDLE handle;
255 if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
256 return FALSE;
258 return mono_native_thread_join_handle (handle, TRUE);
261 #endif
263 #if HAVE_DECL___READFSDWORD==0
264 static MONO_ALWAYS_INLINE unsigned long long
265 __readfsdword (unsigned long offset)
267 unsigned long value;
268 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
269 __asm__ volatile ("movl %%fs:%1,%0"
270 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
271 return value;
273 #endif
275 void
276 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
278 MEMORY_BASIC_INFORMATION meminfo;
279 #if defined(_WIN64) || defined(_M_ARM)
280 /* win7 apis */
281 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
282 guint8 *stackTop = (guint8*)tib->StackBase;
283 guint8 *stackBottom = (guint8*)tib->StackLimit;
284 #else
285 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
286 void* tib = (void*)__readfsdword(0x18);
287 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
288 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
289 #endif
291 Windows stacks are expanded on demand, one page at time. The TIB reports
292 only the currently allocated amount.
293 VirtualQuery will return the actual limit for the bottom, which is what we want.
295 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
296 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
298 *staddr = stackBottom;
299 *stsize = stackTop - stackBottom;
303 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
304 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
305 static gboolean is_wow64 = FALSE;
306 #endif
308 /* We do this at init time to avoid potential races with module opening */
309 void
310 mono_threads_platform_init (void)
312 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
313 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
314 if (is_wow64_func)
315 is_wow64_func (GetCurrentProcess (), &is_wow64);
316 #endif
320 * When running x86 process under x64 system syscalls are done through WoW64. This
321 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
322 * Apparently this transition invalidates the ESP that we would get from calling
323 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
324 * to query whether the thread is in such a transition so we try to restart it later.
325 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
327 gboolean
328 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
330 gboolean ret = FALSE;
331 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
332 /* FIXME On cygwin these are not defined */
333 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
334 if (is_wow64) {
335 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
336 if (handle) {
337 CONTEXT context;
338 ZeroMemory (&context, sizeof (CONTEXT));
339 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
340 if (GetThreadContext (handle, &context)) {
341 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
342 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
343 ret = TRUE;
345 CloseHandle (handle);
348 #endif
349 #endif
350 return ret;
353 gboolean
354 mono_threads_platform_yield (void)
356 return SwitchToThread ();
359 void
360 mono_threads_platform_exit (gsize exit_code)
362 ExitThread (exit_code);
366 ves_icall_System_Threading_Thread_SystemMaxStackSize (MonoError *error)
368 //FIXME
369 return INT_MAX;
372 #if defined(_MSC_VER)
373 const DWORD MS_VC_EXCEPTION=0x406D1388;
374 #pragma pack(push,8)
375 typedef struct tagTHREADNAME_INFO
377 DWORD dwType; // Must be 0x1000.
378 LPCSTR szName; // Pointer to name (in user addr space).
379 DWORD dwThreadID; // Thread ID (-1=caller thread).
380 DWORD dwFlags; // Reserved for future use, must be zero.
381 } THREADNAME_INFO;
382 #pragma pack(pop)
383 #endif
385 void
386 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
388 #if defined(_MSC_VER)
389 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
390 THREADNAME_INFO info;
391 info.dwType = 0x1000;
392 info.szName = name;
393 info.dwThreadID = tid;
394 info.dwFlags = 0;
396 __try {
397 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
399 __except(EXCEPTION_EXECUTE_HANDLER) {
401 #endif
404 #endif