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