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;
157 HANDLE suspend_event;
162 inner_start_thread (LPVOID arg)
164 ThreadStartInfo *start_info = arg;
165 void *t_arg = start_info->arg;
166 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
168 gboolean suspend = start_info->suspend;
169 HANDLE suspend_event = start_info->suspend_event;
170 MonoThreadInfo *info;
172 info = mono_thread_info_attach (&result);
173 info->runtime_thread = TRUE;
174 info->create_suspended = suspend;
176 start_info->handle = info->handle;
178 mono_threads_platform_set_priority(info, start_info->priority);
180 mono_coop_sem_post (&(start_info->registered));
183 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
184 CloseHandle (suspend_event);
187 result = start_func (t_arg);
189 mono_thread_info_detach ();
195 mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
197 ThreadStartInfo start_info;
200 guint32 creation_flags = tp->creation_flags;
203 memset (&start_info, 0, sizeof (start_info));
204 mono_coop_sem_init (&(start_info.registered), 0);
205 start_info.arg = arg;
206 start_info.start_routine = start_routine;
207 start_info.suspend = creation_flags & CREATE_SUSPENDED;
208 start_info.priority = tp->priority;
209 creation_flags &= ~CREATE_SUSPENDED;
210 if (start_info.suspend) {
211 start_info.suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
212 if (!start_info.suspend_event)
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);
225 if (start_info.suspend) {
226 g_assert (SuspendThread (start_info.handle) != (DWORD)-1);
227 SetEvent (start_info.suspend_event);
229 } else if (start_info.suspend) {
230 CloseHandle (start_info.suspend_event);
233 *out_tid = thread_id;
234 mono_coop_sem_destroy (&(start_info.registered));
235 return start_info.handle;
240 mono_native_thread_id_get (void)
242 return GetCurrentThreadId ();
246 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
252 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
254 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
258 mono_threads_platform_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
262 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
264 ResumeThread (handle);
265 CloseHandle (handle);
268 #if HAVE_DECL___READFSDWORD==0
269 static MONO_ALWAYS_INLINE unsigned long long
270 __readfsdword (unsigned long offset)
273 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
274 __asm__ volatile ("movl %%fs:%1,%0"
275 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
281 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
283 MEMORY_BASIC_INFORMATION meminfo;
286 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
287 guint8 *stackTop = (guint8*)tib->StackBase;
288 guint8 *stackBottom = (guint8*)tib->StackLimit;
290 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
291 void* tib = (void*)__readfsdword(0x18);
292 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
293 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
296 Windows stacks are expanded on demand, one page at time. The TIB reports
297 only the currently allocated amount.
298 VirtualQuery will return the actual limit for the bottom, which is what we want.
300 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
301 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
303 *staddr = stackBottom;
304 *stsize = stackTop - stackBottom;
309 mono_threads_platform_yield (void)
311 return SwitchToThread ();
315 mono_threads_platform_exit (int exit_code)
317 ExitThread (exit_code);
321 mono_threads_platform_unregister (MonoThreadInfo *info)
326 mono_threads_platform_open_handle (void)
328 HANDLE thread_handle;
330 thread_handle = GetCurrentThread ();
331 g_assert (thread_handle);
334 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
335 * refer to the thread from other threads for things like aborting.
337 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
338 THREAD_ALL_ACCESS, TRUE, 0);
340 return thread_handle;
344 mono_threads_get_max_stack_size (void)
351 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
353 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
356 #if defined(_MSC_VER)
357 const DWORD MS_VC_EXCEPTION=0x406D1388;
359 typedef struct tagTHREADNAME_INFO
361 DWORD dwType; // Must be 0x1000.
362 LPCSTR szName; // Pointer to name (in user addr space).
363 DWORD dwThreadID; // Thread ID (-1=caller thread).
364 DWORD dwFlags; // Reserved for future use, must be zero.
370 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
372 #if defined(_MSC_VER)
373 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
374 THREADNAME_INFO info;
375 info.dwType = 0x1000;
377 info.dwThreadID = tid;
381 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
383 __except(EXCEPTION_EXECUTE_HANDLER) {
389 mono_threads_platform_set_exited (MonoThreadInfo *info)
394 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
400 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
402 g_assert_not_reached ();
406 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
408 g_assert_not_reached ();
412 mono_threads_platform_get_priority (MonoThreadInfo *info)
414 g_assert (info->handle);
415 return GetThreadPriority (info->handle) + 2;
419 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
421 g_assert (info->handle);
422 return SetThreadPriority (info->handle, priority - 2);
426 mono_threads_platform_init (void)