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