1 /**
2 * \file
3 * Low-level threading, windows version
4 *
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
7 *
8 * (C) 2011 Novell, Inc
9 */
10
11 #include <mono/utils/mono-threads.h>
12
13 #if defined(USE_WINDOWS_BACKEND)
14
15 #include <mono/utils/mono-compiler.h>
16 #include <mono/utils/mono-threads-debug.h>
17 #include <mono/utils/mono-os-wait.h>
18 #include <limits.h>
19
20 void
mono_threads_suspend_init(void)21 mono_threads_suspend_init (void)
22 {
23 }
24
25 gboolean
mono_threads_suspend_begin_async_suspend(MonoThreadInfo * info,gboolean interrupt_kernel)26 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
27 {
28 DWORD id = mono_thread_info_get_tid (info);
29 HANDLE handle;
30 DWORD result;
31
32 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
33 g_assert (handle);
34
35 result = SuspendThread (handle);
36 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
37 if (result == (DWORD)-1) {
38 CloseHandle (handle);
39 return FALSE;
40 }
41
42 /* Suspend logic assumes thread is really suspended before continuing below. Surprisingly SuspendThread */
43 /* is just an async request to the scheduler, meaning that the suspended thread can continue to run */
44 /* user mode code until scheduler gets around and process the request. This will cause a thread state race */
45 /* in mono's thread state machine implementation on Windows. By requesting a threads context after issuing a */
46 /* suspended request, this will wait until thread is suspended and thread context has been collected */
47 /* and returned. */
48 CONTEXT context;
49 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
50 if (!GetThreadContext (handle, &context)) {
51 CloseHandle (handle);
52 return FALSE;
53 }
54
55 /* We're in the middle of a self-suspend, resume and register */
56 if (!mono_threads_transition_finish_async_suspend (info)) {
57 mono_threads_add_to_pending_operation_set (info);
58 result = ResumeThread (handle);
59 g_assert (result == 1);
60 CloseHandle (handle);
61 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
62 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
63 return TRUE;
64 }
65 info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, &context);
66 THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
67 if (info->suspend_can_continue) {
68 if (interrupt_kernel)
69 mono_win32_interrupt_wait (info, handle, id);
70 } else {
71 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
72 }
73
74 CloseHandle (handle);
75 return TRUE;
76 }
77
78 gboolean
mono_threads_suspend_check_suspend_result(MonoThreadInfo * info)79 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
80 {
81 return info->suspend_can_continue;
82 }
83
84
85
86 void
mono_threads_suspend_abort_syscall(MonoThreadInfo * info)87 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
88 {
89 DWORD id = mono_thread_info_get_tid (info);
90 HANDLE handle;
91
92 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
93 g_assert (handle);
94
95 mono_win32_abort_wait (info, handle, id);
96
97 CloseHandle (handle);
98 }
99
100 gboolean
mono_threads_suspend_begin_async_resume(MonoThreadInfo * info)101 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
102 {
103 DWORD id = mono_thread_info_get_tid (info);
104 HANDLE handle;
105 DWORD result;
106
107 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
108 g_assert (handle);
109
110 if (info->async_target) {
111 MonoContext ctx;
112 CONTEXT context;
113 gboolean res;
114
115 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
116 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
117 info->async_target = info->user_data = NULL;
118
119 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
120
121 if (!GetThreadContext (handle, &context)) {
122 CloseHandle (handle);
123 return FALSE;
124 }
125
126 g_assert (context.ContextFlags & CONTEXT_INTEGER);
127 g_assert (context.ContextFlags & CONTEXT_CONTROL);
128
129 mono_monoctx_to_sigctx (&ctx, &context);
130
131 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
132 res = SetThreadContext (handle, &context);
133 if (!res) {
134 CloseHandle (handle);
135 return FALSE;
136 }
137 }
138
139 result = ResumeThread (handle);
140 CloseHandle (handle);
141
142 return result != (DWORD)-1;
143 }
144
145
146 void
mono_threads_suspend_register(MonoThreadInfo * info)147 mono_threads_suspend_register (MonoThreadInfo *info)
148 {
149 }
150
151 void
mono_threads_suspend_free(MonoThreadInfo * info)152 mono_threads_suspend_free (MonoThreadInfo *info)
153 {
154 }
155
156 void
mono_threads_suspend_init_signals(void)157 mono_threads_suspend_init_signals (void)
158 {
159 }
160
161 gint
mono_threads_suspend_search_alternative_signal(void)162 mono_threads_suspend_search_alternative_signal (void)
163 {
164 g_assert_not_reached ();
165 }
166
167 gint
mono_threads_suspend_get_suspend_signal(void)168 mono_threads_suspend_get_suspend_signal (void)
169 {
170 return -1;
171 }
172
173 gint
mono_threads_suspend_get_restart_signal(void)174 mono_threads_suspend_get_restart_signal (void)
175 {
176 return -1;
177 }
178
179 gint
mono_threads_suspend_get_abort_signal(void)180 mono_threads_suspend_get_abort_signal (void)
181 {
182 return -1;
183 }
184
185 #endif
186
187 #if defined (HOST_WIN32)
188
189 gboolean
mono_thread_platform_create_thread(MonoThreadStart thread_fn,gpointer thread_data,gsize * const stack_size,MonoNativeThreadId * tid)190 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
191 {
192 HANDLE result;
193 DWORD thread_id;
194
195 result = CreateThread (NULL, stack_size ? *stack_size : 0, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
196 if (!result)
197 return FALSE;
198
199 /* A new handle is open when attaching
200 * the thread, so we don't need this one */
201 CloseHandle (result);
202
203 if (tid)
204 *tid = thread_id;
205
206 if (stack_size) {
207 // TOOD: Use VirtualQuery to get correct value
208 // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
209 *stack_size = 2 * 1024 * 1024;
210 }
211
212 return TRUE;
213 }
214
215
216 MonoNativeThreadId
mono_native_thread_id_get(void)217 mono_native_thread_id_get (void)
218 {
219 return GetCurrentThreadId ();
220 }
221
222 gboolean
mono_native_thread_id_equals(MonoNativeThreadId id1,MonoNativeThreadId id2)223 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
224 {
225 return id1 == id2;
226 }
227
228 gboolean
mono_native_thread_create(MonoNativeThreadId * tid,gpointer func,gpointer arg)229 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
230 {
231 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
232 }
233
234 gboolean
mono_native_thread_join_handle(HANDLE thread_handle,gboolean close_handle)235 mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle)
236 {
237 DWORD res = WaitForSingleObject (thread_handle, INFINITE);
238
239 if (close_handle)
240 CloseHandle (thread_handle);
241
242 return res != WAIT_FAILED;
243 }
244
245 gboolean
mono_native_thread_join(MonoNativeThreadId tid)246 mono_native_thread_join (MonoNativeThreadId tid)
247 {
248 HANDLE handle;
249
250 if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
251 return FALSE;
252
253 return mono_native_thread_join_handle (handle, TRUE);
254 }
255
256 #if HAVE_DECL___READFSDWORD==0
257 static MONO_ALWAYS_INLINE unsigned long long
__readfsdword(unsigned long offset)258 __readfsdword (unsigned long offset)
259 {
260 unsigned long value;
261 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
262 __asm__ volatile ("movl %%fs:%1,%0"
263 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
264 return value;
265 }
266 #endif
267
268 void
mono_threads_platform_get_stack_bounds(guint8 ** staddr,size_t * stsize)269 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
270 {
271 MEMORY_BASIC_INFORMATION meminfo;
272 #ifdef _WIN64
273 /* win7 apis */
274 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
275 guint8 *stackTop = (guint8*)tib->StackBase;
276 guint8 *stackBottom = (guint8*)tib->StackLimit;
277 #else
278 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
279 void* tib = (void*)__readfsdword(0x18);
280 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
281 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
282 #endif
283 /*
284 Windows stacks are expanded on demand, one page at time. The TIB reports
285 only the currently allocated amount.
286 VirtualQuery will return the actual limit for the bottom, which is what we want.
287 */
288 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
289 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
290
291 *staddr = stackBottom;
292 *stsize = stackTop - stackBottom;
293
294 }
295
296 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
297 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
298 static gboolean is_wow64 = FALSE;
299 #endif
300
301 /* We do this at init time to avoid potential races with module opening */
302 void
mono_threads_platform_init(void)303 mono_threads_platform_init (void)
304 {
305 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
306 LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
307 if (is_wow64_func)
308 is_wow64_func (GetCurrentProcess (), &is_wow64);
309 #endif
310 }
311
312 /*
313 * When running x86 process under x64 system syscalls are done through WoW64. This
314 * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
315 * Apparently this transition invalidates the ESP that we would get from calling
316 * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
317 * to query whether the thread is in such a transition so we try to restart it later.
318 * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
319 */
320 gboolean
mono_threads_platform_in_critical_region(MonoNativeThreadId tid)321 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
322 {
323 gboolean ret = FALSE;
324 #if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
325 /* FIXME On cygwin these are not defined */
326 #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
327 if (is_wow64) {
328 HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
329 if (handle) {
330 CONTEXT context;
331 ZeroMemory (&context, sizeof (CONTEXT));
332 context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
333 if (GetThreadContext (handle, &context)) {
334 if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
335 (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
336 ret = TRUE;
337 }
338 CloseHandle (handle);
339 }
340 }
341 #endif
342 #endif
343 return ret;
344 }
345
346 gboolean
mono_threads_platform_yield(void)347 mono_threads_platform_yield (void)
348 {
349 return SwitchToThread ();
350 }
351
352 void
mono_threads_platform_exit(gsize exit_code)353 mono_threads_platform_exit (gsize exit_code)
354 {
355 ExitThread (exit_code);
356 }
357
358 int
mono_threads_get_max_stack_size(void)359 mono_threads_get_max_stack_size (void)
360 {
361 //FIXME
362 return INT_MAX;
363 }
364
365 #if defined(_MSC_VER)
366 const DWORD MS_VC_EXCEPTION=0x406D1388;
367 #pragma pack(push,8)
368 typedef struct tagTHREADNAME_INFO
369 {
370 DWORD dwType; // Must be 0x1000.
371 LPCSTR szName; // Pointer to name (in user addr space).
372 DWORD dwThreadID; // Thread ID (-1=caller thread).
373 DWORD dwFlags; // Reserved for future use, must be zero.
374 } THREADNAME_INFO;
375 #pragma pack(pop)
376 #endif
377
378 void
mono_native_thread_set_name(MonoNativeThreadId tid,const char * name)379 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
380 {
381 #if defined(_MSC_VER)
382 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
383 THREADNAME_INFO info;
384 info.dwType = 0x1000;
385 info.szName = name;
386 info.dwThreadID = tid;
387 info.dwFlags = 0;
388
389 __try {
390 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
391 }
392 __except(EXCEPTION_EXECUTE_HANDLER) {
393 }
394 #endif
395 }
396
397 #endif
398