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