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