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_needs_abort_syscall (void)
104 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
106 DWORD id = mono_thread_info_get_tid (info);
110 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
113 if (info->async_target) {
118 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
119 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
120 info->async_target = info->user_data = NULL;
122 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
124 if (!GetThreadContext (handle, &context)) {
125 CloseHandle (handle);
129 g_assert (context.ContextFlags & CONTEXT_INTEGER);
130 g_assert (context.ContextFlags & CONTEXT_CONTROL);
132 mono_monoctx_to_sigctx (&ctx, &context);
134 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
135 res = SetThreadContext (handle, &context);
137 CloseHandle (handle);
142 result = ResumeThread (handle);
143 CloseHandle (handle);
145 return result != (DWORD)-1;
150 mono_threads_suspend_register (MonoThreadInfo *info)
155 mono_threads_suspend_free (MonoThreadInfo *info)
160 mono_threads_suspend_init_signals (void)
165 mono_threads_suspend_search_alternative_signal (void)
167 g_assert_not_reached ();
171 mono_threads_suspend_get_suspend_signal (void)
177 mono_threads_suspend_get_restart_signal (void)
183 mono_threads_suspend_get_abort_signal (void)
190 #if defined (HOST_WIN32)
193 mono_threads_platform_register (MonoThreadInfo *info)
195 HANDLE thread_handle;
197 thread_handle = GetCurrentThread ();
198 g_assert (thread_handle);
200 /* The handle returned by GetCurrentThread () is a pseudo handle, so it can't
201 * be used to refer to the thread from other threads for things like aborting. */
202 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
204 g_assert (!info->handle);
205 info->handle = thread_handle;
209 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid)
214 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
218 /* A new handle is open when attaching
219 * the thread, so we don't need this one */
220 CloseHandle (result);
223 *out_tid = thread_id;
226 // TOOD: Use VirtualQuery to get correct value
227 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
228 *stack_size = 2 * 1024 * 1024;
236 mono_native_thread_id_get (void)
238 return GetCurrentThreadId ();
242 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
248 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
250 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
254 mono_native_thread_join (MonoNativeThreadId tid)
258 if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
261 DWORD res = WaitForSingleObject (handle, INFINITE);
263 CloseHandle (handle);
265 return res != WAIT_FAILED;
268 #if HAVE_DECL___READFSDWORD==0
269 static MONO_ALWAYS_INLINE unsigned long long
270 __readfsdword (unsigned long offset)
273 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
274 __asm__ volatile ("movl %%fs:%1,%0"
275 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
281 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
283 MEMORY_BASIC_INFORMATION meminfo;
286 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
287 guint8 *stackTop = (guint8*)tib->StackBase;
288 guint8 *stackBottom = (guint8*)tib->StackLimit;
290 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
291 void* tib = (void*)__readfsdword(0x18);
292 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
293 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
296 Windows stacks are expanded on demand, one page at time. The TIB reports
297 only the currently allocated amount.
298 VirtualQuery will return the actual limit for the bottom, which is what we want.
300 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
301 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
303 *staddr = stackBottom;
304 *stsize = stackTop - stackBottom;
309 mono_threads_platform_yield (void)
311 return SwitchToThread ();
315 mono_threads_platform_exit (int exit_code)
317 mono_thread_info_detach ();
318 ExitThread (exit_code);
322 mono_threads_platform_unregister (MonoThreadInfo *info)
324 g_assert (info->handle);
326 CloseHandle (info->handle);
331 mono_threads_get_max_stack_size (void)
338 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
340 HANDLE thread_handle;
342 g_assert (info->handle);
343 DuplicateHandle (GetCurrentProcess (), info->handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
345 return thread_handle;
349 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
351 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
355 mono_threads_platform_close_thread_handle (HANDLE handle)
357 CloseHandle (handle);
360 #if defined(_MSC_VER)
361 const DWORD MS_VC_EXCEPTION=0x406D1388;
363 typedef struct tagTHREADNAME_INFO
365 DWORD dwType; // Must be 0x1000.
366 LPCSTR szName; // Pointer to name (in user addr space).
367 DWORD dwThreadID; // Thread ID (-1=caller thread).
368 DWORD dwFlags; // Reserved for future use, must be zero.
374 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
376 #if defined(_MSC_VER)
377 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
378 THREADNAME_INFO info;
379 info.dwType = 0x1000;
381 info.dwThreadID = tid;
385 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
387 __except(EXCEPTION_EXECUTE_HANDLER) {
393 mono_threads_platform_set_exited (gpointer handle)
398 mono_threads_platform_init (void)