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