2 * mono-threads-windows.c: Low-level threading, windows version
5 * Rodrigo Kumpera (kumpera@gmail.com)
12 #if defined(HOST_WIN32)
14 #include <mono/utils/mono-threads.h>
19 mono_threads_init_platform (void)
24 interrupt_apc (ULONG_PTR param)
29 mono_threads_core_interrupt (MonoThreadInfo *info)
31 DWORD id = mono_thread_info_get_tid (info);
34 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
37 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
43 mono_threads_core_abort_syscall (MonoThreadInfo *info)
48 mono_threads_core_needs_abort_syscall (void)
54 mono_threads_core_self_suspend (MonoThreadInfo *info)
56 g_assert_not_reached ();
60 mono_threads_core_suspend (MonoThreadInfo *info)
62 DWORD id = mono_thread_info_get_tid (info);
67 g_assert (id != GetCurrentThreadId ());
69 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
72 result = SuspendThread (handle);
73 if (result == (DWORD)-1) {
74 fprintf (stderr, "could not suspend thread %x (handle %p): %d\n", id, handle, GetLastError ()); fflush (stderr);
81 res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->suspend_state, info);
88 mono_threads_core_resume (MonoThreadInfo *info)
90 DWORD id = mono_thread_info_get_tid (info);
94 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
97 if (info->async_target) {
102 ctx = info->suspend_state.ctx;
103 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
104 info->async_target = info->user_data = NULL;
106 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
108 if (!GetThreadContext (handle, &context)) {
109 CloseHandle (handle);
113 g_assert (context.ContextFlags & CONTEXT_INTEGER);
114 g_assert (context.ContextFlags & CONTEXT_CONTROL);
116 // FIXME: This should be in mini-windows.c
117 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
119 context.Rip = ctx.rip;
120 context.Rax = ctx.rax;
121 context.Rcx = ctx.rcx;
122 context.Rdx = ctx.rdx;
123 context.Rbx = ctx.rbx;
124 context.Rsp = ctx.rsp;
125 context.Rbp = ctx.rbp;
126 context.Rsi = ctx.rsi;
127 context.Rdi = ctx.rdi;
130 context.R10 = ctx.r10;
131 context.R11 = ctx.r11;
132 context.R12 = ctx.r12;
133 context.R13 = ctx.r13;
134 context.R14 = ctx.r14;
135 context.R15 = ctx.r15;
137 context.Eip = ctx.eip;
138 context.Edi = ctx.edi;
139 context.Esi = ctx.esi;
140 context.Ebx = ctx.ebx;
141 context.Edx = ctx.edx;
142 context.Ecx = ctx.ecx;
143 context.Eax = ctx.eax;
144 context.Ebp = ctx.ebp;
145 context.Esp = ctx.esp;
148 res = SetThreadContext (handle, &context);
152 result = ResumeThread (handle);
153 g_assert (result != (DWORD)-1);
155 CloseHandle (handle);
157 return result != (DWORD)-1;
161 mono_threads_platform_register (MonoThreadInfo *info)
166 mono_threads_platform_free (MonoThreadInfo *info)
171 LPTHREAD_START_ROUTINE start_routine;
173 MonoSemType registered;
175 HANDLE suspend_event;
179 inner_start_thread (LPVOID arg)
181 ThreadStartInfo *start_info = arg;
182 void *t_arg = start_info->arg;
184 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
186 gboolean suspend = start_info->suspend;
187 HANDLE suspend_event = start_info->suspend_event;
188 MonoThreadInfo *info;
190 info = mono_thread_info_attach (&result);
191 info->runtime_thread = TRUE;
192 info->create_suspended = suspend;
194 post_result = MONO_SEM_POST (&(start_info->registered));
195 g_assert (!post_result);
198 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
199 CloseHandle (suspend_event);
202 result = start_func (t_arg);
204 mono_thread_info_detach ();
210 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
212 ThreadStartInfo *start_info;
216 start_info = g_malloc0 (sizeof (ThreadStartInfo));
219 MONO_SEM_INIT (&(start_info->registered), 0);
220 start_info->arg = arg;
221 start_info->start_routine = start_routine;
222 start_info->suspend = creation_flags & CREATE_SUSPENDED;
223 creation_flags &= ~CREATE_SUSPENDED;
224 if (start_info->suspend) {
225 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
226 if (!start_info->suspend_event)
230 result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
232 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
233 /*if (EINTR != errno) ABORT("sem_wait failed"); */
235 if (start_info->suspend) {
236 g_assert (SuspendThread (result) != (DWORD)-1);
237 SetEvent (start_info->suspend_event);
239 } else if (start_info->suspend) {
240 CloseHandle (start_info->suspend_event);
243 *out_tid = thread_id;
244 MONO_SEM_DESTROY (&(start_info->registered));
251 mono_native_thread_id_get (void)
253 return GetCurrentThreadId ();
257 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
263 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
265 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
269 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
273 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
275 ResumeThread (handle);
276 CloseHandle (handle);
279 #if HAVE_DECL___READFSDWORD==0
280 static __inline__ __attribute__((always_inline))
282 __readfsdword (unsigned long offset)
285 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
286 __asm__ volatile ("movl %%fs:%1,%0"
287 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
293 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
295 MEMORY_BASIC_INFORMATION meminfo;
298 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
299 guint8 *stackTop = (guint8*)tib->StackBase;
300 guint8 *stackBottom = (guint8*)tib->StackLimit;
302 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
303 void* tib = (void*)__readfsdword(0x18);
304 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
305 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
308 Windows stacks are expanded on demand, one page at time. The TIB reports
309 only the currently allocated amount.
310 VirtualQuery will return the actual limit for the bottom, which is what we want.
312 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
313 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
315 *staddr = stackBottom;
316 *stsize = stackTop - stackBottom;
321 mono_threads_core_yield (void)
323 return SwitchToThread ();
327 mono_threads_core_exit (int exit_code)
329 ExitThread (exit_code);
333 mono_threads_core_unregister (MonoThreadInfo *info)
338 mono_threads_core_open_handle (void)
340 HANDLE thread_handle;
342 thread_handle = GetCurrentThread ();
343 g_assert (thread_handle);
346 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
347 * refer to the thread from other threads for things like aborting.
349 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
350 THREAD_ALL_ACCESS, TRUE, 0);
352 return thread_handle;
356 mono_threads_get_max_stack_size (void)
363 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
365 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
368 #if !defined(__GNUC__)
369 const DWORD MS_VC_EXCEPTION=0x406D1388;
371 typedef struct tagTHREADNAME_INFO
373 DWORD dwType; // Must be 0x1000.
374 LPCSTR szName; // Pointer to name (in user addr space).
375 DWORD dwThreadID; // Thread ID (-1=caller thread).
376 DWORD dwFlags; // Reserved for future use, must be zero.
382 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
384 #if !defined(__GNUC__)
385 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
386 THREADNAME_INFO info;
387 info.dwType = 0x1000;
389 info.dwThreadID = tid;
393 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
395 __except(EXCEPTION_EXECUTE_HANDLER) {