Implement MachineKey.Protect and MachineKey.Unprotect
[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 }
47
48 gboolean
49 mono_threads_core_needs_abort_syscall (void)
50 {
51         return FALSE;
52 }
53
54 void
55 mono_threads_core_self_suspend (MonoThreadInfo *info)
56 {
57         g_assert_not_reached ();
58 }
59
60 gboolean
61 mono_threads_core_suspend (MonoThreadInfo *info)
62 {
63         DWORD id = mono_thread_info_get_tid (info);
64         HANDLE handle;
65         DWORD result;
66         gboolean res;
67
68         g_assert (id != GetCurrentThreadId ());
69
70         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
71         g_assert (handle);
72
73         result = SuspendThread (handle);
74         if (result == (DWORD)-1) {
75                 fprintf (stderr, "could not suspend thread %x (handle %p): %d\n", id, handle, GetLastError ()); fflush (stderr);
76                 CloseHandle (handle);
77                 return FALSE;
78         }
79
80         CloseHandle (handle);
81
82         res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->suspend_state, info);
83         g_assert (res);
84
85         return TRUE;
86 }
87
88 gboolean
89 mono_threads_core_resume (MonoThreadInfo *info)
90 {
91         DWORD id = mono_thread_info_get_tid (info);
92         HANDLE handle;
93         DWORD result;
94
95         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
96         g_assert (handle);
97
98         if (info->async_target) {
99                 MonoContext ctx;
100                 CONTEXT context;
101                 gboolean res;
102
103                 ctx = info->suspend_state.ctx;
104                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
105                 info->async_target = info->user_data = NULL;
106
107                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
108
109                 if (!GetThreadContext (handle, &context)) {
110                         CloseHandle (handle);
111                         return FALSE;
112                 }
113
114                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
115                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
116
117                 mono_monoctx_to_sigctx (&ctx, &context);
118
119                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
120                 res = SetThreadContext (handle, &context);
121                 g_assert (res);
122         }
123
124         result = ResumeThread (handle);
125         g_assert (result != (DWORD)-1);
126
127         CloseHandle (handle);
128
129         return result != (DWORD)-1;
130 }
131
132 void
133 mono_threads_platform_register (MonoThreadInfo *info)
134 {
135 }
136
137 void
138 mono_threads_platform_free (MonoThreadInfo *info)
139 {
140 }
141
142 typedef struct {
143         LPTHREAD_START_ROUTINE start_routine;
144         void *arg;
145         MonoSemType registered;
146         gboolean suspend;
147         HANDLE suspend_event;
148 } ThreadStartInfo;
149
150 static DWORD WINAPI
151 inner_start_thread (LPVOID arg)
152 {
153         ThreadStartInfo *start_info = arg;
154         void *t_arg = start_info->arg;
155         int post_result;
156         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
157         DWORD result;
158         gboolean suspend = start_info->suspend;
159         HANDLE suspend_event = start_info->suspend_event;
160         MonoThreadInfo *info;
161
162         info = mono_thread_info_attach (&result);
163         info->runtime_thread = TRUE;
164         info->create_suspended = suspend;
165
166         post_result = MONO_SEM_POST (&(start_info->registered));
167         g_assert (!post_result);
168
169         if (suspend) {
170                 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
171                 CloseHandle (suspend_event);
172         }
173
174         result = start_func (t_arg);
175
176         mono_thread_info_detach ();
177
178         return result;
179 }
180
181 HANDLE
182 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
183 {
184         ThreadStartInfo *start_info;
185         HANDLE result;
186         DWORD thread_id;
187
188         start_info = g_malloc0 (sizeof (ThreadStartInfo));
189         if (!start_info)
190                 return NULL;
191         MONO_SEM_INIT (&(start_info->registered), 0);
192         start_info->arg = arg;
193         start_info->start_routine = start_routine;
194         start_info->suspend = creation_flags & CREATE_SUSPENDED;
195         creation_flags &= ~CREATE_SUSPENDED;
196         if (start_info->suspend) {
197                 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
198                 if (!start_info->suspend_event)
199                         return NULL;
200         }
201
202         result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
203         if (result) {
204                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
205                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
206                 }
207                 if (start_info->suspend) {
208                         g_assert (SuspendThread (result) != (DWORD)-1);
209                         SetEvent (start_info->suspend_event);
210                 }
211         } else if (start_info->suspend) {
212                 CloseHandle (start_info->suspend_event);
213         }
214         if (out_tid)
215                 *out_tid = thread_id;
216         MONO_SEM_DESTROY (&(start_info->registered));
217         g_free (start_info);
218         return result;
219 }
220
221
222 MonoNativeThreadId
223 mono_native_thread_id_get (void)
224 {
225         return GetCurrentThreadId ();
226 }
227
228 gboolean
229 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
230 {
231         return id1 == id2;
232 }
233
234 gboolean
235 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
236 {
237         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
238 }
239
240 void
241 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
242 {
243         HANDLE handle;
244
245         handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
246         g_assert (handle);
247         ResumeThread (handle);
248         CloseHandle (handle);
249 }
250
251 #if HAVE_DECL___READFSDWORD==0
252 static MONO_ALWAYS_INLINE 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
372 gpointer
373 mono_threads_core_prepare_interrupt (HANDLE thread_handle)
374 {
375         return NULL;
376 }
377
378 void
379 mono_threads_core_finish_interrupt (gpointer wait_handle)
380 {
381 }
382
383 void
384 mono_threads_core_self_interrupt (void)
385 {
386 }
387
388 void
389 mono_threads_core_clear_interruption (void)
390 {
391 }
392
393 #endif