Merge pull request #1074 from esdrubal/bug18421
[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 "config.h"
11
12 #if defined(HOST_WIN32)
13
14 #include <mono/utils/mono-threads.h>
15 #include <limits.h>
16
17
18 void
19 mono_threads_init_platform (void)
20 {
21 }
22
23 void
24 mono_threads_core_interrupt (MonoThreadInfo *info)
25 {
26         g_assert (0);
27 }
28
29 void
30 mono_threads_core_abort_syscall (MonoThreadInfo *info)
31 {
32 }
33
34 gboolean
35 mono_threads_core_needs_abort_syscall (void)
36 {
37         return FALSE;
38 }
39
40 void
41 mono_threads_core_self_suspend (MonoThreadInfo *info)
42 {
43         g_assert_not_reached ();
44 }
45
46 gboolean
47 mono_threads_core_suspend (MonoThreadInfo *info)
48 {
49         g_assert_not_reached ();
50 }
51
52 gboolean
53 mono_threads_core_resume (MonoThreadInfo *info)
54 {
55         g_assert_not_reached ();
56 }
57
58 void
59 mono_threads_platform_register (MonoThreadInfo *info)
60 {
61 }
62
63 void
64 mono_threads_platform_free (MonoThreadInfo *info)
65 {
66 }
67
68 typedef struct {
69         LPTHREAD_START_ROUTINE start_routine;
70         void *arg;
71         MonoSemType registered;
72         gboolean suspend;
73         HANDLE suspend_event;
74 } ThreadStartInfo;
75
76 static DWORD WINAPI
77 inner_start_thread (LPVOID arg)
78 {
79         ThreadStartInfo *start_info = arg;
80         void *t_arg = start_info->arg;
81         int post_result;
82         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
83         DWORD result;
84         gboolean suspend = start_info->suspend;
85         HANDLE suspend_event = start_info->suspend_event;
86         MonoThreadInfo *info;
87
88         info = mono_thread_info_attach (&result);
89         info->runtime_thread = TRUE;
90         info->create_suspended = suspend;
91
92         post_result = MONO_SEM_POST (&(start_info->registered));
93         g_assert (!post_result);
94
95         if (suspend) {
96                 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
97                 CloseHandle (suspend_event);
98         }
99
100         result = start_func (t_arg);
101
102         mono_thread_info_detach ();
103
104         return result;
105 }
106
107 HANDLE
108 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
109 {
110         ThreadStartInfo *start_info;
111         HANDLE result;
112         DWORD thread_id;
113
114         start_info = g_malloc0 (sizeof (ThreadStartInfo));
115         if (!start_info)
116                 return NULL;
117         MONO_SEM_INIT (&(start_info->registered), 0);
118         start_info->arg = arg;
119         start_info->start_routine = start_routine;
120         start_info->suspend = creation_flags & CREATE_SUSPENDED;
121         creation_flags &= ~CREATE_SUSPENDED;
122         if (start_info->suspend) {
123                 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
124                 if (!start_info->suspend_event)
125                         return NULL;
126         }
127
128         result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
129         if (result) {
130                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
131                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
132                 }
133                 if (start_info->suspend) {
134                         g_assert (SuspendThread (result) != (DWORD)-1);
135                         SetEvent (start_info->suspend_event);
136                 }
137         } else if (start_info->suspend) {
138                 CloseHandle (start_info->suspend_event);
139         }
140         if (out_tid)
141                 *out_tid = thread_id;
142         MONO_SEM_DESTROY (&(start_info->registered));
143         g_free (start_info);
144         return result;
145 }
146
147
148 MonoNativeThreadId
149 mono_native_thread_id_get (void)
150 {
151         return GetCurrentThreadId ();
152 }
153
154 gboolean
155 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
156 {
157         return id1 == id2;
158 }
159
160 gboolean
161 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
162 {
163         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
164 }
165
166 void
167 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
168 {
169         HANDLE handle;
170
171         handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
172         g_assert (handle);
173         ResumeThread (handle);
174         CloseHandle (handle);
175 }
176
177 #if HAVE_DECL___READFSDWORD==0
178 static __inline__ __attribute__((always_inline))
179 unsigned long long
180 __readfsdword (unsigned long offset)
181 {
182         unsigned long value;
183         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
184    __asm__ volatile ("movl    %%fs:%1,%0"
185      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
186         return value;
187 }
188 #endif
189
190 void
191 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
192 {
193         MEMORY_BASIC_INFORMATION meminfo;
194 #ifdef TARGET_AMD64
195         /* win7 apis */
196         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
197         guint8 *stackTop = (guint8*)tib->StackBase;
198         guint8 *stackBottom = (guint8*)tib->StackLimit;
199 #else
200         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
201         void* tib = (void*)__readfsdword(0x18);
202         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
203         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
204 #endif
205         /*
206         Windows stacks are expanded on demand, one page at time. The TIB reports
207         only the currently allocated amount.
208         VirtualQuery will return the actual limit for the bottom, which is what we want.
209         */
210         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
211                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
212
213         *staddr = stackBottom;
214         *stsize = stackTop - stackBottom;
215
216 }
217
218 gboolean
219 mono_threads_core_yield (void)
220 {
221         return SwitchToThread ();
222 }
223
224 void
225 mono_threads_core_exit (int exit_code)
226 {
227         ExitThread (exit_code);
228 }
229
230 void
231 mono_threads_core_unregister (MonoThreadInfo *info)
232 {
233 }
234
235 HANDLE
236 mono_threads_core_open_handle (void)
237 {
238         HANDLE thread_handle;
239
240         thread_handle = GetCurrentThread ();
241         g_assert (thread_handle);
242
243         /*
244          * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
245          * refer to the thread from other threads for things like aborting.
246          */
247         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
248                                          THREAD_ALL_ACCESS, TRUE, 0);
249
250         return thread_handle;
251 }
252
253 int
254 mono_threads_get_max_stack_size (void)
255 {
256         //FIXME
257         return INT_MAX;
258 }
259
260 HANDLE
261 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
262 {
263         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
264 }
265
266 void
267 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
268 {
269 }
270
271 #endif