[mono-threads] Use create_suspended_sem on windows (#3482)
[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         ExitThread (exit_code);
300 }
301
302 void
303 mono_threads_platform_unregister (MonoThreadInfo *info)
304 {
305 }
306
307 int
308 mono_threads_get_max_stack_size (void)
309 {
310         //FIXME
311         return INT_MAX;
312 }
313
314 HANDLE
315 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
316 {
317         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
318 }
319
320 #if defined(_MSC_VER)
321 const DWORD MS_VC_EXCEPTION=0x406D1388;
322 #pragma pack(push,8)
323 typedef struct tagTHREADNAME_INFO
324 {
325    DWORD dwType; // Must be 0x1000.
326    LPCSTR szName; // Pointer to name (in user addr space).
327    DWORD dwThreadID; // Thread ID (-1=caller thread).
328   DWORD dwFlags; // Reserved for future use, must be zero.
329 } THREADNAME_INFO;
330 #pragma pack(pop)
331 #endif
332
333 void
334 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
335 {
336 #if defined(_MSC_VER)
337         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
338         THREADNAME_INFO info;
339         info.dwType = 0x1000;
340         info.szName = name;
341         info.dwThreadID = tid;
342         info.dwFlags = 0;
343
344         __try {
345                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
346         }
347         __except(EXCEPTION_EXECUTE_HANDLER) {
348         }
349 #endif
350 }
351
352 void
353 mono_threads_platform_set_exited (MonoThreadInfo *info)
354 {
355 }
356
357 void
358 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
359 {
360         /* TODO */
361 }
362
363 void
364 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
365 {
366         g_assert_not_reached ();
367 }
368
369 void
370 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
371 {
372         g_assert_not_reached ();
373 }
374
375 MonoThreadPriority
376 mono_threads_platform_get_priority (MonoThreadInfo *info)
377 {
378         g_assert (info->handle);
379         return GetThreadPriority (info->handle) + 2;
380 }
381
382 gboolean
383 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
384 {
385         g_assert (info->handle);
386         return SetThreadPriority (info->handle, priority - 2);
387 }
388
389 void
390 mono_threads_platform_init (void)
391 {
392 }
393
394 #endif