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