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