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_init_platform (void)
24 interrupt_apc (ULONG_PTR param)
29 mono_threads_core_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_core_check_suspend_result (MonoThreadInfo *info)
72 return info->suspend_can_continue;
76 mono_threads_core_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_platform_register (MonoThreadInfo *info)
127 mono_threads_platform_free (MonoThreadInfo *info)
133 #if defined (HOST_WIN32)
136 LPTHREAD_START_ROUTINE start_routine;
138 MonoCoopSem registered;
140 HANDLE suspend_event;
144 inner_start_thread (LPVOID arg)
146 ThreadStartInfo *start_info = arg;
147 void *t_arg = start_info->arg;
149 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
151 gboolean suspend = start_info->suspend;
152 HANDLE suspend_event = start_info->suspend_event;
153 MonoThreadInfo *info;
155 info = mono_thread_info_attach (&result);
156 info->runtime_thread = TRUE;
157 info->create_suspended = suspend;
159 post_result = mono_coop_sem_post (&(start_info->registered));
160 g_assert (!post_result);
163 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
164 CloseHandle (suspend_event);
167 result = start_func (t_arg);
169 mono_thread_info_detach ();
175 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
177 ThreadStartInfo *start_info;
180 guint32 creation_flags = tp->creation_flags;
183 start_info = g_malloc0 (sizeof (ThreadStartInfo));
186 mono_coop_sem_init (&(start_info->registered), 0);
187 start_info->arg = arg;
188 start_info->start_routine = start_routine;
189 start_info->suspend = creation_flags & CREATE_SUSPENDED;
190 creation_flags &= ~CREATE_SUSPENDED;
191 if (start_info->suspend) {
192 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
193 if (!start_info->suspend_event)
197 result = CreateThread (NULL, tp->stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
199 res = mono_coop_sem_wait (&(start_info->registered), MONO_SEM_FLAGS_NONE);
200 g_assert (res != -1);
202 if (start_info->suspend) {
203 g_assert (SuspendThread (result) != (DWORD)-1);
204 SetEvent (start_info->suspend_event);
206 } else if (start_info->suspend) {
207 CloseHandle (start_info->suspend_event);
210 *out_tid = thread_id;
211 mono_coop_sem_destroy (&(start_info->registered));
218 mono_native_thread_id_get (void)
220 return GetCurrentThreadId ();
224 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
230 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
232 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
236 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
240 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
242 ResumeThread (handle);
243 CloseHandle (handle);
246 #if HAVE_DECL___READFSDWORD==0
247 static MONO_ALWAYS_INLINE unsigned long long
248 __readfsdword (unsigned long offset)
251 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
252 __asm__ volatile ("movl %%fs:%1,%0"
253 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
259 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
261 MEMORY_BASIC_INFORMATION meminfo;
264 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
265 guint8 *stackTop = (guint8*)tib->StackBase;
266 guint8 *stackBottom = (guint8*)tib->StackLimit;
268 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
269 void* tib = (void*)__readfsdword(0x18);
270 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
271 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
274 Windows stacks are expanded on demand, one page at time. The TIB reports
275 only the currently allocated amount.
276 VirtualQuery will return the actual limit for the bottom, which is what we want.
278 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
279 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
281 *staddr = stackBottom;
282 *stsize = stackTop - stackBottom;
287 mono_threads_core_yield (void)
289 return SwitchToThread ();
293 mono_threads_core_exit (int exit_code)
295 ExitThread (exit_code);
299 mono_threads_core_unregister (MonoThreadInfo *info)
304 mono_threads_core_open_handle (void)
306 HANDLE thread_handle;
308 thread_handle = GetCurrentThread ();
309 g_assert (thread_handle);
312 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
313 * refer to the thread from other threads for things like aborting.
315 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
316 THREAD_ALL_ACCESS, TRUE, 0);
318 return thread_handle;
322 mono_threads_get_max_stack_size (void)
329 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
331 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
334 #if defined(_MSC_VER)
335 const DWORD MS_VC_EXCEPTION=0x406D1388;
337 typedef struct tagTHREADNAME_INFO
339 DWORD dwType; // Must be 0x1000.
340 LPCSTR szName; // Pointer to name (in user addr space).
341 DWORD dwThreadID; // Thread ID (-1=caller thread).
342 DWORD dwFlags; // Reserved for future use, must be zero.
348 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
350 #if defined(_MSC_VER)
351 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
352 THREADNAME_INFO info;
353 info.dwType = 0x1000;
355 info.dwThreadID = tid;
359 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
361 __except(EXCEPTION_EXECUTE_HANDLER) {