Merge pull request #3522 from henricm/fix-csharp-compiler-path-windows
[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 int
152 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid)
153 {
154         HANDLE result;
155         DWORD thread_id;
156
157         result = CreateThread (NULL, stack_size, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
158         if (!result)
159                 return -1;
160
161         /* A new handle is open when attaching
162          * the thread, so we don't need this one */
163         CloseHandle (result);
164
165         if (out_tid)
166                 *out_tid = thread_id;
167
168         return 0;
169 }
170
171
172 MonoNativeThreadId
173 mono_native_thread_id_get (void)
174 {
175         return GetCurrentThreadId ();
176 }
177
178 gboolean
179 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
180 {
181         return id1 == id2;
182 }
183
184 gboolean
185 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
186 {
187         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
188 }
189
190 #if HAVE_DECL___READFSDWORD==0
191 static MONO_ALWAYS_INLINE unsigned long long
192 __readfsdword (unsigned long offset)
193 {
194         unsigned long value;
195         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
196    __asm__ volatile ("movl    %%fs:%1,%0"
197      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
198         return value;
199 }
200 #endif
201
202 void
203 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
204 {
205         MEMORY_BASIC_INFORMATION meminfo;
206 #ifdef _WIN64
207         /* win7 apis */
208         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
209         guint8 *stackTop = (guint8*)tib->StackBase;
210         guint8 *stackBottom = (guint8*)tib->StackLimit;
211 #else
212         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
213         void* tib = (void*)__readfsdword(0x18);
214         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
215         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
216 #endif
217         /*
218         Windows stacks are expanded on demand, one page at time. The TIB reports
219         only the currently allocated amount.
220         VirtualQuery will return the actual limit for the bottom, which is what we want.
221         */
222         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
223                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
224
225         *staddr = stackBottom;
226         *stsize = stackTop - stackBottom;
227
228 }
229
230 gboolean
231 mono_threads_platform_yield (void)
232 {
233         return SwitchToThread ();
234 }
235
236 void
237 mono_threads_platform_exit (int exit_code)
238 {
239         mono_thread_info_detach ();
240         ExitThread (exit_code);
241 }
242
243 void
244 mono_threads_platform_unregister (MonoThreadInfo *info)
245 {
246         mono_threads_platform_set_exited (info);
247 }
248
249 int
250 mono_threads_get_max_stack_size (void)
251 {
252         //FIXME
253         return INT_MAX;
254 }
255
256 gpointer
257 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
258 {
259         HANDLE thread_handle;
260
261         g_assert (info->handle);
262         DuplicateHandle (GetCurrentProcess (), info->handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
263
264         return thread_handle;
265 }
266
267 HANDLE
268 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
269 {
270         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
271 }
272
273 void
274 mono_threads_platform_close_thread_handle (HANDLE handle)
275 {
276         CloseHandle (handle);
277 }
278
279 #if defined(_MSC_VER)
280 const DWORD MS_VC_EXCEPTION=0x406D1388;
281 #pragma pack(push,8)
282 typedef struct tagTHREADNAME_INFO
283 {
284    DWORD dwType; // Must be 0x1000.
285    LPCSTR szName; // Pointer to name (in user addr space).
286    DWORD dwThreadID; // Thread ID (-1=caller thread).
287   DWORD dwFlags; // Reserved for future use, must be zero.
288 } THREADNAME_INFO;
289 #pragma pack(pop)
290 #endif
291
292 void
293 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
294 {
295 #if defined(_MSC_VER)
296         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
297         THREADNAME_INFO info;
298         info.dwType = 0x1000;
299         info.szName = name;
300         info.dwThreadID = tid;
301         info.dwFlags = 0;
302
303         __try {
304                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
305         }
306         __except(EXCEPTION_EXECUTE_HANDLER) {
307         }
308 #endif
309 }
310
311 void
312 mono_threads_platform_set_exited (MonoThreadInfo *info)
313 {
314         g_assert (info->handle);
315         // No need to call CloseHandle() here since the InternalThread
316         // destructor will close the handle when the finalizer thread calls it
317         info->handle = NULL;
318 }
319
320 void
321 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
322 {
323         /* TODO */
324 }
325
326 void
327 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
328 {
329         g_assert_not_reached ();
330 }
331
332 void
333 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
334 {
335         g_assert_not_reached ();
336 }
337
338 void
339 mono_threads_platform_init (void)
340 {
341 }
342
343 #endif