Merge pull request #3658 from kumpera/fix-ios-logging
[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         g_assert (info->handle);
247
248         CloseHandle (info->handle);
249         info->handle = NULL;
250 }
251
252 int
253 mono_threads_get_max_stack_size (void)
254 {
255         //FIXME
256         return INT_MAX;
257 }
258
259 gpointer
260 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
261 {
262         HANDLE thread_handle;
263
264         g_assert (info->handle);
265         DuplicateHandle (GetCurrentProcess (), info->handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
266
267         return thread_handle;
268 }
269
270 HANDLE
271 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
272 {
273         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
274 }
275
276 void
277 mono_threads_platform_close_thread_handle (HANDLE handle)
278 {
279         CloseHandle (handle);
280 }
281
282 #if defined(_MSC_VER)
283 const DWORD MS_VC_EXCEPTION=0x406D1388;
284 #pragma pack(push,8)
285 typedef struct tagTHREADNAME_INFO
286 {
287    DWORD dwType; // Must be 0x1000.
288    LPCSTR szName; // Pointer to name (in user addr space).
289    DWORD dwThreadID; // Thread ID (-1=caller thread).
290   DWORD dwFlags; // Reserved for future use, must be zero.
291 } THREADNAME_INFO;
292 #pragma pack(pop)
293 #endif
294
295 void
296 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
297 {
298 #if defined(_MSC_VER)
299         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
300         THREADNAME_INFO info;
301         info.dwType = 0x1000;
302         info.szName = name;
303         info.dwThreadID = tid;
304         info.dwFlags = 0;
305
306         __try {
307                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
308         }
309         __except(EXCEPTION_EXECUTE_HANDLER) {
310         }
311 #endif
312 }
313
314 void
315 mono_threads_platform_set_exited (gpointer handle)
316 {
317 }
318
319 void
320 mono_threads_platform_init (void)
321 {
322 }
323
324 #endif