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);
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 res = 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);
59 //FIXME do we need to QueueUserAPC on this case?
61 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
63 mono_threads_transition_async_suspend_compensation (info);
64 result = ResumeThread (handle);
65 g_assert (result == 1);
66 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
74 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
80 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
82 DWORD id = mono_thread_info_get_tid (info);
86 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
89 if (info->async_target) {
94 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
95 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
96 info->async_target = info->user_data = NULL;
98 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
100 if (!GetThreadContext (handle, &context)) {
101 CloseHandle (handle);
105 g_assert (context.ContextFlags & CONTEXT_INTEGER);
106 g_assert (context.ContextFlags & CONTEXT_CONTROL);
108 mono_monoctx_to_sigctx (&ctx, &context);
110 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
111 res = SetThreadContext (handle, &context);
113 CloseHandle (handle);
118 result = ResumeThread (handle);
119 CloseHandle (handle);
121 return result != (DWORD)-1;
126 mono_threads_platform_register (MonoThreadInfo *info)
131 mono_threads_platform_free (MonoThreadInfo *info)
136 mono_threads_core_begin_global_suspend (void)
141 mono_threads_core_end_global_suspend (void)
147 #if defined (HOST_WIN32)
150 LPTHREAD_START_ROUTINE start_routine;
152 MonoSemType registered;
154 HANDLE suspend_event;
158 inner_start_thread (LPVOID arg)
160 ThreadStartInfo *start_info = arg;
161 void *t_arg = start_info->arg;
163 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
165 gboolean suspend = start_info->suspend;
166 HANDLE suspend_event = start_info->suspend_event;
167 MonoThreadInfo *info;
169 info = mono_thread_info_attach (&result);
170 info->runtime_thread = TRUE;
171 info->create_suspended = suspend;
173 post_result = MONO_SEM_POST (&(start_info->registered));
174 g_assert (!post_result);
177 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
178 CloseHandle (suspend_event);
181 result = start_func (t_arg);
183 mono_thread_info_detach ();
189 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
191 ThreadStartInfo *start_info;
195 start_info = g_malloc0 (sizeof (ThreadStartInfo));
198 MONO_SEM_INIT (&(start_info->registered), 0);
199 start_info->arg = arg;
200 start_info->start_routine = start_routine;
201 start_info->suspend = creation_flags & CREATE_SUSPENDED;
202 creation_flags &= ~CREATE_SUSPENDED;
203 if (start_info->suspend) {
204 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
205 if (!start_info->suspend_event)
209 result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
211 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
212 /*if (EINTR != errno) ABORT("sem_wait failed"); */
214 if (start_info->suspend) {
215 g_assert (SuspendThread (result) != (DWORD)-1);
216 SetEvent (start_info->suspend_event);
218 } else if (start_info->suspend) {
219 CloseHandle (start_info->suspend_event);
222 *out_tid = thread_id;
223 MONO_SEM_DESTROY (&(start_info->registered));
230 mono_native_thread_id_get (void)
232 return GetCurrentThreadId ();
236 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
242 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
244 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
248 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
252 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
254 ResumeThread (handle);
255 CloseHandle (handle);
258 #if HAVE_DECL___READFSDWORD==0
259 static MONO_ALWAYS_INLINE unsigned long long
260 __readfsdword (unsigned long offset)
263 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
264 __asm__ volatile ("movl %%fs:%1,%0"
265 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
271 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
273 MEMORY_BASIC_INFORMATION meminfo;
276 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
277 guint8 *stackTop = (guint8*)tib->StackBase;
278 guint8 *stackBottom = (guint8*)tib->StackLimit;
280 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
281 void* tib = (void*)__readfsdword(0x18);
282 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
283 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
286 Windows stacks are expanded on demand, one page at time. The TIB reports
287 only the currently allocated amount.
288 VirtualQuery will return the actual limit for the bottom, which is what we want.
290 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
291 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
293 *staddr = stackBottom;
294 *stsize = stackTop - stackBottom;
299 mono_threads_core_yield (void)
301 return SwitchToThread ();
305 mono_threads_core_exit (int exit_code)
307 ExitThread (exit_code);
311 mono_threads_core_unregister (MonoThreadInfo *info)
316 mono_threads_core_open_handle (void)
318 HANDLE thread_handle;
320 thread_handle = GetCurrentThread ();
321 g_assert (thread_handle);
324 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
325 * refer to the thread from other threads for things like aborting.
327 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
328 THREAD_ALL_ACCESS, TRUE, 0);
330 return thread_handle;
334 mono_threads_get_max_stack_size (void)
341 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
343 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
346 #if defined(_MSC_VER)
347 const DWORD MS_VC_EXCEPTION=0x406D1388;
349 typedef struct tagTHREADNAME_INFO
351 DWORD dwType; // Must be 0x1000.
352 LPCSTR szName; // Pointer to name (in user addr space).
353 DWORD dwThreadID; // Thread ID (-1=caller thread).
354 DWORD dwFlags; // Reserved for future use, must be zero.
360 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
362 #if defined(_MSC_VER)
363 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
364 THREADNAME_INFO info;
365 info.dwType = 0x1000;
367 info.dwThreadID = tid;
371 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
373 __except(EXCEPTION_EXECUTE_HANDLER) {