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
= info
->native_handle
;
35 result
= SuspendThread (handle
);
36 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id
, ret
);
37 if (result
== (DWORD
)-1) {
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 */
48 context
.ContextFlags
= CONTEXT_INTEGER
| CONTEXT_CONTROL
;
49 if (!GetThreadContext (handle
, &context
)) {
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
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
) {
65 mono_win32_interrupt_wait (info
, handle
, id
);
67 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info
->native_handle
, 0);
74 mono_threads_suspend_check_suspend_result (MonoThreadInfo
*info
)
76 return info
->suspend_can_continue
;
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
);
90 mono_threads_suspend_begin_async_resume (MonoThreadInfo
*info
)
92 DWORD id
= mono_thread_info_get_tid (info
);
96 handle
= info
->native_handle
;
99 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
100 if (info
->async_target
) {
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
)) {
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
);
127 g_error ("Not implemented due to lack of SetThreadContext");
130 result
= ResumeThread (handle
);
132 return result
!= (DWORD
)-1;
137 mono_threads_suspend_register (MonoThreadInfo
*info
)
140 HANDLE currentThreadHandle
= NULL
;
142 success
= DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), ¤tThreadHandle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
143 g_assertf (success
, "Failed to duplicate current thread handle");
145 info
->native_handle
= currentThreadHandle
;
149 mono_threads_suspend_free (MonoThreadInfo
*info
)
151 CloseHandle (info
->native_handle
);
152 info
->native_handle
= NULL
;
156 mono_threads_suspend_init_signals (void)
161 mono_threads_suspend_search_alternative_signal (void)
163 g_assert_not_reached ();
167 mono_threads_suspend_get_suspend_signal (void)
173 mono_threads_suspend_get_restart_signal (void)
179 mono_threads_suspend_get_abort_signal (void)
186 #if defined (HOST_WIN32)
189 mono_thread_platform_create_thread (MonoThreadStart thread_fn
, gpointer thread_data
, gsize
* const stack_size
, MonoNativeThreadId
*tid
)
194 result
= CreateThread (NULL
, stack_size
? *stack_size
: 0, (LPTHREAD_START_ROUTINE
) thread_fn
, thread_data
, 0, &thread_id
);
198 /* A new handle is open when attaching
199 * the thread, so we don't need this one */
200 CloseHandle (result
);
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;
216 mono_native_thread_id_get (void)
218 return GetCurrentThreadId ();
222 mono_native_thread_id_equals (MonoNativeThreadId id1
, MonoNativeThreadId id2
)
228 mono_native_thread_create (MonoNativeThreadId
*tid
, gpointer func
, gpointer arg
)
230 return CreateThread (NULL
, 0, (func
), (arg
), 0, (tid
)) != NULL
;
234 mono_native_thread_join_handle (HANDLE thread_handle
, gboolean close_handle
)
236 DWORD res
= WaitForSingleObject (thread_handle
, INFINITE
);
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)
251 mono_native_thread_join (MonoNativeThreadId tid
)
255 if (!(handle
= OpenThread (SYNCHRONIZE
, TRUE
, tid
)))
258 return mono_native_thread_join_handle (handle
, TRUE
);
263 #if HAVE_DECL___READFSDWORD==0
264 static MONO_ALWAYS_INLINE
unsigned long long
265 __readfsdword (unsigned long offset
)
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
)));
276 mono_threads_platform_get_stack_bounds (guint8
**staddr
, size_t *stsize
)
278 MEMORY_BASIC_INFORMATION meminfo
;
279 #if defined(_WIN64) || defined(_M_ARM)
281 NT_TIB
* tib
= (NT_TIB
*)NtCurrentTeb();
282 guint8
*stackTop
= (guint8
*)tib
->StackBase
;
283 guint8
*stackBottom
= (guint8
*)tib
->StackLimit
;
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);
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
;
308 /* We do this at init time to avoid potential races with module opening */
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");
315 is_wow64_func (GetCurrentProcess (), &is_wow64
);
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.
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)
335 HANDLE handle
= OpenThread (THREAD_ALL_ACCESS
, FALSE
, tid
);
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
))
345 CloseHandle (handle
);
354 mono_threads_platform_yield (void)
356 return SwitchToThread ();
360 mono_threads_platform_exit (gsize exit_code
)
362 ExitThread (exit_code
);
366 ves_icall_System_Threading_Thread_SystemMaxStackSize (MonoError
*error
)
372 #if defined(_MSC_VER)
373 const DWORD MS_VC_EXCEPTION
=0x406D1388;
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.
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;
393 info
.dwThreadID
= tid
;
397 RaiseException( MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
399 __except(EXCEPTION_EXECUTE_HANDLER
) {