Fix shutdown race between mono_thread_manage and mono_thread_detach_internal. (#5599)
[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_handle (HANDLE thread_handle, gboolean close_handle)
223 {
224         DWORD res = WaitForSingleObject (thread_handle, INFINITE);
225
226         if (close_handle)
227                 CloseHandle (thread_handle);
228
229         return res != WAIT_FAILED;
230 }
231
232 gboolean
233 mono_native_thread_join (MonoNativeThreadId tid)
234 {
235         HANDLE handle;
236
237         if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
238                 return FALSE;
239
240         return mono_native_thread_join_handle (handle, TRUE);
241 }
242
243 #if HAVE_DECL___READFSDWORD==0
244 static MONO_ALWAYS_INLINE unsigned long long
245 __readfsdword (unsigned long offset)
246 {
247         unsigned long value;
248         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
249    __asm__ volatile ("movl    %%fs:%1,%0"
250      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
251         return value;
252 }
253 #endif
254
255 void
256 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
257 {
258         MEMORY_BASIC_INFORMATION meminfo;
259 #ifdef _WIN64
260         /* win7 apis */
261         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
262         guint8 *stackTop = (guint8*)tib->StackBase;
263         guint8 *stackBottom = (guint8*)tib->StackLimit;
264 #else
265         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
266         void* tib = (void*)__readfsdword(0x18);
267         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
268         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
269 #endif
270         /*
271         Windows stacks are expanded on demand, one page at time. The TIB reports
272         only the currently allocated amount.
273         VirtualQuery will return the actual limit for the bottom, which is what we want.
274         */
275         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
276                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
277
278         *staddr = stackBottom;
279         *stsize = stackTop - stackBottom;
280
281 }
282
283 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
284 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
285 static gboolean is_wow64 = FALSE;
286 #endif
287
288 /* We do this at init time to avoid potential races with module opening */
289 void
290 mono_threads_platform_init (void)
291 {
292 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
293         LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
294         if (is_wow64_func)
295                 is_wow64_func (GetCurrentProcess (), &is_wow64);
296 #endif
297 }
298
299 /*
300  * When running x86 process under x64 system syscalls are done through WoW64. This
301  * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
302  * Apparently this transition invalidates the ESP that we would get from calling
303  * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
304  * to query whether the thread is in such a transition so we try to restart it later.
305  * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
306  */
307 gboolean
308 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
309 {
310         gboolean ret = FALSE;
311 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
312 /* FIXME On cygwin these are not defined */
313 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
314         if (is_wow64) {
315                 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
316                 if (handle) {
317                         CONTEXT context;
318                         ZeroMemory (&context, sizeof (CONTEXT));
319                         context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
320                         if (GetThreadContext (handle, &context)) {
321                                 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
322                                                 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
323                                         ret = TRUE;
324                         }
325                         CloseHandle (handle);
326                 }
327         }
328 #endif
329 #endif
330         return ret;
331 }
332
333 gboolean
334 mono_threads_platform_yield (void)
335 {
336         return SwitchToThread ();
337 }
338
339 void
340 mono_threads_platform_exit (gsize exit_code)
341 {
342         ExitThread (exit_code);
343 }
344
345 int
346 mono_threads_get_max_stack_size (void)
347 {
348         //FIXME
349         return INT_MAX;
350 }
351
352 #if defined(_MSC_VER)
353 const DWORD MS_VC_EXCEPTION=0x406D1388;
354 #pragma pack(push,8)
355 typedef struct tagTHREADNAME_INFO
356 {
357    DWORD dwType; // Must be 0x1000.
358    LPCSTR szName; // Pointer to name (in user addr space).
359    DWORD dwThreadID; // Thread ID (-1=caller thread).
360   DWORD dwFlags; // Reserved for future use, must be zero.
361 } THREADNAME_INFO;
362 #pragma pack(pop)
363 #endif
364
365 void
366 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
367 {
368 #if defined(_MSC_VER)
369         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
370         THREADNAME_INFO info;
371         info.dwType = 0x1000;
372         info.szName = name;
373         info.dwThreadID = tid;
374         info.dwFlags = 0;
375
376         __try {
377                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
378         }
379         __except(EXCEPTION_EXECUTE_HANDLER) {
380         }
381 #endif
382 }
383
384 #endif