1 /**
2  * \file
3  * Low-level threading
4  *
5  * Author:
6  *	Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * Copyright 2011 Novell, Inc (http://www.novell.com)
9  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12 
13 #include <config.h>
14 
15 /* enable pthread extensions */
16 #ifdef TARGET_MACH
17 #define _DARWIN_C_SOURCE
18 #endif
19 
20 #include <mono/utils/mono-compiler.h>
21 #include <mono/utils/mono-os-semaphore.h>
22 #include <mono/utils/mono-threads.h>
23 #include <mono/utils/mono-tls.h>
24 #include <mono/utils/hazard-pointer.h>
25 #include <mono/utils/mono-memory-model.h>
26 #include <mono/utils/mono-mmap.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-lazy-init.h>
30 #include <mono/utils/mono-coop-mutex.h>
31 #include <mono/utils/mono-coop-semaphore.h>
32 #include <mono/utils/mono-threads-coop.h>
33 #include <mono/utils/mono-threads-debug.h>
34 #include <mono/utils/os-event.h>
35 #include <mono/utils/w32api.h>
36 
37 #include <errno.h>
38 
39 #if defined(__MACH__)
40 #include <mono/utils/mach-support.h>
41 #endif
42 
43 /*
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
47 
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
50 
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
54 
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
61 */
62 static MonoSemType global_suspend_semaphore;
63 
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
68 #ifdef HAVE_KW_THREAD
69 static __thread gint32 tls_small_id = -1;
70 #else
71 static MonoNativeTlsKey small_id_key;
72 #endif
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
75 
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
78 
79 static mono_mutex_t join_mutex;
80 
81 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
82 
83 /*warn at 50 ms*/
84 #define SLEEP_DURATION_BEFORE_WARNING (50)
85 /*never aborts */
86 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
87 
88 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
89 	    sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
90 
91 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
92 
93 void
mono_threads_notify_initiator_of_abort(MonoThreadInfo * info)94 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
95 {
96 	THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
97 	mono_atomic_inc_i32 (&abort_posts);
98 	mono_os_sem_post (&suspend_semaphore);
99 }
100 
101 void
mono_threads_notify_initiator_of_suspend(MonoThreadInfo * info)102 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
103 {
104 	THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
105 	mono_atomic_inc_i32 (&suspend_posts);
106 	mono_os_sem_post (&suspend_semaphore);
107 }
108 
109 void
mono_threads_notify_initiator_of_resume(MonoThreadInfo * info)110 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
111 {
112 	THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
113 	mono_atomic_inc_i32 (&resume_posts);
114 	mono_os_sem_post (&suspend_semaphore);
115 }
116 
117 static gboolean
begin_async_suspend(MonoThreadInfo * info,gboolean interrupt_kernel)118 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
119 {
120 	if (mono_threads_is_coop_enabled ()) {
121 		/* There's nothing else to do after we async request the thread to suspend */
122 		mono_threads_add_to_pending_operation_set (info);
123 		return TRUE;
124 	}
125 
126 	return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
127 }
128 
129 static gboolean
check_async_suspend(MonoThreadInfo * info)130 check_async_suspend (MonoThreadInfo *info)
131 {
132 	if (mono_threads_is_coop_enabled ()) {
133 		/* Async suspend can't async fail on coop */
134 		return TRUE;
135 	}
136 
137 	return mono_threads_suspend_check_suspend_result (info);
138 }
139 
140 static void
resume_async_suspended(MonoThreadInfo * info)141 resume_async_suspended (MonoThreadInfo *info)
142 {
143 	if (mono_threads_is_coop_enabled ())
144 		g_assert_not_reached ();
145 
146 	g_assert (mono_threads_suspend_begin_async_resume (info));
147 }
148 
149 static void
resume_self_suspended(MonoThreadInfo * info)150 resume_self_suspended (MonoThreadInfo* info)
151 {
152 	THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
153 	mono_os_sem_post (&info->resume_semaphore);
154 }
155 
156 void
mono_thread_info_wait_for_resume(MonoThreadInfo * info)157 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
158 {
159 	int res;
160 	THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
161 	res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
162 	g_assert (res != -1);
163 }
164 
165 static void
resume_blocking_suspended(MonoThreadInfo * info)166 resume_blocking_suspended (MonoThreadInfo* info)
167 {
168 	THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
169 	mono_os_sem_post (&info->resume_semaphore);
170 }
171 
172 void
mono_threads_add_to_pending_operation_set(MonoThreadInfo * info)173 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
174 {
175 	THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
176 	++pending_suspends;
177 	mono_atomic_inc_i32 (&pending_ops);
178 }
179 
180 void
mono_threads_begin_global_suspend(void)181 mono_threads_begin_global_suspend (void)
182 {
183 	size_t ps = pending_suspends;
184 	if (G_UNLIKELY (ps != 0))
185 		g_error ("pending_suspends = %d, but must be 0", ps);
186 	THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
187 		abort_posts, waits_done, pending_ops);
188 	g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
189 	mono_threads_coop_begin_global_suspend ();
190 }
191 
192 void
mono_threads_end_global_suspend(void)193 mono_threads_end_global_suspend (void)
194 {
195 	size_t ps = pending_suspends;
196 	if (G_UNLIKELY (ps != 0))
197 		g_error ("pending_suspends = %d, but must be 0", ps);
198 	THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
199 		abort_posts, waits_done, pending_ops);
200 	g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
201 	mono_threads_coop_end_global_suspend ();
202 }
203 
204 static void
dump_threads(void)205 dump_threads (void)
206 {
207 	MonoThreadInfo *cur = mono_thread_info_current ();
208 
209 	MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
210 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
211 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
212 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
213 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
214 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
215 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
216 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
217 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
218 	MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
219 
220 	FOREACH_THREAD_SAFE (info) {
221 #ifdef TARGET_MACH
222 		char thread_name [256] = { 0 };
223 		pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
224 
225 		MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x  %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
226 #else
227 		MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x  %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
228 #endif
229 	} FOREACH_THREAD_SAFE_END
230 }
231 
232 gboolean
mono_threads_wait_pending_operations(void)233 mono_threads_wait_pending_operations (void)
234 {
235 	int i;
236 	int c = pending_suspends;
237 
238 	/* Wait threads to park */
239 	THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
240 	if (pending_suspends) {
241 		MonoStopwatch suspension_time;
242 		mono_stopwatch_start (&suspension_time);
243 		for (i = 0; i < pending_suspends; ++i) {
244 			THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
245 			mono_atomic_inc_i32 (&waits_done);
246 			if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
247 				continue;
248 			mono_stopwatch_stop (&suspension_time);
249 
250 			dump_threads ();
251 
252 			MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
253 			g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
254 		}
255 		mono_stopwatch_stop (&suspension_time);
256 		THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
257 
258 	}
259 
260 	pending_suspends = 0;
261 
262 	return c > 0;
263 }
264 
265 
266 //Thread initialization code
267 
268 static inline void
mono_hazard_pointer_clear_all(MonoThreadHazardPointers * hp,int retain)269 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
270 {
271 	if (retain != 0)
272 		mono_hazard_pointer_clear (hp, 0);
273 	if (retain != 1)
274 		mono_hazard_pointer_clear (hp, 1);
275 	if (retain != 2)
276 		mono_hazard_pointer_clear (hp, 2);
277 }
278 
279 /*
280 If return non null Hazard Pointer 1 holds the return value.
281 */
282 MonoThreadInfo*
mono_thread_info_lookup(MonoNativeThreadId id)283 mono_thread_info_lookup (MonoNativeThreadId id)
284 {
285 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
286 
287 	if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
288 		mono_hazard_pointer_clear_all (hp, -1);
289 		return NULL;
290 	}
291 
292 	mono_hazard_pointer_clear_all (hp, 1);
293 	return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
294 }
295 
296 static gboolean
mono_thread_info_insert(MonoThreadInfo * info)297 mono_thread_info_insert (MonoThreadInfo *info)
298 {
299 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
300 
301 	if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
302 		mono_hazard_pointer_clear_all (hp, -1);
303 		return FALSE;
304 	}
305 
306 	mono_hazard_pointer_clear_all (hp, -1);
307 	return TRUE;
308 }
309 
310 static gboolean
mono_thread_info_remove(MonoThreadInfo * info)311 mono_thread_info_remove (MonoThreadInfo *info)
312 {
313 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
314 	gboolean res;
315 
316 	THREADS_DEBUG ("removing info %p\n", info);
317 	res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
318 	mono_hazard_pointer_clear_all (hp, -1);
319 	return res;
320 }
321 
322 static void
free_thread_info(gpointer mem)323 free_thread_info (gpointer mem)
324 {
325 	MonoThreadInfo *info = (MonoThreadInfo *) mem;
326 
327 	mono_os_sem_destroy (&info->resume_semaphore);
328 	mono_threads_suspend_free (info);
329 
330 	g_free (info);
331 }
332 
333 /*
334  * mono_thread_info_register_small_id
335  *
336  * Registers a small ID for the current thread. This is a 16-bit value uniquely
337  * identifying the current thread. If the current thread already has a small ID
338  * assigned, that small ID will be returned; otherwise, the newly assigned small
339  * ID is returned.
340  */
341 int
mono_thread_info_register_small_id(void)342 mono_thread_info_register_small_id (void)
343 {
344 	int small_id = mono_thread_info_get_small_id ();
345 
346 	if (small_id != -1)
347 		return small_id;
348 
349 	small_id = mono_thread_small_id_alloc ();
350 #ifdef HAVE_KW_THREAD
351 	tls_small_id = small_id;
352 #else
353 	mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
354 #endif
355 	return small_id;
356 }
357 
358 static void
thread_handle_destroy(gpointer data)359 thread_handle_destroy (gpointer data)
360 {
361 	MonoThreadHandle *thread_handle;
362 
363 	thread_handle = (MonoThreadHandle*) data;
364 
365 	mono_os_event_destroy (&thread_handle->event);
366 	g_free (thread_handle);
367 }
368 
369 static gboolean
register_thread(MonoThreadInfo * info)370 register_thread (MonoThreadInfo *info)
371 {
372 	size_t stsize = 0;
373 	guint8 *staddr = NULL;
374 	gboolean result;
375 
376 	info->small_id = mono_thread_info_register_small_id ();
377 	mono_thread_info_set_tid (info, mono_native_thread_id_get ());
378 
379 	info->handle = g_new0 (MonoThreadHandle, 1);
380 	mono_refcount_init (info->handle, thread_handle_destroy);
381 	mono_os_event_init (&info->handle->event, FALSE);
382 
383 	mono_os_sem_init (&info->resume_semaphore, 0);
384 
385 	/*set TLS early so SMR works */
386 	mono_native_tls_set_value (thread_info_key, info);
387 
388 	mono_thread_info_get_stack_bounds (&staddr, &stsize);
389 	g_assert (staddr);
390 	g_assert (stsize);
391 	info->stack_start_limit = staddr;
392 	info->stack_end = staddr + stsize;
393 
394 	info->stackdata = g_byte_array_new ();
395 
396 	info->internal_thread_gchandle = G_MAXUINT32;
397 
398 	info->profiler_signal_ack = 1;
399 
400 	mono_threads_suspend_register (info);
401 
402 	THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
403 
404 	if (threads_callbacks.thread_attach) {
405 		if (!threads_callbacks.thread_attach (info)) {
406 			// g_warning ("thread registation failed\n");
407 			mono_native_tls_set_value (thread_info_key, NULL);
408 			return FALSE;
409 		}
410 	}
411 
412 	/*
413 	Transition it before taking any locks or publishing itself to reduce the chance
414 	of others witnessing a detached thread.
415 	We can reasonably expect that until this thread gets published, no other thread will
416 	try to manipulate it.
417 	*/
418 	mono_threads_transition_attach (info);
419 	mono_thread_info_suspend_lock ();
420 	/*If this fail it means a given thread has been registered twice, which doesn't make sense. */
421 	result = mono_thread_info_insert (info);
422 	g_assert (result);
423 	mono_thread_info_suspend_unlock ();
424 
425 	return TRUE;
426 }
427 
428 static void
429 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
430 
431 static void
432 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
433 
434 static void
unregister_thread(void * arg)435 unregister_thread (void *arg)
436 {
437 	gpointer gc_unsafe_stackdata;
438 	MonoThreadInfo *info;
439 	int small_id;
440 	gboolean result;
441 	gpointer handle;
442 
443 	info = (MonoThreadInfo *) arg;
444 	g_assert (info);
445 	g_assert (mono_thread_info_is_current (info));
446 	g_assert (mono_thread_info_is_live (info));
447 
448 	/* Pump the HP queue while the thread is alive.*/
449 	mono_thread_hazardous_try_free_some ();
450 
451 	small_id = info->small_id;
452 
453 	/* We only enter the GC unsafe region, as when exiting this function, the thread
454 	 * will be detached, and the current MonoThreadInfo* will be destroyed. */
455 	mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
456 
457 	THREADS_DEBUG ("unregistering info %p\n", info);
458 
459 	mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
460 
461 	/*
462 	 * TLS destruction order is not reliable so small_id might be cleaned up
463 	 * before us.
464 	 */
465 #ifndef HAVE_KW_THREAD
466 	mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
467 #endif
468 
469 	/* we need to duplicate it, as the info->handle is going
470 	 * to be closed when unregistering from the platform */
471 	handle = mono_threads_open_thread_handle (info->handle);
472 
473 	/*
474 	First perform the callback that requires no locks.
475 	This callback has the potential of taking other locks, so we do it before.
476 	After it completes, the thread remains functional.
477 	*/
478 	if (threads_callbacks.thread_detach)
479 		threads_callbacks.thread_detach (info);
480 
481 	mono_thread_info_suspend_lock_with_info (info);
482 
483 	/*
484 	Now perform the callback that must be done under locks.
485 	This will render the thread useless and non-suspendable, so it must
486 	be done while holding the suspend lock to give no other thread chance
487 	to suspend it.
488 	*/
489 	if (threads_callbacks.thread_detach_with_lock)
490 		threads_callbacks.thread_detach_with_lock (info);
491 
492 	/* The thread is no longer active, so unref its handle */
493 	mono_threads_close_thread_handle (info->handle);
494 	info->handle = NULL;
495 
496 	result = mono_thread_info_remove (info);
497 	g_assert (result);
498 	mono_threads_transition_detach (info);
499 
500 	mono_thread_info_suspend_unlock ();
501 
502 	g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
503 
504 	/*now it's safe to free the thread info.*/
505 	mono_thread_hazardous_try_free (info, free_thread_info);
506 
507 	mono_thread_small_id_free (small_id);
508 
509 	mono_threads_signal_thread_handle (handle);
510 
511 	mono_threads_close_thread_handle (handle);
512 
513 	mono_native_tls_set_value (thread_info_key, NULL);
514 }
515 
516 static void
thread_exited_dtor(void * arg)517 thread_exited_dtor (void *arg)
518 {
519 #if defined(__MACH__)
520 	/*
521 	 * Since we use pthread dtors to clean up thread data, if a thread
522 	 * is attached to the runtime by another pthread dtor after our dtor
523 	 * has ran, it will never be detached, leading to various problems
524 	 * since the thread ids etc. will be reused while they are still in
525 	 * the threads hashtables etc.
526 	 * Dtors are called in a loop until all user tls entries are 0,
527 	 * but the loop has a maximum count (4), so if we set the tls
528 	 * variable every time, it will remain set when system tls dtors
529 	 * are ran. This allows mono_thread_info_is_exiting () to detect
530 	 * whenever the thread is exiting, even if it is executed from a
531 	 * system tls dtor (i.e. obj-c dealloc methods).
532 	 */
533 	mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
534 #endif
535 }
536 
537 MonoThreadInfo*
mono_thread_info_current_unchecked(void)538 mono_thread_info_current_unchecked (void)
539 {
540 	return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
541 }
542 
543 
544 MonoThreadInfo*
mono_thread_info_current(void)545 mono_thread_info_current (void)
546 {
547 	MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
548 	if (info)
549 		return info;
550 
551 	info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
552 
553 	/*
554 	We might be called during thread cleanup, but we cannot be called after cleanup as happened.
555 	The way to distinguish between before, during and after cleanup is the following:
556 
557 	-If the TLS key is set, cleanup has not begun;
558 	-If the TLS key is clean, but the thread remains registered, cleanup is in progress;
559 	-If the thread is nowhere to be found, cleanup has finished.
560 
561 	We cannot function after cleanup since there's no way to ensure what will happen.
562 	*/
563 	g_assert (info);
564 
565 	/*We're looking up the current thread which will not be freed until we finish running, so no need to keep it on a HP */
566 	mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
567 
568 	return info;
569 }
570 
571 /*
572  * mono_thread_info_get_small_id
573  *
574  * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
575  * identifying the current thread. Returns -1 if the current thread doesn't have
576  * a small ID assigned.
577  *
578  * To ensure that the calling thread has a small ID assigned, call either
579  * mono_thread_info_attach or mono_thread_info_register_small_id.
580  */
581 int
mono_thread_info_get_small_id(void)582 mono_thread_info_get_small_id (void)
583 {
584 #ifdef HAVE_KW_THREAD
585 	return tls_small_id;
586 #else
587 	gpointer val = mono_native_tls_get_value (small_id_key);
588 	if (!val)
589 		return -1;
590 	return GPOINTER_TO_INT (val) - 1;
591 #endif
592 }
593 
594 MonoLinkedListSet*
mono_thread_info_list_head(void)595 mono_thread_info_list_head (void)
596 {
597 	return &thread_list;
598 }
599 
600 /**
601  * mono_threads_attach_tools_thread
602  *
603  * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
604  *
605  * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
606  * not be counted as a regular thread for high order facilities such as executing managed code or accessing
607  * the managed heap.
608  *
609  * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
610  * doing things like resolving backtraces in their background processing thread.
611  */
612 void
mono_threads_attach_tools_thread(void)613 mono_threads_attach_tools_thread (void)
614 {
615 	MonoThreadInfo *info;
616 
617 	/* Must only be called once */
618 	g_assert (!mono_native_tls_get_value (thread_info_key));
619 
620 	while (!mono_threads_inited) {
621 		mono_thread_info_usleep (10);
622 	}
623 
624 	info = mono_thread_info_attach ();
625 	g_assert (info);
626 
627 	info->tools_thread = TRUE;
628 }
629 
630 MonoThreadInfo*
mono_thread_info_attach(void)631 mono_thread_info_attach (void)
632 {
633 	MonoThreadInfo *info;
634 
635 #ifdef HOST_WIN32
636 	if (!mono_threads_inited)
637 	{
638 		/* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
639 		 * thread is created before an embedding API user initialized Mono. */
640 		THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
641 		return NULL;
642 	}
643 #endif
644 
645 	g_assert (mono_threads_inited);
646 
647 	info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
648 	if (!info) {
649 		info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
650 		THREADS_DEBUG ("attaching %p\n", info);
651 		if (!register_thread (info)) {
652 			g_free (info);
653 			return NULL;
654 		}
655 	}
656 
657 	return info;
658 }
659 
660 void
mono_thread_info_detach(void)661 mono_thread_info_detach (void)
662 {
663 	MonoThreadInfo *info;
664 
665 #ifdef HOST_WIN32
666 	if (!mono_threads_inited)
667 	{
668 		/* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
669 		 * is created before an embedding API user initialized Mono. */
670 		THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
671 		return;
672 	}
673 #endif
674 
675 	g_assert (mono_threads_inited);
676 
677 	info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
678 	if (info) {
679 		THREADS_DEBUG ("detaching %p\n", info);
680 		unregister_thread (info);
681 	}
682 }
683 
684 gboolean
mono_thread_info_try_get_internal_thread_gchandle(MonoThreadInfo * info,guint32 * gchandle)685 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
686 {
687 	g_assert (info);
688 	g_assert (mono_thread_info_is_current (info));
689 
690 	if (info->internal_thread_gchandle == G_MAXUINT32)
691 		return FALSE;
692 
693 	*gchandle = info->internal_thread_gchandle;
694 	return TRUE;
695 }
696 
697 void
mono_thread_info_set_internal_thread_gchandle(MonoThreadInfo * info,guint32 gchandle)698 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
699 {
700 	g_assert (info);
701 	g_assert (mono_thread_info_is_current (info));
702 	g_assert (gchandle != G_MAXUINT32);
703 	info->internal_thread_gchandle = gchandle;
704 }
705 
706 void
mono_thread_info_unset_internal_thread_gchandle(THREAD_INFO_TYPE * info)707 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
708 {
709 	g_assert (info);
710 	g_assert (mono_thread_info_is_current (info));
711 	info->internal_thread_gchandle = G_MAXUINT32;
712 }
713 
714 /*
715  * mono_thread_info_is_exiting:
716  *
717  *   Return whenever the current thread is exiting, i.e. it is running pthread
718  * dtors.
719  */
720 gboolean
mono_thread_info_is_exiting(void)721 mono_thread_info_is_exiting (void)
722 {
723 #if defined(__MACH__)
724 	if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
725 		return TRUE;
726 #endif
727 	return FALSE;
728 }
729 
730 #ifndef HOST_WIN32
731 static void
thread_info_key_dtor(void * arg)732 thread_info_key_dtor (void *arg)
733 {
734 	/* Put the MonoThreadInfo back for the duration of the
735 	 * unregister code.  In some circumstances the thread needs to
736 	 * take the GC lock which may block which requires a coop
737 	 * state transition. */
738 	mono_native_tls_set_value (thread_info_key, arg);
739 	unregister_thread (arg);
740 	mono_native_tls_set_value (thread_info_key, NULL);
741 }
742 #endif
743 
744 void
mono_thread_info_init(size_t info_size)745 mono_thread_info_init (size_t info_size)
746 {
747 	gboolean res;
748 	thread_info_size = info_size;
749 	char *sleepLimit;
750 #ifdef HOST_WIN32
751 	res = mono_native_tls_alloc (&thread_info_key, NULL);
752 	res = mono_native_tls_alloc (&thread_exited_key, NULL);
753 #else
754 	res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
755 	res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
756 #endif
757 
758 	g_assert (res);
759 
760 #ifndef HAVE_KW_THREAD
761 	res = mono_native_tls_alloc (&small_id_key, NULL);
762 #endif
763 	g_assert (res);
764 
765 	if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
766 		errno = 0;
767 		long threshold = strtol(sleepLimit, NULL, 10);
768 		if ((errno == 0) && (threshold >= 40))  {
769 			sleepAbortDuration = threshold;
770 			sleepWarnDuration = threshold / 20;
771 		} else
772 			g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
773 		g_free (sleepLimit);
774 	}
775 
776 	mono_os_sem_init (&global_suspend_semaphore, 1);
777 	mono_os_sem_init (&suspend_semaphore, 0);
778 	mono_os_mutex_init (&join_mutex);
779 
780 	mono_lls_init (&thread_list, NULL);
781 	mono_thread_smr_init ();
782 	mono_threads_suspend_init ();
783 	mono_threads_coop_init ();
784 	mono_threads_platform_init ();
785 
786 #if defined(__MACH__)
787 	mono_mach_init (thread_info_key);
788 #endif
789 
790 	mono_threads_inited = TRUE;
791 
792 	g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
793 }
794 
795 void
mono_thread_info_callbacks_init(MonoThreadInfoCallbacks * callbacks)796 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
797 {
798 	threads_callbacks = *callbacks;
799 }
800 
801 void
mono_thread_info_signals_init(void)802 mono_thread_info_signals_init (void)
803 {
804 	mono_threads_suspend_init_signals ();
805 }
806 
807 void
mono_thread_info_runtime_init(MonoThreadInfoRuntimeCallbacks * callbacks)808 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
809 {
810 	runtime_callbacks = *callbacks;
811 }
812 
813 MonoThreadInfoRuntimeCallbacks *
mono_threads_get_runtime_callbacks(void)814 mono_threads_get_runtime_callbacks (void)
815 {
816 	return &runtime_callbacks;
817 }
818 
819 static gboolean
mono_thread_info_core_resume(MonoThreadInfo * info)820 mono_thread_info_core_resume (MonoThreadInfo *info)
821 {
822 	gboolean res = FALSE;
823 
824 	switch (mono_threads_transition_request_resume (info)) {
825 	case ResumeError:
826 		res = FALSE;
827 		break;
828 	case ResumeOk:
829 		res = TRUE;
830 		break;
831 	case ResumeInitSelfResume:
832 		resume_self_suspended (info);
833 		res = TRUE;
834 		break;
835 	case ResumeInitAsyncResume:
836 		resume_async_suspended (info);
837 		res = TRUE;
838 		break;
839 	case ResumeInitBlockingResume:
840 		resume_blocking_suspended (info);
841 		res = TRUE;
842 		break;
843 	}
844 
845 	return res;
846 }
847 
848 gboolean
mono_thread_info_resume(MonoNativeThreadId tid)849 mono_thread_info_resume (MonoNativeThreadId tid)
850 {
851 	gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
852 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
853 	MonoThreadInfo *info;
854 
855 	THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
856 
857 	mono_thread_info_suspend_lock ();
858 
859 	info = mono_thread_info_lookup (tid); /*info on HP1*/
860 	if (!info) {
861 		result = FALSE;
862 		goto cleanup;
863 	}
864 
865 	result = mono_thread_info_core_resume (info);
866 
867 	//Wait for the pending resume to finish
868 	mono_threads_wait_pending_operations ();
869 
870 cleanup:
871 	mono_thread_info_suspend_unlock ();
872 	mono_hazard_pointer_clear (hp, 1);
873 	return result;
874 }
875 
876 gboolean
mono_thread_info_begin_suspend(MonoThreadInfo * info)877 mono_thread_info_begin_suspend (MonoThreadInfo *info)
878 {
879 	switch (mono_threads_transition_request_async_suspension (info)) {
880 	case AsyncSuspendAlreadySuspended:
881 	case AsyncSuspendBlocking:
882 		return TRUE;
883 	case AsyncSuspendWait:
884 		mono_threads_add_to_pending_operation_set (info);
885 		return TRUE;
886 	case AsyncSuspendInitSuspend:
887 		return begin_async_suspend (info, FALSE);
888 	default:
889 		g_assert_not_reached ();
890 	}
891 }
892 
893 gboolean
mono_thread_info_begin_resume(MonoThreadInfo * info)894 mono_thread_info_begin_resume (MonoThreadInfo *info)
895 {
896 	return mono_thread_info_core_resume (info);
897 }
898 
899 /*
900 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
901 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
902 */
903 static gboolean
is_thread_in_critical_region(MonoThreadInfo * info)904 is_thread_in_critical_region (MonoThreadInfo *info)
905 {
906 	gpointer stack_start;
907 	MonoThreadUnwindState *state;
908 
909 	if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
910 		return TRUE;
911 
912 	/* Are we inside a system critical region? */
913 	if (info->inside_critical_region)
914 		return TRUE;
915 
916 	/* Are we inside a GC critical region? */
917 	if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
918 		return TRUE;
919 	}
920 
921 	/* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
922 	state = mono_thread_info_get_suspend_state (info);
923 	if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
924 		return FALSE;
925 
926 	stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
927 	/* altstack signal handler, sgen can't handle them, so we treat them as critical */
928 	if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
929 		return TRUE;
930 
931 	if (threads_callbacks.ip_in_critical_region)
932 		return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
933 
934 	return FALSE;
935 }
936 
937 gboolean
mono_thread_info_in_critical_location(MonoThreadInfo * info)938 mono_thread_info_in_critical_location (MonoThreadInfo *info)
939 {
940 	return is_thread_in_critical_region (info);
941 }
942 
943 /*
944 The return value is only valid until a matching mono_thread_info_resume is called
945 */
946 static MonoThreadInfo*
suspend_sync(MonoNativeThreadId tid,gboolean interrupt_kernel)947 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
948 {
949 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
950 	MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
951 	if (!info)
952 		return NULL;
953 
954 	switch (mono_threads_transition_request_async_suspension (info)) {
955 	case AsyncSuspendAlreadySuspended:
956 		mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
957 		return info;
958 	case AsyncSuspendWait:
959 		mono_threads_add_to_pending_operation_set (info);
960 		break;
961 	case AsyncSuspendInitSuspend:
962 		if (!begin_async_suspend (info, interrupt_kernel)) {
963 			mono_hazard_pointer_clear (hp, 1);
964 			return NULL;
965 		}
966 		break;
967 	case AsyncSuspendBlocking:
968 		if (interrupt_kernel)
969 			mono_threads_suspend_abort_syscall (info);
970 
971 		return info;
972 	default:
973 		g_assert_not_reached ();
974 	}
975 
976 	//Wait for the pending suspend to finish
977 	mono_threads_wait_pending_operations ();
978 
979 	if (!check_async_suspend (info)) {
980 		mono_thread_info_core_resume (info);
981 		mono_threads_wait_pending_operations ();
982 		mono_hazard_pointer_clear (hp, 1);
983 		return NULL;
984 	}
985 	return info;
986 }
987 
988 static MonoThreadInfo*
suspend_sync_nolock(MonoNativeThreadId id,gboolean interrupt_kernel)989 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
990 {
991 	MonoThreadInfo *info = NULL;
992 	int sleep_duration = 0;
993 	for (;;) {
994 		if (!(info = suspend_sync (id, interrupt_kernel))) {
995 			mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
996 			return NULL;
997 		}
998 
999 		/*WARNING: We now are in interrupt context until we resume the thread. */
1000 		if (!is_thread_in_critical_region (info))
1001 			break;
1002 
1003 		if (!mono_thread_info_core_resume (info)) {
1004 			mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1005 			return NULL;
1006 		}
1007 		THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
1008 
1009 		/* Wait for the pending resume to finish */
1010 		mono_threads_wait_pending_operations ();
1011 
1012 		if (sleep_duration == 0)
1013 			mono_thread_info_yield ();
1014 		else
1015 			g_usleep (sleep_duration);
1016 
1017 		sleep_duration += 10;
1018 	}
1019 	return info;
1020 }
1021 
1022 void
mono_thread_info_safe_suspend_and_run(MonoNativeThreadId id,gboolean interrupt_kernel,MonoSuspendThreadCallback callback,gpointer user_data)1023 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
1024 {
1025 	int result;
1026 	MonoThreadInfo *info = NULL;
1027 	MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1028 
1029 	THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
1030 	/*FIXME: unify this with self-suspend*/
1031 	g_assert (id != mono_native_thread_id_get ());
1032 
1033 	/* This can block during stw */
1034 	mono_thread_info_suspend_lock ();
1035 	mono_threads_begin_global_suspend ();
1036 
1037 	info = suspend_sync_nolock (id, interrupt_kernel);
1038 	if (!info)
1039 		goto done;
1040 
1041 	switch (result = callback (info, user_data)) {
1042 	case MonoResumeThread:
1043 		mono_hazard_pointer_set (hp, 1, info);
1044 		mono_thread_info_core_resume (info);
1045 		mono_threads_wait_pending_operations ();
1046 		break;
1047 	case KeepSuspended:
1048 		g_assert (!mono_threads_is_coop_enabled ());
1049 		break;
1050 	default:
1051 		g_error ("Invalid suspend_and_run callback return value %d", result);
1052 	}
1053 
1054 done:
1055 	mono_hazard_pointer_clear (hp, 1);
1056 	mono_threads_end_global_suspend ();
1057 	mono_thread_info_suspend_unlock ();
1058 }
1059 
1060 /**
1061 Inject an assynchronous call into the target thread. The target thread must be suspended and
1062 only a single async call can be setup for a given suspend cycle.
1063 This async call must cause stack unwinding as the current implementation doesn't save enough state
1064 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1065 currently used only to deliver exceptions.
1066 */
1067 void
mono_thread_info_setup_async_call(MonoThreadInfo * info,void (* target_func)(void *),void * user_data)1068 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1069 {
1070 	if (!mono_threads_is_coop_enabled ()) {
1071 		/* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1072 		 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1073 		 * region or entering a gc unsafe region */
1074 		g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1075 	}
1076 	/*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1077 	g_assert (!info->async_target);
1078 	info->async_target = target_func;
1079 	/* This is not GC tracked */
1080 	info->user_data = user_data;
1081 }
1082 
1083 /*
1084 The suspend lock is held during any suspend in progress.
1085 A GC that has safepoints must take this lock as part of its
1086 STW to make sure no unsafe pending suspend is in progress.
1087 */
1088 
1089 static void
mono_thread_info_suspend_lock_with_info(MonoThreadInfo * info)1090 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1091 {
1092 	g_assert (info);
1093 	g_assert (mono_thread_info_is_current (info));
1094 	g_assert (mono_thread_info_is_live (info));
1095 
1096 	MONO_ENTER_GC_SAFE_WITH_INFO(info);
1097 
1098 	int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1099 	g_assert (res != -1);
1100 
1101 	MONO_EXIT_GC_SAFE_WITH_INFO;
1102 }
1103 
1104 void
mono_thread_info_suspend_lock(void)1105 mono_thread_info_suspend_lock (void)
1106 {
1107 	MonoThreadInfo *info;
1108 	gint res;
1109 
1110 	info = mono_thread_info_current_unchecked ();
1111 	if (info && mono_thread_info_is_live (info)) {
1112 		mono_thread_info_suspend_lock_with_info (info);
1113 		return;
1114 	}
1115 
1116 	/* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1117 	 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1118 
1119 	res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1120 	g_assert (res != -1);
1121 }
1122 
1123 void
mono_thread_info_suspend_unlock(void)1124 mono_thread_info_suspend_unlock (void)
1125 {
1126 	mono_os_sem_post (&global_suspend_semaphore);
1127 }
1128 
1129 /*
1130  * This is a very specific function whose only purpose is to
1131  * break a given thread from socket syscalls.
1132  *
1133  * This only exists because linux won't fail a call to connect
1134  * if the underlying is closed.
1135  *
1136  * TODO We should cleanup and unify this with the other syscall abort
1137  * facility.
1138  */
1139 void
mono_thread_info_abort_socket_syscall_for_close(MonoNativeThreadId tid)1140 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1141 {
1142 	MonoThreadHazardPointers *hp;
1143 	MonoThreadInfo *info;
1144 
1145 	if (tid == mono_native_thread_id_get ())
1146 		return;
1147 
1148 	hp = mono_hazard_pointer_get ();
1149 	info = mono_thread_info_lookup (tid);
1150 	if (!info)
1151 		return;
1152 
1153 	if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1154 		mono_hazard_pointer_clear (hp, 1);
1155 		return;
1156 	}
1157 
1158 	mono_thread_info_suspend_lock ();
1159 	mono_threads_begin_global_suspend ();
1160 
1161 	mono_threads_suspend_abort_syscall (info);
1162 	mono_threads_wait_pending_operations ();
1163 
1164 	mono_hazard_pointer_clear (hp, 1);
1165 
1166 	mono_threads_end_global_suspend ();
1167 	mono_thread_info_suspend_unlock ();
1168 }
1169 
1170 /*
1171  * mono_thread_info_set_is_async_context:
1172  *
1173  *   Set whenever the current thread is in an async context. Some runtime functions might behave
1174  * differently while in an async context in order to be async safe.
1175  */
1176 void
mono_thread_info_set_is_async_context(gboolean async_context)1177 mono_thread_info_set_is_async_context (gboolean async_context)
1178 {
1179 	MonoThreadInfo *info = mono_thread_info_current ();
1180 
1181 	if (info)
1182 		info->is_async_context = async_context;
1183 }
1184 
1185 gboolean
mono_thread_info_is_async_context(void)1186 mono_thread_info_is_async_context (void)
1187 {
1188 	MonoThreadInfo *info = mono_thread_info_current ();
1189 
1190 	if (info)
1191 		return info->is_async_context;
1192 	else
1193 		return FALSE;
1194 }
1195 
1196 /*
1197  * mono_thread_info_get_stack_bounds:
1198  *
1199  *   Return the address and size of the current threads stack. Return NULL as the
1200  * stack address if the stack address cannot be determined.
1201  */
1202 void
mono_thread_info_get_stack_bounds(guint8 ** staddr,size_t * stsize)1203 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1204 {
1205 	guint8 *current = (guint8 *)&stsize;
1206 	mono_threads_platform_get_stack_bounds (staddr, stsize);
1207 	if (!*staddr)
1208 		return;
1209 
1210 	/* Sanity check the result */
1211 	g_assert ((current > *staddr) && (current < *staddr + *stsize));
1212 
1213 	/* When running under emacs, sometimes staddr is not aligned to a page size */
1214 	*staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1215 }
1216 
1217 gboolean
mono_thread_info_yield(void)1218 mono_thread_info_yield (void)
1219 {
1220 	return mono_threads_platform_yield ();
1221 }
1222 
1223 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1224 static MonoCoopMutex sleep_mutex;
1225 static MonoCoopCond sleep_cond;
1226 
1227 static void
sleep_initialize(void)1228 sleep_initialize (void)
1229 {
1230 	mono_coop_mutex_init (&sleep_mutex);
1231 	mono_coop_cond_init (&sleep_cond);
1232 }
1233 
1234 static void
sleep_interrupt(gpointer data)1235 sleep_interrupt (gpointer data)
1236 {
1237 	mono_coop_mutex_lock (&sleep_mutex);
1238 	mono_coop_cond_broadcast (&sleep_cond);
1239 	mono_coop_mutex_unlock (&sleep_mutex);
1240 }
1241 
1242 static inline guint32
sleep_interruptable(guint32 ms,gboolean * alerted)1243 sleep_interruptable (guint32 ms, gboolean *alerted)
1244 {
1245 	gint64 now, end;
1246 
1247 	g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1248 
1249 	g_assert (alerted);
1250 	*alerted = FALSE;
1251 
1252 	if (ms != MONO_INFINITE_WAIT)
1253 		end = mono_msec_ticks() + ms;
1254 
1255 	mono_lazy_initialize (&sleep_init, sleep_initialize);
1256 
1257 	mono_coop_mutex_lock (&sleep_mutex);
1258 
1259 	for (;;) {
1260 		if (ms != MONO_INFINITE_WAIT) {
1261 			now = mono_msec_ticks();
1262 			if (now >= end)
1263 				break;
1264 		}
1265 
1266 		mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1267 		if (*alerted) {
1268 			mono_coop_mutex_unlock (&sleep_mutex);
1269 			return WAIT_IO_COMPLETION;
1270 		}
1271 
1272 		if (ms != MONO_INFINITE_WAIT)
1273 			mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1274 		else
1275 			mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1276 
1277 		mono_thread_info_uninstall_interrupt (alerted);
1278 		if (*alerted) {
1279 			mono_coop_mutex_unlock (&sleep_mutex);
1280 			return WAIT_IO_COMPLETION;
1281 		}
1282 	}
1283 
1284 	mono_coop_mutex_unlock (&sleep_mutex);
1285 
1286 	return 0;
1287 }
1288 
1289 gint
mono_thread_info_sleep(guint32 ms,gboolean * alerted)1290 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1291 {
1292 	if (ms == 0) {
1293 		MonoThreadInfo *info;
1294 
1295 		mono_thread_info_yield ();
1296 
1297 		info = mono_thread_info_current ();
1298 		if (info && mono_thread_info_is_interrupt_state (info))
1299 			return WAIT_IO_COMPLETION;
1300 
1301 		return 0;
1302 	}
1303 
1304 	if (alerted)
1305 		return sleep_interruptable (ms, alerted);
1306 
1307 	MONO_ENTER_GC_SAFE;
1308 
1309 	if (ms == MONO_INFINITE_WAIT) {
1310 		do {
1311 #ifdef HOST_WIN32
1312 			Sleep (G_MAXUINT32);
1313 #else
1314 			sleep (G_MAXUINT32);
1315 #endif
1316 		} while (1);
1317 	} else {
1318 		int ret;
1319 #if defined (HAVE_CLOCK_NANOSLEEP) && !defined(HOST_ANDROID)
1320 		struct timespec start, target;
1321 
1322 		/* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1323 		ret = clock_gettime (CLOCK_MONOTONIC, &start);
1324 		g_assert (ret == 0);
1325 
1326 		target = start;
1327 		target.tv_sec += ms / 1000;
1328 		target.tv_nsec += (ms % 1000) * 1000000;
1329 		if (target.tv_nsec > 999999999) {
1330 			target.tv_nsec -= 999999999;
1331 			target.tv_sec ++;
1332 		}
1333 
1334 		do {
1335 			ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1336 		} while (ret != 0);
1337 #elif HOST_WIN32
1338 		Sleep (ms);
1339 #else
1340 		struct timespec req, rem;
1341 
1342 		req.tv_sec = ms / 1000;
1343 		req.tv_nsec = (ms % 1000) * 1000000;
1344 
1345 		do {
1346 			memset (&rem, 0, sizeof (rem));
1347 			ret = nanosleep (&req, &rem);
1348 		} while (ret != 0);
1349 #endif /* HAVE_CLOCK_NANOSLEEP */
1350 	}
1351 
1352 	MONO_EXIT_GC_SAFE;
1353 
1354 	return 0;
1355 }
1356 
1357 gint
mono_thread_info_usleep(guint64 us)1358 mono_thread_info_usleep (guint64 us)
1359 {
1360 	MONO_ENTER_GC_SAFE;
1361 	g_usleep (us);
1362 	MONO_EXIT_GC_SAFE;
1363 	return 0;
1364 }
1365 
1366 gpointer
mono_thread_info_tls_get(THREAD_INFO_TYPE * info,MonoTlsKey key)1367 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1368 {
1369 	return ((MonoThreadInfo*)info)->tls [key];
1370 }
1371 
1372 /*
1373  * mono_threads_info_tls_set:
1374  *
1375  *   Set the TLS key to VALUE in the info structure. This can be used to obtain
1376  * values of TLS variables for threads other than the current thread.
1377  * This should only be used for infrequently changing TLS variables, and it should
1378  * be paired with setting the real TLS variable since this provides no GC tracking.
1379  */
1380 void
mono_thread_info_tls_set(THREAD_INFO_TYPE * info,MonoTlsKey key,gpointer value)1381 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1382 {
1383 	((MonoThreadInfo*)info)->tls [key] = value;
1384 }
1385 
1386 /*
1387  * mono_thread_info_exit:
1388  *
1389  *   Exit the current thread.
1390  * This function doesn't return.
1391  */
1392 void
mono_thread_info_exit(gsize exit_code)1393 mono_thread_info_exit (gsize exit_code)
1394 {
1395 	mono_thread_info_detach ();
1396 
1397 	mono_threads_platform_exit (0);
1398 }
1399 
1400 /*
1401  * mono_threads_open_thread_handle:
1402  *
1403  *  Duplicate the handle. The handle needs to be closed by calling
1404  *  mono_threads_close_thread_handle () when it is no longer needed.
1405  */
1406 MonoThreadHandle*
mono_threads_open_thread_handle(MonoThreadHandle * thread_handle)1407 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1408 {
1409 	return mono_refcount_inc (thread_handle);
1410 }
1411 
1412 void
mono_threads_close_thread_handle(MonoThreadHandle * thread_handle)1413 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1414 {
1415 	mono_refcount_dec (thread_handle);
1416 }
1417 
1418 static void
mono_threads_signal_thread_handle(MonoThreadHandle * thread_handle)1419 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1420 {
1421 	mono_os_event_set (&thread_handle->event);
1422 }
1423 
1424 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1425 
1426 struct _MonoThreadInfoInterruptToken {
1427 	void (*callback) (gpointer data);
1428 	gpointer data;
1429 };
1430 
1431 /*
1432  * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1433  *
1434  *  - @callback: must be able to be called from another thread and always cancel the wait
1435  *  - @data: passed to the callback
1436  *  - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1437  *     if set to TRUE, it must mean that the thread is in interrupted state
1438  */
1439 void
mono_thread_info_install_interrupt(void (* callback)(gpointer data),gpointer data,gboolean * interrupted)1440 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1441 {
1442 	MonoThreadInfo *info;
1443 	MonoThreadInfoInterruptToken *previous_token, *token;
1444 
1445 	g_assert (callback);
1446 
1447 	g_assert (interrupted);
1448 	*interrupted = FALSE;
1449 
1450 	info = mono_thread_info_current ();
1451 	g_assert (info);
1452 
1453 	/* The memory of this token can be freed at 2 places:
1454 	 *  - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1455 	 *     by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1456 	 *  - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1457 	 *     functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1458 	token = g_new0 (MonoThreadInfoInterruptToken, 1);
1459 	token->callback = callback;
1460 	token->data = data;
1461 
1462 	previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, token, NULL);
1463 
1464 	if (previous_token) {
1465 		if (previous_token != INTERRUPT_STATE)
1466 			g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1467 
1468 		g_free (token);
1469 
1470 		*interrupted = TRUE;
1471 	}
1472 
1473 	THREADS_INTERRUPT_DEBUG ("interrupt install    tid %p token %p previous_token %p interrupted %s\n",
1474 		mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1475 }
1476 
1477 void
mono_thread_info_uninstall_interrupt(gboolean * interrupted)1478 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1479 {
1480 	MonoThreadInfo *info;
1481 	MonoThreadInfoInterruptToken *previous_token;
1482 
1483 	g_assert (interrupted);
1484 	*interrupted = FALSE;
1485 
1486 	info = mono_thread_info_current ();
1487 	g_assert (info);
1488 
1489 	previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_xchg_ptr ((gpointer*) &info->interrupt_token, NULL);
1490 
1491 	/* only the installer can uninstall the token */
1492 	g_assert (previous_token);
1493 
1494 	if (previous_token == INTERRUPT_STATE) {
1495 		/* if it is interrupted, then it is going to be freed in finish interrupt */
1496 		*interrupted = TRUE;
1497 	} else {
1498 		g_free (previous_token);
1499 	}
1500 
1501 	THREADS_INTERRUPT_DEBUG ("interrupt uninstall  tid %p previous_token %p interrupted %s\n",
1502 		mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1503 }
1504 
1505 static MonoThreadInfoInterruptToken*
set_interrupt_state(MonoThreadInfo * info)1506 set_interrupt_state (MonoThreadInfo *info)
1507 {
1508 	MonoThreadInfoInterruptToken *token, *previous_token;
1509 
1510 	g_assert (info);
1511 
1512 	/* Atomically obtain the token the thread is
1513 	* waiting on, and change it to a flag value. */
1514 
1515 	do {
1516 		previous_token = info->interrupt_token;
1517 
1518 		/* Already interrupted */
1519 		if (previous_token == INTERRUPT_STATE) {
1520 			token = NULL;
1521 			break;
1522 		}
1523 
1524 		token = previous_token;
1525 	} while (mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1526 
1527 	return token;
1528 }
1529 
1530 /*
1531  * mono_thread_info_prepare_interrupt:
1532  *
1533  * The state of the thread info interrupt token is set to 'interrupted' which means that :
1534  *  - if the thread calls one of the WaitFor functions, the function will return with
1535  *     WAIT_IO_COMPLETION instead of waiting
1536  *  - if the thread was waiting when this function was called, the wait will be broken
1537  *
1538  * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1539  * didn't receive the interrupt signal yet, in this case it should call the wait function
1540  * again. This essentially means that the target thread will busy wait until it is ready to
1541  * process the interruption.
1542  */
1543 MonoThreadInfoInterruptToken*
mono_thread_info_prepare_interrupt(MonoThreadInfo * info)1544 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1545 {
1546 	MonoThreadInfoInterruptToken *token;
1547 
1548 	token = set_interrupt_state (info);
1549 
1550 	THREADS_INTERRUPT_DEBUG ("interrupt prepare    tid %p token %p\n",
1551 		mono_thread_info_get_tid (info), token);
1552 
1553 	return token;
1554 }
1555 
1556 void
mono_thread_info_finish_interrupt(MonoThreadInfoInterruptToken * token)1557 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1558 {
1559 	THREADS_INTERRUPT_DEBUG ("interrupt finish     token %p\n", token);
1560 
1561 	if (token == NULL)
1562 		return;
1563 
1564 	g_assert (token->callback);
1565 
1566 	token->callback (token->data);
1567 
1568 	g_free (token);
1569 }
1570 
1571 void
mono_thread_info_self_interrupt(void)1572 mono_thread_info_self_interrupt (void)
1573 {
1574 	MonoThreadInfo *info;
1575 	MonoThreadInfoInterruptToken *token;
1576 
1577 	info = mono_thread_info_current ();
1578 	g_assert (info);
1579 
1580 	token = set_interrupt_state (info);
1581 	g_assert (!token);
1582 
1583 	THREADS_INTERRUPT_DEBUG ("interrupt self       tid %p\n",
1584 		mono_thread_info_get_tid (info));
1585 }
1586 
1587 /* Clear the interrupted flag of the current thread, set with
1588  * mono_thread_info_self_interrupt, so it can wait again */
1589 void
mono_thread_info_clear_self_interrupt()1590 mono_thread_info_clear_self_interrupt ()
1591 {
1592 	MonoThreadInfo *info;
1593 	MonoThreadInfoInterruptToken *previous_token;
1594 
1595 	info = mono_thread_info_current ();
1596 	g_assert (info);
1597 
1598 	previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1599 	g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1600 
1601 	THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1602 }
1603 
1604 gboolean
mono_thread_info_is_interrupt_state(MonoThreadInfo * info)1605 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1606 {
1607 	g_assert (info);
1608 	return mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1609 }
1610 
1611 void
mono_thread_info_describe_interrupt_token(MonoThreadInfo * info,GString * text)1612 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1613 {
1614 	g_assert (info);
1615 
1616 	if (!mono_atomic_load_ptr ((gpointer*) &info->interrupt_token))
1617 		g_string_append_printf (text, "not waiting");
1618 	else if (mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1619 		g_string_append_printf (text, "interrupted state");
1620 	else
1621 		g_string_append_printf (text, "waiting");
1622 }
1623 
1624 gboolean
mono_thread_info_is_current(MonoThreadInfo * info)1625 mono_thread_info_is_current (MonoThreadInfo *info)
1626 {
1627 	return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1628 }
1629 
1630 MonoThreadInfoWaitRet
mono_thread_info_wait_one_handle(MonoThreadHandle * thread_handle,guint32 timeout,gboolean alertable)1631 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1632 {
1633 	MonoOSEventWaitRet res;
1634 
1635 	res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1636 	if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1637 		return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1638 	else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1639 		return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1640 	else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1641 		return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1642 	else
1643 		g_error ("%s: unknown res value %d", __func__, res);
1644 }
1645 
1646 MonoThreadInfoWaitRet
mono_thread_info_wait_multiple_handle(MonoThreadHandle ** thread_handles,gsize nhandles,MonoOSEvent * background_change_event,gboolean waitall,guint32 timeout,gboolean alertable)1647 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1648 {
1649 	MonoOSEventWaitRet res;
1650 	MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1651 	gint i;
1652 
1653 	g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1654 	if (background_change_event)
1655 		g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1656 
1657 	for (i = 0; i < nhandles; ++i)
1658 		thread_events [i] = &thread_handles [i]->event;
1659 
1660 	if (background_change_event)
1661 		thread_events [nhandles ++] = background_change_event;
1662 
1663 	res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1664 	if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1665 		return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1666 	else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1667 		return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1668 	else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1669 		return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1670 	else
1671 		g_error ("%s: unknown res value %d", __func__, res);
1672 }
1673 
1674 /*
1675  * mono_threads_join_mutex:
1676  *
1677  *   This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1678  * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1679  * The code inside the lock should not block.
1680  */
1681 void
mono_threads_join_lock(void)1682 mono_threads_join_lock (void)
1683 {
1684 #ifdef TARGET_OSX
1685 	mono_os_mutex_lock (&join_mutex);
1686 #endif
1687 }
1688 
1689 void
mono_threads_join_unlock(void)1690 mono_threads_join_unlock (void)
1691 {
1692 #ifdef TARGET_OSX
1693 	mono_os_mutex_unlock (&join_mutex);
1694 #endif
1695 }
1696