Merge pull request #1674 from alexanderkyte/gdb_support
[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 <limits.h>
16
17
18 void
19 mono_threads_init_platform (void)
20 {
21 }
22
23 static void CALLBACK
24 interrupt_apc (ULONG_PTR param)
25 {
26 }
27
28 void
29 mono_threads_core_abort_syscall (MonoThreadInfo *info)
30 {
31         DWORD id = mono_thread_info_get_tid (info);
32         HANDLE handle;
33
34         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
35         g_assert (handle);
36
37         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
38
39         CloseHandle (handle);
40 }
41
42 gboolean
43 mono_threads_core_needs_abort_syscall (void)
44 {
45         return TRUE;
46 }
47
48 gboolean
49 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
50 {
51         DWORD id = mono_thread_info_get_tid (info);
52         HANDLE handle;
53         DWORD result;
54         gboolean res;
55
56         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
57         g_assert (handle);
58
59         result = SuspendThread (handle);
60         THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
61         if (result == (DWORD)-1) {
62                 CloseHandle (handle);
63                 return FALSE;
64         }
65
66         /* We're in the middle of a self-suspend, resume and register */
67         if (!mono_threads_transition_finish_async_suspend (info)) {
68                 mono_threads_add_to_pending_operation_set (info);
69                 result = ResumeThread (handle);
70                 g_assert (result == 1);
71                 CloseHandle (handle);
72                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
73                 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
74                 return TRUE;
75         }
76         res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
77         THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
78         if (res) {
79                 //FIXME do we need to QueueUserAPC on this case?
80                 if (interrupt_kernel)
81                         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
82         } else {
83                 mono_threads_transition_async_suspend_compensation (info);
84                 result = ResumeThread (handle);
85                 g_assert (result == 1);
86                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
87         }
88
89         CloseHandle (handle);
90         return res;
91 }
92
93 gboolean
94 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
95 {
96         return TRUE;
97 }
98
99 gboolean
100 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
101 {
102         DWORD id = mono_thread_info_get_tid (info);
103         HANDLE handle;
104         DWORD result;
105
106         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
107         g_assert (handle);
108
109         if (info->async_target) {
110                 MonoContext ctx;
111                 CONTEXT context;
112                 gboolean res;
113
114                 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
115                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
116                 info->async_target = info->user_data = NULL;
117
118                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
119
120                 if (!GetThreadContext (handle, &context)) {
121                         CloseHandle (handle);
122                         return FALSE;
123                 }
124
125                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
126                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
127
128                 mono_monoctx_to_sigctx (&ctx, &context);
129
130                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
131                 res = SetThreadContext (handle, &context);
132                 if (!res) {
133                         CloseHandle (handle);
134                         return FALSE;
135                 }
136         }
137
138         result = ResumeThread (handle);
139         CloseHandle (handle);
140
141         return result != (DWORD)-1;
142 }
143
144
145 void
146 mono_threads_platform_register (MonoThreadInfo *info)
147 {
148 }
149
150 void
151 mono_threads_platform_free (MonoThreadInfo *info)
152 {
153 }
154
155 #endif
156
157 #if defined (HOST_WIN32)
158
159 typedef struct {
160         LPTHREAD_START_ROUTINE start_routine;
161         void *arg;
162         MonoSemType registered;
163         gboolean suspend;
164         HANDLE suspend_event;
165 } ThreadStartInfo;
166
167 static DWORD WINAPI
168 inner_start_thread (LPVOID arg)
169 {
170         ThreadStartInfo *start_info = arg;
171         void *t_arg = start_info->arg;
172         int post_result;
173         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
174         DWORD result;
175         gboolean suspend = start_info->suspend;
176         HANDLE suspend_event = start_info->suspend_event;
177         MonoThreadInfo *info;
178
179         info = mono_thread_info_attach (&result);
180         info->runtime_thread = TRUE;
181         info->create_suspended = suspend;
182
183         post_result = MONO_SEM_POST (&(start_info->registered));
184         g_assert (!post_result);
185
186         if (suspend) {
187                 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
188                 CloseHandle (suspend_event);
189         }
190
191         result = start_func (t_arg);
192
193         mono_thread_info_detach ();
194
195         return result;
196 }
197
198 HANDLE
199 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
200 {
201         ThreadStartInfo *start_info;
202         HANDLE result;
203         DWORD thread_id;
204
205         start_info = g_malloc0 (sizeof (ThreadStartInfo));
206         if (!start_info)
207                 return NULL;
208         MONO_SEM_INIT (&(start_info->registered), 0);
209         start_info->arg = arg;
210         start_info->start_routine = start_routine;
211         start_info->suspend = creation_flags & CREATE_SUSPENDED;
212         creation_flags &= ~CREATE_SUSPENDED;
213         if (start_info->suspend) {
214                 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
215                 if (!start_info->suspend_event)
216                         return NULL;
217         }
218
219         result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
220         if (result) {
221                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
222                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
223                 }
224                 if (start_info->suspend) {
225                         g_assert (SuspendThread (result) != (DWORD)-1);
226                         SetEvent (start_info->suspend_event);
227                 }
228         } else if (start_info->suspend) {
229                 CloseHandle (start_info->suspend_event);
230         }
231         if (out_tid)
232                 *out_tid = thread_id;
233         MONO_SEM_DESTROY (&(start_info->registered));
234         g_free (start_info);
235         return result;
236 }
237
238
239 MonoNativeThreadId
240 mono_native_thread_id_get (void)
241 {
242         return GetCurrentThreadId ();
243 }
244
245 gboolean
246 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
247 {
248         return id1 == id2;
249 }
250
251 gboolean
252 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
253 {
254         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
255 }
256
257 void
258 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
259 {
260         HANDLE handle;
261
262         handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
263         g_assert (handle);
264         ResumeThread (handle);
265         CloseHandle (handle);
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_core_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_core_yield (void)
310 {
311         return SwitchToThread ();
312 }
313
314 void
315 mono_threads_core_exit (int exit_code)
316 {
317         ExitThread (exit_code);
318 }
319
320 void
321 mono_threads_core_unregister (MonoThreadInfo *info)
322 {
323 }
324
325 HANDLE
326 mono_threads_core_open_handle (void)
327 {
328         HANDLE thread_handle;
329
330         thread_handle = GetCurrentThread ();
331         g_assert (thread_handle);
332
333         /*
334          * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
335          * refer to the thread from other threads for things like aborting.
336          */
337         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
338                                          THREAD_ALL_ACCESS, TRUE, 0);
339
340         return thread_handle;
341 }
342
343 int
344 mono_threads_get_max_stack_size (void)
345 {
346         //FIXME
347         return INT_MAX;
348 }
349
350 HANDLE
351 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
352 {
353         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
354 }
355
356 #if defined(_MSC_VER)
357 const DWORD MS_VC_EXCEPTION=0x406D1388;
358 #pragma pack(push,8)
359 typedef struct tagTHREADNAME_INFO
360 {
361    DWORD dwType; // Must be 0x1000.
362    LPCSTR szName; // Pointer to name (in user addr space).
363    DWORD dwThreadID; // Thread ID (-1=caller thread).
364   DWORD dwFlags; // Reserved for future use, must be zero.
365 } THREADNAME_INFO;
366 #pragma pack(pop)
367 #endif
368
369 void
370 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
371 {
372 #if defined(_MSC_VER)
373         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
374         THREADNAME_INFO info;
375         info.dwType = 0x1000;
376         info.szName = name;
377         info.dwThreadID = tid;
378         info.dwFlags = 0;
379
380         __try {
381                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
382         }
383         __except(EXCEPTION_EXECUTE_HANDLER) {
384         }
385 #endif
386 }
387
388
389 gpointer
390 mono_threads_core_prepare_interrupt (HANDLE thread_handle)
391 {
392         return NULL;
393 }
394
395 void
396 mono_threads_core_finish_interrupt (gpointer wait_handle)
397 {
398 }
399
400 void
401 mono_threads_core_self_interrupt (void)
402 {
403 }
404
405 void
406 mono_threads_core_clear_interruption (void)
407 {
408 }
409
410 #endif