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