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_suspend_init (void)
24 interrupt_apc (ULONG_PTR param)
29 mono_threads_suspend_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_suspend_check_suspend_result (MonoThreadInfo *info)
72 return info->suspend_can_continue;
76 mono_threads_suspend_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_suspend_register (MonoThreadInfo *info)
127 mono_threads_suspend_free (MonoThreadInfo *info)
133 #if defined (HOST_WIN32)
136 mono_threads_platform_register (MonoThreadInfo *info)
138 HANDLE thread_handle;
140 thread_handle = GetCurrentThread ();
141 g_assert (thread_handle);
143 /* The handle returned by GetCurrentThread () is a pseudo handle, so it can't
144 * be used to refer to the thread from other threads for things like aborting. */
145 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
147 g_assert (!info->handle);
148 info->handle = thread_handle;
152 LPTHREAD_START_ROUTINE start_routine;
155 MonoCoopSem registered;
161 inner_start_thread (LPVOID arg)
163 ThreadStartInfo *start_info = arg;
164 void *t_arg = start_info->arg;
165 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
167 gboolean suspend = start_info->suspend;
168 MonoThreadInfo *info;
171 info = mono_thread_info_attach (&result);
172 info->runtime_thread = TRUE;
174 start_info->handle = info->handle;
176 mono_threads_platform_set_priority(info, start_info->priority);
179 info->create_suspended = TRUE;
180 mono_coop_sem_init (&info->create_suspended_sem, 0);
183 mono_coop_sem_post (&(start_info->registered));
186 res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
187 g_assert (res != -1);
189 mono_coop_sem_destroy (&info->create_suspended_sem);
192 result = start_func (t_arg);
194 mono_thread_info_detach ();
200 mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
202 ThreadStartInfo start_info;
205 guint32 creation_flags = tp->creation_flags;
208 memset (&start_info, 0, sizeof (start_info));
209 mono_coop_sem_init (&(start_info.registered), 0);
210 start_info.arg = arg;
211 start_info.start_routine = start_routine;
212 start_info.suspend = creation_flags & CREATE_SUSPENDED;
213 start_info.priority = tp->priority;
214 creation_flags &= ~CREATE_SUSPENDED;
216 result = CreateThread (NULL, tp->stack_size, inner_start_thread, &start_info, creation_flags, &thread_id);
218 res = mono_coop_sem_wait (&(start_info.registered), MONO_SEM_FLAGS_NONE);
219 g_assert (res != -1);
221 /* A new handle has been opened when attaching
222 * the thread, so we don't need this one */
223 CloseHandle (result);
226 *out_tid = thread_id;
227 mono_coop_sem_destroy (&(start_info.registered));
228 return start_info.handle;
233 mono_native_thread_id_get (void)
235 return GetCurrentThreadId ();
239 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
245 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
247 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
250 #if HAVE_DECL___READFSDWORD==0
251 static MONO_ALWAYS_INLINE unsigned long long
252 __readfsdword (unsigned long offset)
255 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
256 __asm__ volatile ("movl %%fs:%1,%0"
257 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
263 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
265 MEMORY_BASIC_INFORMATION meminfo;
268 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
269 guint8 *stackTop = (guint8*)tib->StackBase;
270 guint8 *stackBottom = (guint8*)tib->StackLimit;
272 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
273 void* tib = (void*)__readfsdword(0x18);
274 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
275 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
278 Windows stacks are expanded on demand, one page at time. The TIB reports
279 only the currently allocated amount.
280 VirtualQuery will return the actual limit for the bottom, which is what we want.
282 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
283 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
285 *staddr = stackBottom;
286 *stsize = stackTop - stackBottom;
291 mono_threads_platform_yield (void)
293 return SwitchToThread ();
297 mono_threads_platform_exit (int exit_code)
299 mono_thread_info_detach ();
300 ExitThread (exit_code);
304 mono_threads_platform_unregister (MonoThreadInfo *info)
306 mono_threads_platform_set_exited (info);
310 mono_threads_get_max_stack_size (void)
317 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
319 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
322 #if defined(_MSC_VER)
323 const DWORD MS_VC_EXCEPTION=0x406D1388;
325 typedef struct tagTHREADNAME_INFO
327 DWORD dwType; // Must be 0x1000.
328 LPCSTR szName; // Pointer to name (in user addr space).
329 DWORD dwThreadID; // Thread ID (-1=caller thread).
330 DWORD dwFlags; // Reserved for future use, must be zero.
336 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
338 #if defined(_MSC_VER)
339 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
340 THREADNAME_INFO info;
341 info.dwType = 0x1000;
343 info.dwThreadID = tid;
347 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
349 __except(EXCEPTION_EXECUTE_HANDLER) {
355 mono_threads_platform_set_exited (MonoThreadInfo *info)
357 g_assert (info->handle);
358 // No need to call CloseHandle() here since the InternalThread
359 // destructor will close the handle when the finalizer thread calls it
364 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
370 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
372 g_assert_not_reached ();
376 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
378 g_assert_not_reached ();
382 mono_threads_platform_get_priority (MonoThreadInfo *info)
384 g_assert (info->handle);
385 return GetThreadPriority (info->handle) + 2;
389 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
391 g_assert (info->handle);
392 return SetThreadPriority (info->handle, priority - 2);
396 mono_threads_platform_init (void)