Merge pull request #3453 from ntherning/cleanup-AssemblyTest-temp-file-handling
[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_suspend_init (void)
20 {
21 }
22
23 static void CALLBACK
24 interrupt_apc (ULONG_PTR param)
25 {
26 }
27
28 gboolean
29 mono_threads_suspend_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
35         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
36         g_assert (handle);
37
38         result = SuspendThread (handle);
39         THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
40         if (result == (DWORD)-1) {
41                 CloseHandle (handle);
42                 return FALSE;
43         }
44
45         /* We're in the middle of a self-suspend, resume and register */
46         if (!mono_threads_transition_finish_async_suspend (info)) {
47                 mono_threads_add_to_pending_operation_set (info);
48                 result = ResumeThread (handle);
49                 g_assert (result == 1);
50                 CloseHandle (handle);
51                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
52                 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
53                 return TRUE;
54         }
55         info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
56         THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
57         if (info->suspend_can_continue) {
58                 //FIXME do we need to QueueUserAPC on this case?
59                 if (interrupt_kernel)
60                         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
61         } else {
62                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
63         }
64
65         CloseHandle (handle);
66         return info->suspend_can_continue;
67 }
68
69 gboolean
70 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
71 {
72         return info->suspend_can_continue;
73 }
74
75 gboolean
76 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
77 {
78         DWORD id = mono_thread_info_get_tid (info);
79         HANDLE handle;
80         DWORD result;
81
82         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
83         g_assert (handle);
84
85         if (info->async_target) {
86                 MonoContext ctx;
87                 CONTEXT context;
88                 gboolean res;
89
90                 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
91                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
92                 info->async_target = info->user_data = NULL;
93
94                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
95
96                 if (!GetThreadContext (handle, &context)) {
97                         CloseHandle (handle);
98                         return FALSE;
99                 }
100
101                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
102                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
103
104                 mono_monoctx_to_sigctx (&ctx, &context);
105
106                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
107                 res = SetThreadContext (handle, &context);
108                 if (!res) {
109                         CloseHandle (handle);
110                         return FALSE;
111                 }
112         }
113
114         result = ResumeThread (handle);
115         CloseHandle (handle);
116
117         return result != (DWORD)-1;
118 }
119
120
121 void
122 mono_threads_suspend_register (MonoThreadInfo *info)
123 {
124 }
125
126 void
127 mono_threads_suspend_free (MonoThreadInfo *info)
128 {
129 }
130
131 #endif
132
133 #if defined (HOST_WIN32)
134
135 void
136 mono_threads_platform_register (MonoThreadInfo *info)
137 {
138         HANDLE thread_handle;
139
140         thread_handle = GetCurrentThread ();
141         g_assert (thread_handle);
142
143         /* The handle returned by GetCurrentThread () is a pseudo handle, so it can't
144          * be used to refer to the thread from other threads for things like aborting. */
145         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
146
147         g_assert (!info->handle);
148         info->handle = thread_handle;
149 }
150
151 typedef struct {
152         LPTHREAD_START_ROUTINE start_routine;
153         void *arg;
154         gint32 priority;
155         MonoCoopSem registered;
156         gboolean suspend;
157         HANDLE suspend_event;
158         HANDLE handle;
159 } ThreadStartInfo;
160
161 static DWORD WINAPI
162 inner_start_thread (LPVOID arg)
163 {
164         ThreadStartInfo *start_info = arg;
165         void *t_arg = start_info->arg;
166         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
167         DWORD result;
168         gboolean suspend = start_info->suspend;
169         HANDLE suspend_event = start_info->suspend_event;
170         MonoThreadInfo *info;
171
172         info = mono_thread_info_attach (&result);
173         info->runtime_thread = TRUE;
174         info->create_suspended = suspend;
175
176         start_info->handle = info->handle;
177
178         mono_threads_platform_set_priority(info, start_info->priority);
179
180         mono_coop_sem_post (&(start_info->registered));
181
182         if (suspend) {
183                 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
184                 CloseHandle (suspend_event);
185         }
186
187         result = start_func (t_arg);
188
189         mono_thread_info_detach ();
190
191         return result;
192 }
193
194 HANDLE
195 mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
196 {
197         ThreadStartInfo start_info;
198         HANDLE result;
199         DWORD thread_id;
200         guint32 creation_flags = tp->creation_flags;
201         int res;
202
203         memset (&start_info, 0, sizeof (start_info));
204         mono_coop_sem_init (&(start_info.registered), 0);
205         start_info.arg = arg;
206         start_info.start_routine = start_routine;
207         start_info.suspend = creation_flags & CREATE_SUSPENDED;
208         start_info.priority = tp->priority;
209         creation_flags &= ~CREATE_SUSPENDED;
210         if (start_info.suspend) {
211                 start_info.suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
212                 if (!start_info.suspend_event)
213                         return NULL;
214         }
215
216         result = CreateThread (NULL, tp->stack_size, inner_start_thread, &start_info, creation_flags, &thread_id);
217         if (result) {
218                 res = mono_coop_sem_wait (&(start_info.registered), MONO_SEM_FLAGS_NONE);
219                 g_assert (res != -1);
220
221                 /* A new handle has been opened when attaching
222                  * the thread, so we don't need this one */
223                 CloseHandle (result);
224
225                 if (start_info.suspend) {
226                         g_assert (SuspendThread (start_info.handle) != (DWORD)-1);
227                         SetEvent (start_info.suspend_event);
228                 }
229         } else if (start_info.suspend) {
230                 CloseHandle (start_info.suspend_event);
231         }
232         if (out_tid)
233                 *out_tid = thread_id;
234         mono_coop_sem_destroy (&(start_info.registered));
235         return start_info.handle;
236 }
237
238
239 MonoNativeThreadId
240 mono_native_thread_id_get (void)
241 {
242         return GetCurrentThreadId ();
243 }
244
245 gboolean
246 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
247 {
248         return id1 == id2;
249 }
250
251 gboolean
252 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
253 {
254         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
255 }
256
257 void
258 mono_threads_platform_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
259 {
260         HANDLE handle;
261
262         handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
263         g_assert (handle);
264         ResumeThread (handle);
265         CloseHandle (handle);
266 }
267
268 #if HAVE_DECL___READFSDWORD==0
269 static MONO_ALWAYS_INLINE unsigned long long
270 __readfsdword (unsigned long offset)
271 {
272         unsigned long value;
273         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
274    __asm__ volatile ("movl    %%fs:%1,%0"
275      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
276         return value;
277 }
278 #endif
279
280 void
281 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
282 {
283         MEMORY_BASIC_INFORMATION meminfo;
284 #ifdef _WIN64
285         /* win7 apis */
286         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
287         guint8 *stackTop = (guint8*)tib->StackBase;
288         guint8 *stackBottom = (guint8*)tib->StackLimit;
289 #else
290         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
291         void* tib = (void*)__readfsdword(0x18);
292         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
293         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
294 #endif
295         /*
296         Windows stacks are expanded on demand, one page at time. The TIB reports
297         only the currently allocated amount.
298         VirtualQuery will return the actual limit for the bottom, which is what we want.
299         */
300         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
301                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
302
303         *staddr = stackBottom;
304         *stsize = stackTop - stackBottom;
305
306 }
307
308 gboolean
309 mono_threads_platform_yield (void)
310 {
311         return SwitchToThread ();
312 }
313
314 void
315 mono_threads_platform_exit (int exit_code)
316 {
317         ExitThread (exit_code);
318 }
319
320 void
321 mono_threads_platform_unregister (MonoThreadInfo *info)
322 {
323 }
324
325 int
326 mono_threads_get_max_stack_size (void)
327 {
328         //FIXME
329         return INT_MAX;
330 }
331
332 HANDLE
333 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
334 {
335         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
336 }
337
338 #if defined(_MSC_VER)
339 const DWORD MS_VC_EXCEPTION=0x406D1388;
340 #pragma pack(push,8)
341 typedef struct tagTHREADNAME_INFO
342 {
343    DWORD dwType; // Must be 0x1000.
344    LPCSTR szName; // Pointer to name (in user addr space).
345    DWORD dwThreadID; // Thread ID (-1=caller thread).
346   DWORD dwFlags; // Reserved for future use, must be zero.
347 } THREADNAME_INFO;
348 #pragma pack(pop)
349 #endif
350
351 void
352 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
353 {
354 #if defined(_MSC_VER)
355         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
356         THREADNAME_INFO info;
357         info.dwType = 0x1000;
358         info.szName = name;
359         info.dwThreadID = tid;
360         info.dwFlags = 0;
361
362         __try {
363                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
364         }
365         __except(EXCEPTION_EXECUTE_HANDLER) {
366         }
367 #endif
368 }
369
370 void
371 mono_threads_platform_set_exited (MonoThreadInfo *info)
372 {
373 }
374
375 void
376 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
377 {
378         /* TODO */
379 }
380
381 void
382 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
383 {
384         g_assert_not_reached ();
385 }
386
387 void
388 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
389 {
390         g_assert_not_reached ();
391 }
392
393 MonoThreadPriority
394 mono_threads_platform_get_priority (MonoThreadInfo *info)
395 {
396         g_assert (info->handle);
397         return GetThreadPriority (info->handle) + 2;
398 }
399
400 gboolean
401 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
402 {
403         g_assert (info->handle);
404         return SetThreadPriority (info->handle, priority - 2);
405 }
406
407 void
408 mono_threads_platform_init (void)
409 {
410 }
411
412 #endif