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, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
177 ThreadStartInfo *start_info;
182 start_info = g_malloc0 (sizeof (ThreadStartInfo));
185 mono_coop_sem_init (&(start_info->registered), 0);
186 start_info->arg = arg;
187 start_info->start_routine = start_routine;
188 start_info->suspend = creation_flags & CREATE_SUSPENDED;
189 creation_flags &= ~CREATE_SUSPENDED;
190 if (start_info->suspend) {
191 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
192 if (!start_info->suspend_event)
196 result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
198 res = mono_coop_sem_wait (&(start_info->registered), MONO_SEM_FLAGS_NONE);
199 g_assert (res != -1);
201 if (start_info->suspend) {
202 g_assert (SuspendThread (result) != (DWORD)-1);
203 SetEvent (start_info->suspend_event);
205 } else if (start_info->suspend) {
206 CloseHandle (start_info->suspend_event);
209 *out_tid = thread_id;
210 mono_coop_sem_destroy (&(start_info->registered));
217 mono_native_thread_id_get (void)
219 return GetCurrentThreadId ();
223 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
229 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
231 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
235 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
239 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
241 ResumeThread (handle);
242 CloseHandle (handle);
245 #if HAVE_DECL___READFSDWORD==0
246 static MONO_ALWAYS_INLINE unsigned long long
247 __readfsdword (unsigned long offset)
250 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
251 __asm__ volatile ("movl %%fs:%1,%0"
252 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
258 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
260 MEMORY_BASIC_INFORMATION meminfo;
263 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
264 guint8 *stackTop = (guint8*)tib->StackBase;
265 guint8 *stackBottom = (guint8*)tib->StackLimit;
267 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
268 void* tib = (void*)__readfsdword(0x18);
269 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
270 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
273 Windows stacks are expanded on demand, one page at time. The TIB reports
274 only the currently allocated amount.
275 VirtualQuery will return the actual limit for the bottom, which is what we want.
277 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
278 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
280 *staddr = stackBottom;
281 *stsize = stackTop - stackBottom;
286 mono_threads_core_yield (void)
288 return SwitchToThread ();
292 mono_threads_core_exit (int exit_code)
294 ExitThread (exit_code);
298 mono_threads_core_unregister (MonoThreadInfo *info)
303 mono_threads_core_open_handle (void)
305 HANDLE thread_handle;
307 thread_handle = GetCurrentThread ();
308 g_assert (thread_handle);
311 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
312 * refer to the thread from other threads for things like aborting.
314 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
315 THREAD_ALL_ACCESS, TRUE, 0);
317 return thread_handle;
321 mono_threads_get_max_stack_size (void)
328 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
330 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
333 #if defined(_MSC_VER)
334 const DWORD MS_VC_EXCEPTION=0x406D1388;
336 typedef struct tagTHREADNAME_INFO
338 DWORD dwType; // Must be 0x1000.
339 LPCSTR szName; // Pointer to name (in user addr space).
340 DWORD dwThreadID; // Thread ID (-1=caller thread).
341 DWORD dwFlags; // Reserved for future use, must be zero.
347 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
349 #if defined(_MSC_VER)
350 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
351 THREADNAME_INFO info;
352 info.dwType = 0x1000;
354 info.dwThreadID = tid;
358 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
360 __except(EXCEPTION_EXECUTE_HANDLER) {