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