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