[amd64] Save missing register
[mono.git] / mono / utils / mono-threads-windows.c
1 /**
2  * \file
3  * Low-level threading, windows version
4  *
5  * Author:
6  *      Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * (C) 2011 Novell, Inc
9  */
10
11 #include <mono/utils/mono-threads.h>
12
13 #if defined(USE_WINDOWS_BACKEND)
14
15 #include <mono/utils/mono-compiler.h>
16 #include <mono/utils/mono-threads-debug.h>
17 #include <limits.h>
18
19
20 void
21 mono_threads_suspend_init (void)
22 {
23 }
24
25 static void CALLBACK
26 interrupt_apc (ULONG_PTR param)
27 {
28 }
29
30 gboolean
31 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
32 {
33         DWORD id = mono_thread_info_get_tid (info);
34         HANDLE handle;
35         DWORD result;
36
37         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
38         g_assert (handle);
39
40         result = SuspendThread (handle);
41         THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
42         if (result == (DWORD)-1) {
43                 CloseHandle (handle);
44                 return FALSE;
45         }
46
47         /* We're in the middle of a self-suspend, resume and register */
48         if (!mono_threads_transition_finish_async_suspend (info)) {
49                 mono_threads_add_to_pending_operation_set (info);
50                 result = ResumeThread (handle);
51                 g_assert (result == 1);
52                 CloseHandle (handle);
53                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
54                 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
55                 return TRUE;
56         }
57         info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
58         THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
59         if (info->suspend_can_continue) {
60                 //FIXME do we need to QueueUserAPC on this case?
61                 if (interrupt_kernel)
62                         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
63         } else {
64                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
65         }
66
67         CloseHandle (handle);
68         return TRUE;
69 }
70
71 gboolean
72 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
73 {
74         return info->suspend_can_continue;
75 }
76
77 static void CALLBACK
78 abort_apc (ULONG_PTR param)
79 {
80         THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
81 }
82
83 void
84 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
85 {
86         DWORD id = mono_thread_info_get_tid (info);
87         HANDLE handle;
88
89         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
90         g_assert (handle);
91
92         THREADS_INTERRUPT_DEBUG ("%06d - Aborting syscall in thread %06d", GetCurrentThreadId (), id);
93         QueueUserAPC ((PAPCFUNC)abort_apc, handle, (ULONG_PTR)NULL);
94
95         CloseHandle (handle);
96 }
97
98 gboolean
99 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
100 {
101         DWORD id = mono_thread_info_get_tid (info);
102         HANDLE handle;
103         DWORD result;
104
105         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
106         g_assert (handle);
107
108         if (info->async_target) {
109                 MonoContext ctx;
110                 CONTEXT context;
111                 gboolean res;
112
113                 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
114                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
115                 info->async_target = info->user_data = NULL;
116
117                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
118
119                 if (!GetThreadContext (handle, &context)) {
120                         CloseHandle (handle);
121                         return FALSE;
122                 }
123
124                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
125                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
126
127                 mono_monoctx_to_sigctx (&ctx, &context);
128
129                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
130                 res = SetThreadContext (handle, &context);
131                 if (!res) {
132                         CloseHandle (handle);
133                         return FALSE;
134                 }
135         }
136
137         result = ResumeThread (handle);
138         CloseHandle (handle);
139
140         return result != (DWORD)-1;
141 }
142
143
144 void
145 mono_threads_suspend_register (MonoThreadInfo *info)
146 {
147 }
148
149 void
150 mono_threads_suspend_free (MonoThreadInfo *info)
151 {
152 }
153
154 void
155 mono_threads_suspend_init_signals (void)
156 {
157 }
158
159 gint
160 mono_threads_suspend_search_alternative_signal (void)
161 {
162         g_assert_not_reached ();
163 }
164
165 gint
166 mono_threads_suspend_get_suspend_signal (void)
167 {
168         return -1;
169 }
170
171 gint
172 mono_threads_suspend_get_restart_signal (void)
173 {
174         return -1;
175 }
176
177 gint
178 mono_threads_suspend_get_abort_signal (void)
179 {
180         return -1;
181 }
182
183 #endif
184
185 #if defined (HOST_WIN32)
186
187 gboolean
188 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
189 {
190         HANDLE result;
191         DWORD thread_id;
192
193         result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
194         if (!result)
195                 return FALSE;
196
197         /* A new handle is open when attaching
198          * the thread, so we don't need this one */
199         CloseHandle (result);
200
201         if (tid)
202                 *tid = thread_id;
203
204         if (stack_size) {
205                 // TOOD: Use VirtualQuery to get correct value 
206                 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
207                 *stack_size = 2 * 1024 * 1024;
208         }
209
210         return TRUE;
211 }
212
213
214 MonoNativeThreadId
215 mono_native_thread_id_get (void)
216 {
217         return GetCurrentThreadId ();
218 }
219
220 gboolean
221 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
222 {
223         return id1 == id2;
224 }
225
226 gboolean
227 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
228 {
229         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
230 }
231
232 gboolean
233 mono_native_thread_join (MonoNativeThreadId tid)
234 {
235         HANDLE handle;
236
237         if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
238                 return FALSE;
239
240         DWORD res = WaitForSingleObject (handle, INFINITE);
241
242         CloseHandle (handle);
243
244         return res != WAIT_FAILED;
245 }
246
247 #if HAVE_DECL___READFSDWORD==0
248 static MONO_ALWAYS_INLINE unsigned long long
249 __readfsdword (unsigned long offset)
250 {
251         unsigned long value;
252         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
253    __asm__ volatile ("movl    %%fs:%1,%0"
254      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
255         return value;
256 }
257 #endif
258
259 void
260 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
261 {
262         MEMORY_BASIC_INFORMATION meminfo;
263 #ifdef _WIN64
264         /* win7 apis */
265         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
266         guint8 *stackTop = (guint8*)tib->StackBase;
267         guint8 *stackBottom = (guint8*)tib->StackLimit;
268 #else
269         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
270         void* tib = (void*)__readfsdword(0x18);
271         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
272         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
273 #endif
274         /*
275         Windows stacks are expanded on demand, one page at time. The TIB reports
276         only the currently allocated amount.
277         VirtualQuery will return the actual limit for the bottom, which is what we want.
278         */
279         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
280                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
281
282         *staddr = stackBottom;
283         *stsize = stackTop - stackBottom;
284
285 }
286
287 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
288 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
289 static gboolean is_wow64 = FALSE;
290 #endif
291
292 /* We do this at init time to avoid potential races with module opening */
293 void
294 mono_threads_platform_init (void)
295 {
296 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
297         LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
298         if (is_wow64_func)
299                 is_wow64_func (GetCurrentProcess (), &is_wow64);
300 #endif
301 }
302
303 /*
304  * When running x86 process under x64 system syscalls are done through WoW64. This
305  * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
306  * Apparently this transition invalidates the ESP that we would get from calling
307  * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
308  * to query whether the thread is in such a transition so we try to restart it later.
309  * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
310  */
311 gboolean
312 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
313 {
314         gboolean ret = FALSE;
315 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
316 /* FIXME On cygwin these are not defined */
317 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
318         if (is_wow64) {
319                 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
320                 if (handle) {
321                         CONTEXT context;
322                         ZeroMemory (&context, sizeof (CONTEXT));
323                         context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
324                         if (GetThreadContext (handle, &context)) {
325                                 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
326                                                 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
327                                         ret = TRUE;
328                         }
329                         CloseHandle (handle);
330                 }
331         }
332 #endif
333 #endif
334         return ret;
335 }
336
337 gboolean
338 mono_threads_platform_yield (void)
339 {
340         return SwitchToThread ();
341 }
342
343 void
344 mono_threads_platform_exit (gsize exit_code)
345 {
346         ExitThread (exit_code);
347 }
348
349 int
350 mono_threads_get_max_stack_size (void)
351 {
352         //FIXME
353         return INT_MAX;
354 }
355
356 #if defined(_MSC_VER)
357 const DWORD MS_VC_EXCEPTION=0x406D1388;
358 #pragma pack(push,8)
359 typedef struct tagTHREADNAME_INFO
360 {
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.
365 } THREADNAME_INFO;
366 #pragma pack(pop)
367 #endif
368
369 void
370 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
371 {
372 #if defined(_MSC_VER)
373         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
374         THREADNAME_INFO info;
375         info.dwType = 0x1000;
376         info.szName = name;
377         info.dwThreadID = tid;
378         info.dwFlags = 0;
379
380         __try {
381                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
382         }
383         __except(EXCEPTION_EXECUTE_HANDLER) {
384         }
385 #endif
386 }
387
388 #endif