2 * mono-threads-windows.c: Low-level threading, windows version
5 * Rodrigo Kumpera (kumpera@gmail.com)
12 #if defined(HOST_WIN32)
14 #include <mono/utils/mono-threads.h>
15 #include <mono/utils/mono-compiler.h>
20 mono_threads_init_platform (void)
25 interrupt_apc (ULONG_PTR param)
30 mono_threads_core_interrupt (MonoThreadInfo *info)
32 DWORD id = mono_thread_info_get_tid (info);
35 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
38 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
44 mono_threads_core_abort_syscall (MonoThreadInfo *info)
46 mono_threads_core_interrupt (info);
50 mono_threads_core_needs_abort_syscall (void)
56 mono_threads_core_self_suspend (MonoThreadInfo *info)
58 g_assert_not_reached ();
62 mono_threads_core_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
64 DWORD id = mono_thread_info_get_tid (info);
69 g_assert (id != GetCurrentThreadId ());
71 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
74 result = SuspendThread (handle);
75 if (result == (DWORD)-1) {
76 fprintf (stderr, "could not suspend thread %x (handle %p): %d\n", id, handle, GetLastError ()); fflush (stderr);
83 res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->suspend_state, info);
90 mono_threads_core_resume (MonoThreadInfo *info)
92 DWORD id = mono_thread_info_get_tid (info);
96 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
99 if (info->async_target) {
104 ctx = info->suspend_state.ctx;
105 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
106 info->async_target = info->user_data = NULL;
108 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
110 if (!GetThreadContext (handle, &context)) {
111 CloseHandle (handle);
115 g_assert (context.ContextFlags & CONTEXT_INTEGER);
116 g_assert (context.ContextFlags & CONTEXT_CONTROL);
118 mono_monoctx_to_sigctx (&ctx, &context);
120 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
121 res = SetThreadContext (handle, &context);
125 result = ResumeThread (handle);
126 g_assert (result != (DWORD)-1);
128 CloseHandle (handle);
130 return result != (DWORD)-1;
134 mono_threads_platform_register (MonoThreadInfo *info)
139 mono_threads_platform_free (MonoThreadInfo *info)
144 LPTHREAD_START_ROUTINE start_routine;
146 MonoSemType registered;
148 HANDLE suspend_event;
152 inner_start_thread (LPVOID arg)
154 ThreadStartInfo *start_info = arg;
155 void *t_arg = start_info->arg;
157 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
159 gboolean suspend = start_info->suspend;
160 HANDLE suspend_event = start_info->suspend_event;
161 MonoThreadInfo *info;
163 info = mono_thread_info_attach (&result);
164 info->runtime_thread = TRUE;
165 info->create_suspended = suspend;
167 post_result = MONO_SEM_POST (&(start_info->registered));
168 g_assert (!post_result);
171 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
172 CloseHandle (suspend_event);
175 result = start_func (t_arg);
177 mono_thread_info_detach ();
183 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
185 ThreadStartInfo *start_info;
189 start_info = g_malloc0 (sizeof (ThreadStartInfo));
192 MONO_SEM_INIT (&(start_info->registered), 0);
193 start_info->arg = arg;
194 start_info->start_routine = start_routine;
195 start_info->suspend = creation_flags & CREATE_SUSPENDED;
196 creation_flags &= ~CREATE_SUSPENDED;
197 if (start_info->suspend) {
198 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
199 if (!start_info->suspend_event)
203 result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
205 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
206 /*if (EINTR != errno) ABORT("sem_wait failed"); */
208 if (start_info->suspend) {
209 g_assert (SuspendThread (result) != (DWORD)-1);
210 SetEvent (start_info->suspend_event);
212 } else if (start_info->suspend) {
213 CloseHandle (start_info->suspend_event);
216 *out_tid = thread_id;
217 MONO_SEM_DESTROY (&(start_info->registered));
224 mono_native_thread_id_get (void)
226 return GetCurrentThreadId ();
230 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
236 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
238 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
242 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
246 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
248 ResumeThread (handle);
249 CloseHandle (handle);
252 #if HAVE_DECL___READFSDWORD==0
253 static MONO_ALWAYS_INLINE unsigned long long
254 __readfsdword (unsigned long offset)
257 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
258 __asm__ volatile ("movl %%fs:%1,%0"
259 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
265 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
267 MEMORY_BASIC_INFORMATION meminfo;
270 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
271 guint8 *stackTop = (guint8*)tib->StackBase;
272 guint8 *stackBottom = (guint8*)tib->StackLimit;
274 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
275 void* tib = (void*)__readfsdword(0x18);
276 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
277 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
280 Windows stacks are expanded on demand, one page at time. The TIB reports
281 only the currently allocated amount.
282 VirtualQuery will return the actual limit for the bottom, which is what we want.
284 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
285 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
287 *staddr = stackBottom;
288 *stsize = stackTop - stackBottom;
293 mono_threads_core_yield (void)
295 return SwitchToThread ();
299 mono_threads_core_exit (int exit_code)
301 ExitThread (exit_code);
305 mono_threads_core_unregister (MonoThreadInfo *info)
310 mono_threads_core_open_handle (void)
312 HANDLE thread_handle;
314 thread_handle = GetCurrentThread ();
315 g_assert (thread_handle);
318 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
319 * refer to the thread from other threads for things like aborting.
321 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
322 THREAD_ALL_ACCESS, TRUE, 0);
324 return thread_handle;
328 mono_threads_get_max_stack_size (void)
335 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
337 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
340 #if !defined(__GNUC__)
341 const DWORD MS_VC_EXCEPTION=0x406D1388;
343 typedef struct tagTHREADNAME_INFO
345 DWORD dwType; // Must be 0x1000.
346 LPCSTR szName; // Pointer to name (in user addr space).
347 DWORD dwThreadID; // Thread ID (-1=caller thread).
348 DWORD dwFlags; // Reserved for future use, must be zero.
354 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
356 #if !defined(__GNUC__)
357 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
358 THREADNAME_INFO info;
359 info.dwType = 0x1000;
361 info.dwThreadID = tid;
365 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
367 __except(EXCEPTION_EXECUTE_HANDLER) {
374 mono_threads_core_prepare_interrupt (HANDLE thread_handle)
380 mono_threads_core_finish_interrupt (gpointer wait_handle)
385 mono_threads_core_self_interrupt (void)
390 mono_threads_core_clear_interruption (void)