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 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid)
157 result = CreateThread (NULL, stack_size, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
161 /* A new handle is open when attaching
162 * the thread, so we don't need this one */
163 CloseHandle (result);
166 *out_tid = thread_id;
173 mono_native_thread_id_get (void)
175 return GetCurrentThreadId ();
179 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
185 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
187 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
190 #if HAVE_DECL___READFSDWORD==0
191 static MONO_ALWAYS_INLINE unsigned long long
192 __readfsdword (unsigned long offset)
195 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
196 __asm__ volatile ("movl %%fs:%1,%0"
197 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
203 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
205 MEMORY_BASIC_INFORMATION meminfo;
208 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
209 guint8 *stackTop = (guint8*)tib->StackBase;
210 guint8 *stackBottom = (guint8*)tib->StackLimit;
212 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
213 void* tib = (void*)__readfsdword(0x18);
214 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
215 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
218 Windows stacks are expanded on demand, one page at time. The TIB reports
219 only the currently allocated amount.
220 VirtualQuery will return the actual limit for the bottom, which is what we want.
222 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
223 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
225 *staddr = stackBottom;
226 *stsize = stackTop - stackBottom;
231 mono_threads_platform_yield (void)
233 return SwitchToThread ();
237 mono_threads_platform_exit (int exit_code)
239 mono_thread_info_detach ();
240 ExitThread (exit_code);
244 mono_threads_platform_unregister (MonoThreadInfo *info)
246 mono_threads_platform_set_exited (info);
250 mono_threads_get_max_stack_size (void)
257 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
259 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
263 mono_threads_platform_close_thread_handle (HANDLE handle)
265 CloseHandle (handle);
268 #if defined(_MSC_VER)
269 const DWORD MS_VC_EXCEPTION=0x406D1388;
271 typedef struct tagTHREADNAME_INFO
273 DWORD dwType; // Must be 0x1000.
274 LPCSTR szName; // Pointer to name (in user addr space).
275 DWORD dwThreadID; // Thread ID (-1=caller thread).
276 DWORD dwFlags; // Reserved for future use, must be zero.
282 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
284 #if defined(_MSC_VER)
285 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
286 THREADNAME_INFO info;
287 info.dwType = 0x1000;
289 info.dwThreadID = tid;
293 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
295 __except(EXCEPTION_EXECUTE_HANDLER) {
301 mono_threads_platform_set_exited (MonoThreadInfo *info)
303 g_assert (info->handle);
304 // No need to call CloseHandle() here since the InternalThread
305 // destructor will close the handle when the finalizer thread calls it
310 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
316 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
318 g_assert_not_reached ();
322 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
324 g_assert_not_reached ();
328 mono_threads_platform_get_priority (MonoThreadInfo *info)
330 g_assert (info->handle);
331 return GetThreadPriority (info->handle) + 2;
335 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
339 g_assert (info->handle);
341 res = SetThreadPriority (info->handle, priority - 2);
343 g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
347 mono_threads_platform_init (void)