d2b3b5e2ea357281d6e7b9ce9e47c6edf496e1d1
[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 handle;
158 } ThreadStartInfo;
159
160 static DWORD WINAPI
161 inner_start_thread (LPVOID arg)
162 {
163         ThreadStartInfo *start_info = arg;
164         void *t_arg = start_info->arg;
165         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
166         DWORD result;
167         gboolean suspend = start_info->suspend;
168         MonoThreadInfo *info;
169         int res;
170
171         info = mono_thread_info_attach (&result);
172         info->runtime_thread = TRUE;
173
174         start_info->handle = info->handle;
175
176         mono_threads_platform_set_priority(info, start_info->priority);
177
178         if (suspend) {
179                 info->create_suspended = TRUE;
180                 mono_coop_sem_init (&info->create_suspended_sem, 0);
181         }
182
183         mono_coop_sem_post (&(start_info->registered));
184
185         if (suspend) {
186                 res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
187                 g_assert (res != -1);
188
189                 mono_coop_sem_destroy (&info->create_suspended_sem);
190         }
191
192         result = start_func (t_arg);
193
194         mono_thread_info_detach ();
195
196         return result;
197 }
198
199 HANDLE
200 mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
201 {
202         ThreadStartInfo start_info;
203         HANDLE result;
204         DWORD thread_id;
205         guint32 creation_flags = tp->creation_flags;
206         int res;
207
208         memset (&start_info, 0, sizeof (start_info));
209         mono_coop_sem_init (&(start_info.registered), 0);
210         start_info.arg = arg;
211         start_info.start_routine = start_routine;
212         start_info.suspend = creation_flags & CREATE_SUSPENDED;
213         start_info.priority = tp->priority;
214         creation_flags &= ~CREATE_SUSPENDED;
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 (out_tid)
226                 *out_tid = thread_id;
227         mono_coop_sem_destroy (&(start_info.registered));
228         return start_info.handle;
229 }
230
231
232 MonoNativeThreadId
233 mono_native_thread_id_get (void)
234 {
235         return GetCurrentThreadId ();
236 }
237
238 gboolean
239 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
240 {
241         return id1 == id2;
242 }
243
244 gboolean
245 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
246 {
247         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
248 }
249
250 #if HAVE_DECL___READFSDWORD==0
251 static MONO_ALWAYS_INLINE unsigned long long
252 __readfsdword (unsigned long offset)
253 {
254         unsigned long value;
255         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
256    __asm__ volatile ("movl    %%fs:%1,%0"
257      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
258         return value;
259 }
260 #endif
261
262 void
263 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
264 {
265         MEMORY_BASIC_INFORMATION meminfo;
266 #ifdef _WIN64
267         /* win7 apis */
268         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
269         guint8 *stackTop = (guint8*)tib->StackBase;
270         guint8 *stackBottom = (guint8*)tib->StackLimit;
271 #else
272         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
273         void* tib = (void*)__readfsdword(0x18);
274         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
275         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
276 #endif
277         /*
278         Windows stacks are expanded on demand, one page at time. The TIB reports
279         only the currently allocated amount.
280         VirtualQuery will return the actual limit for the bottom, which is what we want.
281         */
282         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
283                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
284
285         *staddr = stackBottom;
286         *stsize = stackTop - stackBottom;
287
288 }
289
290 gboolean
291 mono_threads_platform_yield (void)
292 {
293         return SwitchToThread ();
294 }
295
296 void
297 mono_threads_platform_exit (int exit_code)
298 {
299         mono_thread_info_detach ();
300         ExitThread (exit_code);
301 }
302
303 void
304 mono_threads_platform_unregister (MonoThreadInfo *info)
305 {
306         mono_threads_platform_set_exited (info);
307 }
308
309 int
310 mono_threads_get_max_stack_size (void)
311 {
312         //FIXME
313         return INT_MAX;
314 }
315
316 HANDLE
317 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
318 {
319         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
320 }
321
322 #if defined(_MSC_VER)
323 const DWORD MS_VC_EXCEPTION=0x406D1388;
324 #pragma pack(push,8)
325 typedef struct tagTHREADNAME_INFO
326 {
327    DWORD dwType; // Must be 0x1000.
328    LPCSTR szName; // Pointer to name (in user addr space).
329    DWORD dwThreadID; // Thread ID (-1=caller thread).
330   DWORD dwFlags; // Reserved for future use, must be zero.
331 } THREADNAME_INFO;
332 #pragma pack(pop)
333 #endif
334
335 void
336 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
337 {
338 #if defined(_MSC_VER)
339         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
340         THREADNAME_INFO info;
341         info.dwType = 0x1000;
342         info.szName = name;
343         info.dwThreadID = tid;
344         info.dwFlags = 0;
345
346         __try {
347                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
348         }
349         __except(EXCEPTION_EXECUTE_HANDLER) {
350         }
351 #endif
352 }
353
354 void
355 mono_threads_platform_set_exited (MonoThreadInfo *info)
356 {
357         g_assert (info->handle);
358         // No need to call CloseHandle() here since the InternalThread
359         // destructor will close the handle when the finalizer thread calls it
360         info->handle = NULL;
361 }
362
363 void
364 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
365 {
366         /* TODO */
367 }
368
369 void
370 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
371 {
372         g_assert_not_reached ();
373 }
374
375 void
376 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
377 {
378         g_assert_not_reached ();
379 }
380
381 MonoThreadPriority
382 mono_threads_platform_get_priority (MonoThreadInfo *info)
383 {
384         g_assert (info->handle);
385         return GetThreadPriority (info->handle) + 2;
386 }
387
388 gboolean
389 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
390 {
391         g_assert (info->handle);
392         return SetThreadPriority (info->handle, priority - 2);
393 }
394
395 void
396 mono_threads_platform_init (void)
397 {
398 }
399
400 #endif