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