Merge pull request #3831 from rolfbjarne/watchos-fix-defaultproxy-test
[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 void
193 mono_threads_platform_register (MonoThreadInfo *info)
194 {
195         HANDLE thread_handle;
196
197         thread_handle = GetCurrentThread ();
198         g_assert (thread_handle);
199
200         /* The handle returned by GetCurrentThread () is a pseudo handle, so it can't
201          * be used to refer to the thread from other threads for things like aborting. */
202         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
203
204         g_assert (!info->handle);
205         info->handle = thread_handle;
206 }
207
208 int
209 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid)
210 {
211         HANDLE result;
212         DWORD thread_id;
213
214         result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
215         if (!result)
216                 return -1;
217
218         /* A new handle is open when attaching
219          * the thread, so we don't need this one */
220         CloseHandle (result);
221
222         if (out_tid)
223                 *out_tid = thread_id;
224
225         if (stack_size) {
226                 // TOOD: Use VirtualQuery to get correct value 
227                 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
228                 *stack_size = 2 * 1024 * 1024;
229         }
230
231         return 0;
232 }
233
234
235 MonoNativeThreadId
236 mono_native_thread_id_get (void)
237 {
238         return GetCurrentThreadId ();
239 }
240
241 gboolean
242 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
243 {
244         return id1 == id2;
245 }
246
247 gboolean
248 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
249 {
250         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
251 }
252
253 gboolean
254 mono_native_thread_join (MonoNativeThreadId tid)
255 {
256         HANDLE handle;
257
258         if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
259                 return FALSE;
260
261         DWORD res = WaitForSingleObject (handle, INFINITE);
262
263         CloseHandle (handle);
264
265         return res != WAIT_FAILED;
266 }
267
268 #if HAVE_DECL___READFSDWORD==0
269 static MONO_ALWAYS_INLINE unsigned long long
270 __readfsdword (unsigned long offset)
271 {
272         unsigned long value;
273         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
274    __asm__ volatile ("movl    %%fs:%1,%0"
275      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
276         return value;
277 }
278 #endif
279
280 void
281 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
282 {
283         MEMORY_BASIC_INFORMATION meminfo;
284 #ifdef _WIN64
285         /* win7 apis */
286         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
287         guint8 *stackTop = (guint8*)tib->StackBase;
288         guint8 *stackBottom = (guint8*)tib->StackLimit;
289 #else
290         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
291         void* tib = (void*)__readfsdword(0x18);
292         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
293         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
294 #endif
295         /*
296         Windows stacks are expanded on demand, one page at time. The TIB reports
297         only the currently allocated amount.
298         VirtualQuery will return the actual limit for the bottom, which is what we want.
299         */
300         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
301                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
302
303         *staddr = stackBottom;
304         *stsize = stackTop - stackBottom;
305
306 }
307
308 gboolean
309 mono_threads_platform_yield (void)
310 {
311         return SwitchToThread ();
312 }
313
314 void
315 mono_threads_platform_exit (int exit_code)
316 {
317         mono_thread_info_detach ();
318         ExitThread (exit_code);
319 }
320
321 void
322 mono_threads_platform_unregister (MonoThreadInfo *info)
323 {
324         g_assert (info->handle);
325
326         CloseHandle (info->handle);
327         info->handle = NULL;
328 }
329
330 int
331 mono_threads_get_max_stack_size (void)
332 {
333         //FIXME
334         return INT_MAX;
335 }
336
337 gpointer
338 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
339 {
340         HANDLE thread_handle;
341
342         g_assert (info->handle);
343         DuplicateHandle (GetCurrentProcess (), info->handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
344
345         return thread_handle;
346 }
347
348 HANDLE
349 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
350 {
351         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
352 }
353
354 void
355 mono_threads_platform_close_thread_handle (HANDLE handle)
356 {
357         CloseHandle (handle);
358 }
359
360 #if defined(_MSC_VER)
361 const DWORD MS_VC_EXCEPTION=0x406D1388;
362 #pragma pack(push,8)
363 typedef struct tagTHREADNAME_INFO
364 {
365    DWORD dwType; // Must be 0x1000.
366    LPCSTR szName; // Pointer to name (in user addr space).
367    DWORD dwThreadID; // Thread ID (-1=caller thread).
368   DWORD dwFlags; // Reserved for future use, must be zero.
369 } THREADNAME_INFO;
370 #pragma pack(pop)
371 #endif
372
373 void
374 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
375 {
376 #if defined(_MSC_VER)
377         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
378         THREADNAME_INFO info;
379         info.dwType = 0x1000;
380         info.szName = name;
381         info.dwThreadID = tid;
382         info.dwFlags = 0;
383
384         __try {
385                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
386         }
387         __except(EXCEPTION_EXECUTE_HANDLER) {
388         }
389 #endif
390 }
391
392 void
393 mono_threads_platform_set_exited (gpointer handle)
394 {
395 }
396
397 void
398 mono_threads_platform_init (void)
399 {
400 }
401
402 #endif