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)
137 #if defined (HOST_WIN32)
140 LPTHREAD_START_ROUTINE start_routine;
142 MonoCoopSem registered;
144 HANDLE suspend_event;
148 inner_start_thread (LPVOID arg)
150 ThreadStartInfo *start_info = arg;
151 void *t_arg = start_info->arg;
153 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
155 gboolean suspend = start_info->suspend;
156 HANDLE suspend_event = start_info->suspend_event;
157 MonoThreadInfo *info;
159 info = mono_thread_info_attach (&result);
160 info->runtime_thread = TRUE;
161 info->create_suspended = suspend;
163 post_result = mono_coop_sem_post (&(start_info->registered));
164 g_assert (!post_result);
167 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
168 CloseHandle (suspend_event);
171 result = start_func (t_arg);
173 mono_thread_info_detach ();
179 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
181 ThreadStartInfo *start_info;
186 start_info = g_malloc0 (sizeof (ThreadStartInfo));
189 mono_coop_sem_init (&(start_info->registered), 0);
190 start_info->arg = arg;
191 start_info->start_routine = start_routine;
192 start_info->suspend = creation_flags & CREATE_SUSPENDED;
193 creation_flags &= ~CREATE_SUSPENDED;
194 if (start_info->suspend) {
195 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
196 if (!start_info->suspend_event)
200 result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
202 res = mono_coop_sem_wait (&(start_info->registered), MONO_SEM_FLAGS_NONE);
203 g_assert (res != -1);
205 if (start_info->suspend) {
206 g_assert (SuspendThread (result) != (DWORD)-1);
207 SetEvent (start_info->suspend_event);
209 } else if (start_info->suspend) {
210 CloseHandle (start_info->suspend_event);
213 *out_tid = thread_id;
214 mono_coop_sem_destroy (&(start_info->registered));
221 mono_native_thread_id_get (void)
223 return GetCurrentThreadId ();
227 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
233 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
235 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
239 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
243 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
245 ResumeThread (handle);
246 CloseHandle (handle);
249 #if HAVE_DECL___READFSDWORD==0
250 static MONO_ALWAYS_INLINE unsigned long long
251 __readfsdword (unsigned long offset)
254 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
255 __asm__ volatile ("movl %%fs:%1,%0"
256 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
262 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
264 MEMORY_BASIC_INFORMATION meminfo;
267 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
268 guint8 *stackTop = (guint8*)tib->StackBase;
269 guint8 *stackBottom = (guint8*)tib->StackLimit;
271 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
272 void* tib = (void*)__readfsdword(0x18);
273 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
274 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
277 Windows stacks are expanded on demand, one page at time. The TIB reports
278 only the currently allocated amount.
279 VirtualQuery will return the actual limit for the bottom, which is what we want.
281 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
282 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
284 *staddr = stackBottom;
285 *stsize = stackTop - stackBottom;
290 mono_threads_core_yield (void)
292 return SwitchToThread ();
296 mono_threads_core_exit (int exit_code)
298 ExitThread (exit_code);
302 mono_threads_core_unregister (MonoThreadInfo *info)
307 mono_threads_core_open_handle (void)
309 HANDLE thread_handle;
311 thread_handle = GetCurrentThread ();
312 g_assert (thread_handle);
315 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
316 * refer to the thread from other threads for things like aborting.
318 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
319 THREAD_ALL_ACCESS, TRUE, 0);
321 return thread_handle;
325 mono_threads_get_max_stack_size (void)
332 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
334 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
337 #if defined(_MSC_VER)
338 const DWORD MS_VC_EXCEPTION=0x406D1388;
340 typedef struct tagTHREADNAME_INFO
342 DWORD dwType; // Must be 0x1000.
343 LPCSTR szName; // Pointer to name (in user addr space).
344 DWORD dwThreadID; // Thread ID (-1=caller thread).
345 DWORD dwFlags; // Reserved for future use, must be zero.
351 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
353 #if defined(_MSC_VER)
354 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
355 THREADNAME_INFO info;
356 info.dwType = 0x1000;
358 info.dwThreadID = tid;
362 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
364 __except(EXCEPTION_EXECUTE_HANDLER) {