[threads] Remove mono_threads_suspend_needs_abort_syscall (#4450)
[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_begin_async_resume (MonoThreadInfo *info)
99 {
100         DWORD id = mono_thread_info_get_tid (info);
101         HANDLE handle;
102         DWORD result;
103
104         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
105         g_assert (handle);
106
107         if (info->async_target) {
108                 MonoContext ctx;
109                 CONTEXT context;
110                 gboolean res;
111
112                 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
113                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
114                 info->async_target = info->user_data = NULL;
115
116                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
117
118                 if (!GetThreadContext (handle, &context)) {
119                         CloseHandle (handle);
120                         return FALSE;
121                 }
122
123                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
124                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
125
126                 mono_monoctx_to_sigctx (&ctx, &context);
127
128                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
129                 res = SetThreadContext (handle, &context);
130                 if (!res) {
131                         CloseHandle (handle);
132                         return FALSE;
133                 }
134         }
135
136         result = ResumeThread (handle);
137         CloseHandle (handle);
138
139         return result != (DWORD)-1;
140 }
141
142
143 void
144 mono_threads_suspend_register (MonoThreadInfo *info)
145 {
146 }
147
148 void
149 mono_threads_suspend_free (MonoThreadInfo *info)
150 {
151 }
152
153 void
154 mono_threads_suspend_init_signals (void)
155 {
156 }
157
158 gint
159 mono_threads_suspend_search_alternative_signal (void)
160 {
161         g_assert_not_reached ();
162 }
163
164 gint
165 mono_threads_suspend_get_suspend_signal (void)
166 {
167         return -1;
168 }
169
170 gint
171 mono_threads_suspend_get_restart_signal (void)
172 {
173         return -1;
174 }
175
176 gint
177 mono_threads_suspend_get_abort_signal (void)
178 {
179         return -1;
180 }
181
182 #endif
183
184 #if defined (HOST_WIN32)
185
186 int
187 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid)
188 {
189         HANDLE result;
190         DWORD thread_id;
191
192         result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
193         if (!result)
194                 return -1;
195
196         /* A new handle is open when attaching
197          * the thread, so we don't need this one */
198         CloseHandle (result);
199
200         if (out_tid)
201                 *out_tid = thread_id;
202
203         if (stack_size) {
204                 // TOOD: Use VirtualQuery to get correct value 
205                 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
206                 *stack_size = 2 * 1024 * 1024;
207         }
208
209         return 0;
210 }
211
212
213 MonoNativeThreadId
214 mono_native_thread_id_get (void)
215 {
216         return GetCurrentThreadId ();
217 }
218
219 gboolean
220 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
221 {
222         return id1 == id2;
223 }
224
225 gboolean
226 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
227 {
228         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
229 }
230
231 gboolean
232 mono_native_thread_join (MonoNativeThreadId tid)
233 {
234         HANDLE handle;
235
236         if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
237                 return FALSE;
238
239         DWORD res = WaitForSingleObject (handle, INFINITE);
240
241         CloseHandle (handle);
242
243         return res != WAIT_FAILED;
244 }
245
246 #if HAVE_DECL___READFSDWORD==0
247 static MONO_ALWAYS_INLINE unsigned long long
248 __readfsdword (unsigned long offset)
249 {
250         unsigned long value;
251         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
252    __asm__ volatile ("movl    %%fs:%1,%0"
253      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
254         return value;
255 }
256 #endif
257
258 void
259 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
260 {
261         MEMORY_BASIC_INFORMATION meminfo;
262 #ifdef _WIN64
263         /* win7 apis */
264         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
265         guint8 *stackTop = (guint8*)tib->StackBase;
266         guint8 *stackBottom = (guint8*)tib->StackLimit;
267 #else
268         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
269         void* tib = (void*)__readfsdword(0x18);
270         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
271         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
272 #endif
273         /*
274         Windows stacks are expanded on demand, one page at time. The TIB reports
275         only the currently allocated amount.
276         VirtualQuery will return the actual limit for the bottom, which is what we want.
277         */
278         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
279                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
280
281         *staddr = stackBottom;
282         *stsize = stackTop - stackBottom;
283
284 }
285
286 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
287 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
288 static gboolean is_wow64 = FALSE;
289 #endif
290
291 /* We do this at init time to avoid potential races with module opening */
292 void
293 mono_threads_platform_init (void)
294 {
295 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
296         LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
297         if (is_wow64_func)
298                 is_wow64_func (GetCurrentProcess (), &is_wow64);
299 #endif
300 }
301
302 /*
303  * When running x86 process under x64 system syscalls are done through WoW64. This
304  * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
305  * Apparently this transition invalidates the ESP that we would get from calling
306  * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
307  * to query whether the thread is in such a transition so we try to restart it later.
308  * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
309  */
310 gboolean
311 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
312 {
313         gboolean ret = FALSE;
314 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
315 /* FIXME On cygwin these are not defined */
316 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
317         if (is_wow64) {
318                 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
319                 if (handle) {
320                         CONTEXT context;
321                         ZeroMemory (&context, sizeof (CONTEXT));
322                         context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
323                         if (GetThreadContext (handle, &context)) {
324                                 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
325                                                 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
326                                         ret = TRUE;
327                         }
328                         CloseHandle (handle);
329                 }
330         }
331 #endif
332 #endif
333         return ret;
334 }
335
336 gboolean
337 mono_threads_platform_yield (void)
338 {
339         return SwitchToThread ();
340 }
341
342 void
343 mono_threads_platform_exit (gsize exit_code)
344 {
345         ExitThread (exit_code);
346 }
347
348 int
349 mono_threads_get_max_stack_size (void)
350 {
351         //FIXME
352         return INT_MAX;
353 }
354
355 #if defined(_MSC_VER)
356 const DWORD MS_VC_EXCEPTION=0x406D1388;
357 #pragma pack(push,8)
358 typedef struct tagTHREADNAME_INFO
359 {
360    DWORD dwType; // Must be 0x1000.
361    LPCSTR szName; // Pointer to name (in user addr space).
362    DWORD dwThreadID; // Thread ID (-1=caller thread).
363   DWORD dwFlags; // Reserved for future use, must be zero.
364 } THREADNAME_INFO;
365 #pragma pack(pop)
366 #endif
367
368 void
369 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
370 {
371 #if defined(_MSC_VER)
372         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
373         THREADNAME_INFO info;
374         info.dwType = 0x1000;
375         info.szName = name;
376         info.dwThreadID = tid;
377         info.dwFlags = 0;
378
379         __try {
380                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
381         }
382         __except(EXCEPTION_EXECUTE_HANDLER) {
383         }
384 #endif
385 }
386
387 #endif