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