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>
19 mono_threads_suspend_init (void)
24 interrupt_apc (ULONG_PTR param)
29 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
31 DWORD id = mono_thread_info_get_tid (info);
35 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
38 result = SuspendThread (handle);
39 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
40 if (result == (DWORD)-1) {
45 /* We're in the middle of a self-suspend, resume and register */
46 if (!mono_threads_transition_finish_async_suspend (info)) {
47 mono_threads_add_to_pending_operation_set (info);
48 result = ResumeThread (handle);
49 g_assert (result == 1);
51 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
52 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
55 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
56 THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
57 if (info->suspend_can_continue) {
58 //FIXME do we need to QueueUserAPC on this case?
60 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
62 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
66 return info->suspend_can_continue;
70 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
72 return info->suspend_can_continue;
76 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
78 DWORD id = mono_thread_info_get_tid (info);
82 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
85 if (info->async_target) {
90 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
91 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
92 info->async_target = info->user_data = NULL;
94 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
96 if (!GetThreadContext (handle, &context)) {
101 g_assert (context.ContextFlags & CONTEXT_INTEGER);
102 g_assert (context.ContextFlags & CONTEXT_CONTROL);
104 mono_monoctx_to_sigctx (&ctx, &context);
106 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
107 res = SetThreadContext (handle, &context);
109 CloseHandle (handle);
114 result = ResumeThread (handle);
115 CloseHandle (handle);
117 return result != (DWORD)-1;
122 mono_threads_suspend_register (MonoThreadInfo *info)
127 mono_threads_suspend_free (MonoThreadInfo *info)
133 #if defined (HOST_WIN32)
136 mono_threads_platform_register (MonoThreadInfo *info)
138 HANDLE thread_handle;
140 thread_handle = GetCurrentThread ();
141 g_assert (thread_handle);
143 /* The handle returned by GetCurrentThread () is a pseudo handle, so it can't
144 * be used to refer to the thread from other threads for things like aborting. */
145 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
147 g_assert (!info->handle);
148 info->handle = thread_handle;
152 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid)
157 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
161 /* A new handle is open when attaching
162 * the thread, so we don't need this one */
163 CloseHandle (result);
166 *out_tid = thread_id;
169 // TOOD: Use VirtualQuery to get correct value
170 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
171 *stack_size = 2 * 1024 * 1024;
179 mono_native_thread_id_get (void)
181 return GetCurrentThreadId ();
185 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
191 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
193 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
197 mono_native_thread_join (MonoNativeThreadId tid)
201 if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
204 DWORD res = WaitForSingleObject (handle, INFINITE);
206 CloseHandle (handle);
208 return res != WAIT_FAILED;
211 #if HAVE_DECL___READFSDWORD==0
212 static MONO_ALWAYS_INLINE unsigned long long
213 __readfsdword (unsigned long offset)
216 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
217 __asm__ volatile ("movl %%fs:%1,%0"
218 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
224 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
226 MEMORY_BASIC_INFORMATION meminfo;
229 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
230 guint8 *stackTop = (guint8*)tib->StackBase;
231 guint8 *stackBottom = (guint8*)tib->StackLimit;
233 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
234 void* tib = (void*)__readfsdword(0x18);
235 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
236 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
239 Windows stacks are expanded on demand, one page at time. The TIB reports
240 only the currently allocated amount.
241 VirtualQuery will return the actual limit for the bottom, which is what we want.
243 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
244 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
246 *staddr = stackBottom;
247 *stsize = stackTop - stackBottom;
252 mono_threads_platform_yield (void)
254 return SwitchToThread ();
258 mono_threads_platform_exit (int exit_code)
260 mono_thread_info_detach ();
261 ExitThread (exit_code);
265 mono_threads_platform_unregister (MonoThreadInfo *info)
267 g_assert (info->handle);
269 CloseHandle (info->handle);
274 mono_threads_get_max_stack_size (void)
281 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
283 HANDLE thread_handle;
285 g_assert (info->handle);
286 DuplicateHandle (GetCurrentProcess (), info->handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
288 return thread_handle;
292 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
294 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
298 mono_threads_platform_close_thread_handle (HANDLE handle)
300 CloseHandle (handle);
303 #if defined(_MSC_VER)
304 const DWORD MS_VC_EXCEPTION=0x406D1388;
306 typedef struct tagTHREADNAME_INFO
308 DWORD dwType; // Must be 0x1000.
309 LPCSTR szName; // Pointer to name (in user addr space).
310 DWORD dwThreadID; // Thread ID (-1=caller thread).
311 DWORD dwFlags; // Reserved for future use, must be zero.
317 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
319 #if defined(_MSC_VER)
320 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
321 THREADNAME_INFO info;
322 info.dwType = 0x1000;
324 info.dwThreadID = tid;
328 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
330 __except(EXCEPTION_EXECUTE_HANDLER) {
336 mono_threads_platform_set_exited (gpointer handle)
341 mono_threads_platform_init (void)