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