[netcore] Reorder test targets
[mono-project.git] / mono / utils / mono-threads.c
blob8d9c3680d0484846a1bec1ec2425c7a3689f514b
1 /**
2 * \file
3 * Low-level threading
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
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.
13 #include <config.h>
15 /* enable pthread extensions */
16 #ifdef TARGET_MACH
17 #define _DARWIN_C_SOURCE
18 #endif
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>
37 #include <errno.h>
38 #include <mono/utils/mono-errno.h>
40 #if defined(__MACH__)
41 #include <mono/utils/mach-support.h>
42 #endif
45 Mutex that makes sure only a single thread can be suspending others.
46 Suspend is a very racy operation since it requires restarting until
47 the target thread is not on an unsafe region.
49 We could implement this using critical regions, but would be much much
50 harder for an operation that is hardly performance critical.
52 The GC has to acquire this lock before starting a STW to make sure
53 a runtime suspend won't make it wronly see a thread in a safepoint
54 when it is in fact not.
56 This has to be a naked locking primitive, and not a coop aware one, as
57 it needs to be usable when destroying thread_info_key, the TLS key for
58 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
59 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
60 to an assertion error. We then simply switch state manually in
61 mono_thread_info_suspend_lock_with_info.
63 static MonoSemType global_suspend_semaphore;
65 static size_t thread_info_size;
66 static MonoThreadInfoCallbacks threads_callbacks;
67 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
68 static MonoNativeTlsKey thread_info_key, thread_exited_key;
69 #ifdef MONO_KEYWORD_THREAD
70 static MONO_KEYWORD_THREAD gint32 tls_small_id = -1;
71 #else
72 static MonoNativeTlsKey small_id_key;
73 #endif
74 static MonoLinkedListSet thread_list;
75 static gboolean mono_threads_inited = FALSE;
77 static MonoSemType suspend_semaphore;
78 static size_t pending_suspends;
80 static mono_mutex_t join_mutex;
82 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
84 /*warn at 50 ms*/
85 #define SLEEP_DURATION_BEFORE_WARNING (50)
86 /*never aborts */
87 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
89 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
90 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
92 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
94 void
95 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
97 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
98 mono_atomic_inc_i32 (&abort_posts);
99 mono_os_sem_post (&suspend_semaphore);
102 void
103 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
105 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
106 // check that the thread is really in a valid suspended state.
107 g_assert (mono_thread_info_get_suspend_state (info) != NULL);
108 mono_atomic_inc_i32 (&suspend_posts);
109 mono_os_sem_post (&suspend_semaphore);
112 void
113 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
115 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
116 mono_atomic_inc_i32 (&resume_posts);
117 mono_os_sem_post (&suspend_semaphore);
120 typedef enum {
121 BeginSuspendFail = 0,
122 BeginSuspendOkPreemptive = 1,
123 BeginSuspendOkCooperative = 2,
124 BeginSuspendOkNoWait = 3,
125 } BeginSuspendResult;
127 static BeginSuspendResult
128 begin_cooperative_suspend (MonoThreadInfo *info)
130 /* There's nothing else to do after we async request the thread to suspend */
131 mono_threads_add_to_pending_operation_set (info);
132 return BeginSuspendOkCooperative;
135 static BeginSuspendResult
136 begin_preemptive_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
138 if (mono_threads_suspend_begin_async_suspend (info, interrupt_kernel))
139 return BeginSuspendOkPreemptive;
140 else
141 return BeginSuspendFail;
144 static BeginSuspendResult
145 begin_suspend_for_running_thread (MonoThreadInfo *info, gboolean interrupt_kernel)
147 /* If we're using full cooperative suspend or hybrid suspend,
148 * cooperatively suspend RUNNING threads */
149 if (mono_threads_are_safepoints_enabled ())
150 return begin_cooperative_suspend (info);
151 else
152 return begin_preemptive_suspend (info, interrupt_kernel);
155 static BeginSuspendResult
156 begin_suspend_for_blocking_thread (MonoThreadInfo *info, gboolean interrupt_kernel, MonoThreadSuspendPhase phase, gboolean *did_interrupt)
158 // if a thread can't transition to blocking, we certainly shouldn't be
159 // trying to suspend it like it's blocking.
160 g_assert (mono_threads_is_blocking_transition_enabled ());
161 // with hybrid suspend, preemptively suspend blocking threads,
162 // otherwise blocking already counts as suspended.
163 if (mono_threads_is_hybrid_suspension_enabled ()) {
164 if (did_interrupt) {
165 *did_interrupt = interrupt_kernel;
167 switch (phase) {
168 case MONO_THREAD_SUSPEND_PHASE_INITIAL:
169 /* In hybrid suspend, in the first phase, a thread in
170 * blocking can continue running (and possibly
171 * self-suspend). We'll preemptively suspend it in the
172 * second phase. */
173 return BeginSuspendOkNoWait;
174 case MONO_THREAD_SUSPEND_PHASE_MOPUP:
175 return begin_preemptive_suspend (info, interrupt_kernel);
176 default:
177 g_assert_not_reached ();
179 } else {
180 if (did_interrupt)
181 *did_interrupt = FALSE;
182 // In full cooperative suspend, treat a thread in BLOCKING as
183 // already suspended and don't wait for it.
184 return BeginSuspendOkNoWait;
188 static gboolean
189 check_async_suspend (MonoThreadInfo *info, BeginSuspendResult result)
191 switch (result) {
192 case BeginSuspendOkCooperative:
193 return TRUE;
194 case BeginSuspendOkPreemptive:
195 return mono_threads_suspend_check_suspend_result (info);
196 case BeginSuspendFail:
197 return FALSE;
198 case BeginSuspendOkNoWait:
199 return TRUE;
200 default:
201 g_assert_not_reached ();
205 static void
206 resume_async_suspended (MonoThreadInfo *info)
208 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
209 g_assert_not_reached ();
211 g_assert (mono_threads_suspend_begin_async_resume (info));
214 static void
215 resume_self_suspended (MonoThreadInfo* info)
217 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
218 mono_os_sem_post (&info->resume_semaphore);
221 void
222 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
224 int res;
225 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
226 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
227 g_assert (res != -1);
230 static void
231 resume_blocking_suspended (MonoThreadInfo* info)
233 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
234 mono_os_sem_post (&info->resume_semaphore);
237 void
238 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
240 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
241 ++pending_suspends;
242 mono_atomic_inc_i32 (&pending_ops);
245 void
246 mono_threads_begin_global_suspend (void)
248 size_t ps = pending_suspends;
249 if (G_UNLIKELY (ps != 0))
250 g_error ("pending_suspends = %d, but must be 0", ps);
251 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,
252 abort_posts, waits_done, pending_ops);
253 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
254 mono_threads_coop_begin_global_suspend ();
257 void
258 mono_threads_end_global_suspend (void)
260 size_t ps = pending_suspends;
261 if (G_UNLIKELY (ps != 0))
262 g_error ("pending_suspends = %d, but must be 0", ps);
263 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
264 abort_posts, waits_done, pending_ops);
265 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
266 mono_threads_coop_end_global_suspend ();
269 static void
270 dump_threads (void)
272 MonoThreadInfo *cur = mono_thread_info_current ();
274 g_async_safe_printf ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
275 g_async_safe_printf ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
276 g_async_safe_printf ("\t0x1\t- detached (GOOD, unless the thread is running managed code)\n");
277 g_async_safe_printf ("\t0x2\t- running (BAD, unless it's the gc thread)\n");
278 g_async_safe_printf ("\t0x?03\t- async suspended (GOOD)\n");
279 g_async_safe_printf ("\t0x?04\t- self suspended (GOOD)\n");
280 g_async_safe_printf ("\t0x?05\t- async suspend requested (BAD)\n");
281 g_async_safe_printf ("\t0x6\t- blocking (BAD, unless there's no suspend initiator)\n");
282 g_async_safe_printf ("\t0x?07\t- blocking async suspended (GOOD)\n");
283 g_async_safe_printf ("\t0x?08\t- blocking self suspended (GOOD)\n");
284 g_async_safe_printf ("\t0x?09\t- blocking suspend requested (BAD in coop; GOOD in hybrid)\n");
286 FOREACH_THREAD_SAFE_ALL (info) {
287 #ifdef TARGET_MACH
288 char thread_name [256] = { 0 };
289 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
291 g_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" : "" );
292 #else
293 g_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" : "" );
294 #endif
295 } FOREACH_THREAD_SAFE_END
298 gboolean
299 mono_threads_wait_pending_operations (void)
301 int i;
302 int c = pending_suspends;
304 /* Wait threads to park */
305 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
306 if (pending_suspends) {
307 MonoStopwatch suspension_time;
308 mono_stopwatch_start (&suspension_time);
309 for (i = 0; i < pending_suspends; ++i) {
310 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
311 mono_atomic_inc_i32 (&waits_done);
312 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
313 continue;
314 mono_stopwatch_stop (&suspension_time);
316 dump_threads ();
318 g_async_safe_printf ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
319 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
321 mono_stopwatch_stop (&suspension_time);
322 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
326 pending_suspends = 0;
328 return c > 0;
332 //Thread initialization code
334 static inline void
335 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
337 if (retain != 0)
338 mono_hazard_pointer_clear (hp, 0);
339 if (retain != 1)
340 mono_hazard_pointer_clear (hp, 1);
341 if (retain != 2)
342 mono_hazard_pointer_clear (hp, 2);
346 If return non null Hazard Pointer 1 holds the return value.
348 MonoThreadInfo*
349 mono_thread_info_lookup (MonoNativeThreadId id)
351 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
353 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
354 mono_hazard_pointer_clear_all (hp, -1);
355 return NULL;
358 mono_hazard_pointer_clear_all (hp, 1);
359 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
362 static gboolean
363 mono_thread_info_insert (MonoThreadInfo *info)
365 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
367 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
368 mono_hazard_pointer_clear_all (hp, -1);
369 return FALSE;
372 mono_hazard_pointer_clear_all (hp, -1);
373 return TRUE;
376 static gboolean
377 mono_thread_info_remove (MonoThreadInfo *info)
379 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
380 gboolean res;
382 THREADS_DEBUG ("removing info %p\n", info);
383 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
384 mono_hazard_pointer_clear_all (hp, -1);
385 return res;
388 static void
389 free_thread_info (gpointer mem)
391 MonoThreadInfo *info = (MonoThreadInfo *) mem;
393 mono_os_sem_destroy (&info->resume_semaphore);
394 mono_threads_suspend_free (info);
396 g_free (info);
400 * mono_thread_info_register_small_id
402 * Registers a small ID for the current thread. This is a 16-bit value uniquely
403 * identifying the current thread. If the current thread already has a small ID
404 * assigned, that small ID will be returned; otherwise, the newly assigned small
405 * ID is returned.
408 mono_thread_info_register_small_id (void)
410 int small_id = mono_thread_info_get_small_id ();
412 if (small_id != -1)
413 return small_id;
415 small_id = mono_thread_small_id_alloc ();
416 #ifdef MONO_KEYWORD_THREAD
417 tls_small_id = small_id;
418 #else
419 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
420 #endif
421 return small_id;
424 static void
425 thread_handle_destroy (gpointer data)
427 MonoThreadHandle *thread_handle;
429 thread_handle = (MonoThreadHandle*) data;
431 mono_os_event_destroy (&thread_handle->event);
432 g_free (thread_handle);
435 static gboolean
436 register_thread (MonoThreadInfo *info)
438 size_t stsize = 0;
439 guint8 *staddr = NULL;
440 gboolean result;
442 info->small_id = mono_thread_info_register_small_id ();
443 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
445 info->handle = g_new0 (MonoThreadHandle, 1);
446 mono_refcount_init (info->handle, thread_handle_destroy);
447 mono_os_event_init (&info->handle->event, FALSE);
449 mono_os_sem_init (&info->resume_semaphore, 0);
451 /*set TLS early so SMR works */
452 mono_native_tls_set_value (thread_info_key, info);
454 mono_thread_info_get_stack_bounds (&staddr, &stsize);
455 g_assert (staddr);
456 g_assert (stsize);
457 info->stack_start_limit = staddr;
458 info->stack_end = staddr + stsize;
460 info->stackdata = g_byte_array_new ();
462 info->internal_thread_gchandle = G_MAXUINT32;
464 info->profiler_signal_ack = 1;
466 #ifdef USE_WINDOWS_BACKEND
467 info->win32_apc_info = 0;
468 info->win32_apc_info_io_handle = INVALID_HANDLE_VALUE;
469 #endif
471 mono_threads_suspend_register (info);
473 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
475 if (threads_callbacks.thread_attach) {
476 if (!threads_callbacks.thread_attach (info)) {
477 // g_warning ("thread registation failed\n");
478 mono_native_tls_set_value (thread_info_key, NULL);
479 return FALSE;
484 Transition it before taking any locks or publishing itself to reduce the chance
485 of others witnessing a detached thread.
486 We can reasonably expect that until this thread gets published, no other thread will
487 try to manipulate it.
489 mono_threads_transition_attach (info);
490 mono_thread_info_suspend_lock ();
491 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
492 result = mono_thread_info_insert (info);
493 g_assert (result);
494 mono_thread_info_suspend_unlock ();
496 return TRUE;
499 static void
500 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
502 static void
503 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
505 static void
506 unregister_thread (void *arg)
508 MONO_STACKDATA (gc_unsafe_stackdata);
509 MonoThreadInfo *info;
510 int small_id;
511 gboolean result;
512 MonoThreadHandle* handle;
514 info = (MonoThreadInfo *) arg;
515 g_assert (info);
516 g_assert (mono_thread_info_is_current (info));
517 g_assert (mono_thread_info_is_live (info));
519 /* Pump the HP queue while the thread is alive.*/
520 mono_thread_hazardous_try_free_some ();
522 small_id = info->small_id;
524 /* We only enter the GC unsafe region, as when exiting this function, the thread
525 * will be detached, and the current MonoThreadInfo* will be destroyed. */
526 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
528 THREADS_DEBUG ("unregistering info %p\n", info);
530 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
533 * TLS destruction order is not reliable so small_id might be cleaned up
534 * before us.
536 #ifndef MONO_KEYWORD_THREAD
537 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
538 #endif
540 /* we need to duplicate it, as the info->handle is going
541 * to be closed when unregistering from the platform */
542 handle = mono_threads_open_thread_handle (info->handle);
545 First perform the callback that requires no locks.
546 This callback has the potential of taking other locks, so we do it before.
547 After it completes, the thread remains functional.
549 if (threads_callbacks.thread_detach)
550 threads_callbacks.thread_detach (info);
552 mono_thread_info_suspend_lock_with_info (info);
555 Now perform the callback that must be done under locks.
556 This will render the thread useless and non-suspendable, so it must
557 be done while holding the suspend lock to give no other thread chance
558 to suspend it.
560 if (threads_callbacks.thread_detach_with_lock)
561 threads_callbacks.thread_detach_with_lock (info);
563 /* The thread is no longer active, so unref its handle */
564 mono_threads_close_thread_handle (info->handle);
565 info->handle = NULL;
567 result = mono_thread_info_remove (info);
568 g_assert (result);
569 mono_threads_transition_detach (info);
571 mono_thread_info_suspend_unlock ();
573 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
575 /*now it's safe to free the thread info.*/
576 mono_thread_hazardous_try_free (info, free_thread_info);
578 mono_thread_small_id_free (small_id);
580 mono_threads_signal_thread_handle (handle);
582 mono_threads_close_thread_handle (handle);
584 mono_native_tls_set_value (thread_info_key, NULL);
587 static void
588 thread_exited_dtor (void *arg)
590 #if defined(__MACH__)
592 * Since we use pthread dtors to clean up thread data, if a thread
593 * is attached to the runtime by another pthread dtor after our dtor
594 * has ran, it will never be detached, leading to various problems
595 * since the thread ids etc. will be reused while they are still in
596 * the threads hashtables etc.
597 * Dtors are called in a loop until all user tls entries are 0,
598 * but the loop has a maximum count (4), so if we set the tls
599 * variable every time, it will remain set when system tls dtors
600 * are ran. This allows mono_thread_info_is_exiting () to detect
601 * whenever the thread is exiting, even if it is executed from a
602 * system tls dtor (i.e. obj-c dealloc methods).
604 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
605 #endif
608 MonoThreadInfo*
609 mono_thread_info_current_unchecked (void)
611 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
615 MonoThreadInfo*
616 mono_thread_info_current (void)
618 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
619 if (info)
620 return info;
622 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
625 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
626 The way to distinguish between before, during and after cleanup is the following:
628 -If the TLS key is set, cleanup has not begun;
629 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
630 -If the thread is nowhere to be found, cleanup has finished.
632 We cannot function after cleanup since there's no way to ensure what will happen.
634 g_assert (info);
636 /*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 */
637 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
639 return info;
643 * mono_thread_info_get_small_id
645 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
646 * identifying the current thread. Returns -1 if the current thread doesn't have
647 * a small ID assigned.
649 * To ensure that the calling thread has a small ID assigned, call either
650 * mono_thread_info_attach or mono_thread_info_register_small_id.
653 mono_thread_info_get_small_id (void)
655 #ifdef MONO_KEYWORD_THREAD
656 return tls_small_id;
657 #else
658 gpointer val = mono_native_tls_get_value (small_id_key);
659 if (!val)
660 return -1;
661 return GPOINTER_TO_INT (val) - 1;
662 #endif
665 MonoLinkedListSet*
666 mono_thread_info_list_head (void)
668 return &thread_list;
671 MonoThreadInfo *
672 mono_thread_info_attach (void)
674 MonoThreadInfo *info;
676 #ifdef HOST_WIN32
677 if (!mono_threads_inited)
679 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
680 * thread is created before an embedding API user initialized Mono. */
681 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
682 return NULL;
684 #endif
686 g_assert (mono_threads_inited);
688 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
689 if (!info) {
690 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
691 THREADS_DEBUG ("attaching %p\n", info);
692 if (!register_thread (info)) {
693 g_free (info);
694 return NULL;
698 return info;
701 void
702 mono_thread_info_detach (void)
704 MonoThreadInfo *info;
706 #ifdef HOST_WIN32
707 if (!mono_threads_inited)
709 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
710 * is created before an embedding API user initialized Mono. */
711 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
712 return;
714 #endif
716 g_assert (mono_threads_inited);
718 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
719 if (info) {
720 THREADS_DEBUG ("detaching %p\n", info);
721 unregister_thread (info);
725 gboolean
726 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
728 g_assert (info);
729 g_assert (mono_thread_info_is_current (info));
731 if (info->internal_thread_gchandle == G_MAXUINT32)
732 return FALSE;
734 *gchandle = info->internal_thread_gchandle;
735 return TRUE;
738 void
739 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
741 g_assert (info);
742 g_assert (mono_thread_info_is_current (info));
743 g_assert (gchandle != G_MAXUINT32);
744 info->internal_thread_gchandle = gchandle;
747 void
748 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
750 g_assert (info);
751 g_assert (mono_thread_info_is_current (info));
752 info->internal_thread_gchandle = G_MAXUINT32;
756 * mono_thread_info_is_exiting:
758 * Return whenever the current thread is exiting, i.e. it is running pthread
759 * dtors.
761 gboolean
762 mono_thread_info_is_exiting (void)
764 #if defined(__MACH__)
765 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
766 return TRUE;
767 #endif
768 return FALSE;
771 #ifndef HOST_WIN32
772 static void
773 thread_info_key_dtor (void *arg)
775 /* Put the MonoThreadInfo back for the duration of the
776 * unregister code. In some circumstances the thread needs to
777 * take the GC lock which may block which requires a coop
778 * state transition. */
779 mono_native_tls_set_value (thread_info_key, arg);
780 unregister_thread (arg);
781 mono_native_tls_set_value (thread_info_key, NULL);
783 #endif
785 MonoThreadInfoFlags
786 mono_thread_info_get_flags (MonoThreadInfo *info)
788 return (MonoThreadInfoFlags)mono_atomic_load_i32 (&info->flags);
791 void
792 mono_thread_info_set_flags (MonoThreadInfoFlags flags)
794 MonoThreadInfo *info = mono_thread_info_current ();
795 MonoThreadInfoFlags old = (MonoThreadInfoFlags)mono_atomic_load_i32 (&info->flags);
797 if (threads_callbacks.thread_flags_changing)
798 threads_callbacks.thread_flags_changing (old, flags);
800 mono_atomic_store_i32 (&info->flags, flags);
802 if (threads_callbacks.thread_flags_changed)
803 threads_callbacks.thread_flags_changed (old, flags);
806 #define MONO_END_INIT_CB GINT_TO_POINTER(-1)
807 static GSList *init_callbacks;
809 void
810 mono_thread_info_wait_inited (void)
812 MonoSemType cb;
813 mono_os_sem_init (&cb, 0);
814 GSList *old = init_callbacks;
816 GSList wait_request;
817 wait_request.data = &cb;
818 wait_request.next = old;
820 while (mono_threads_inited != TRUE) {
821 GSList *old_read = (GSList*)mono_atomic_cas_ptr ((gpointer *) &init_callbacks, &wait_request, old);
823 // Queued up waiter, need to be unstuck
824 if (old_read == old) {
825 break;
826 } else if (old_read == GINT_TO_POINTER (MONO_END_INIT_CB)) {
827 // Is inited
828 return;
829 } else {
830 // We raced with another writer
831 wait_request.next = (GSList *) old_read;
832 old = old_read;
836 while (mono_threads_inited != TRUE) {
837 gboolean timedout = mono_os_sem_timedwait (&cb, 1000, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
838 if (!timedout)
839 break;
842 g_assert (mono_threads_inited);
843 return;
846 static void
847 mono_thread_info_set_inited (void)
849 mono_threads_inited = TRUE;
850 mono_memory_barrier ();
852 GSList *old = init_callbacks;
854 while (TRUE) {
855 GSList* old_read = (GSList*)mono_atomic_cas_ptr ((gpointer *) &init_callbacks, MONO_END_INIT_CB, (gpointer) old);
856 if (old == old_read)
857 break;
858 else
859 old = old_read;
861 if (old == MONO_END_INIT_CB) {
862 // Try not to use g_error / g_warning because this machinery used by logging
863 // Don't want to loop back into it.
864 fprintf (stderr, "Global threads inited twice");
865 exit (1);
866 return;
869 while (old != NULL) {
870 GSList *curr = (GSList *) old;
871 GSList *next = old->next;
873 mono_os_sem_post ((MonoSemType*)curr->data);
874 old = next;
877 return;
880 void
881 mono_thread_info_cleanup ()
883 mono_native_tls_free (thread_info_key);
884 mono_native_tls_free (thread_exited_key);
887 void
888 mono_thread_info_init (size_t info_size)
890 gboolean res;
891 thread_info_size = info_size;
892 char *sleepLimit;
893 #ifdef HOST_WIN32
894 res = mono_native_tls_alloc (&thread_info_key, NULL);
895 res = mono_native_tls_alloc (&thread_exited_key, NULL);
896 #else
897 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
898 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
899 #endif
901 g_assert (res);
903 #ifndef MONO_KEYWORD_THREAD
904 res = mono_native_tls_alloc (&small_id_key, NULL);
905 #endif
906 g_assert (res);
908 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
909 mono_set_errno (0);
910 long threshold = strtol(sleepLimit, NULL, 10);
911 if ((errno == 0) && (threshold >= 40)) {
912 sleepAbortDuration = threshold;
913 sleepWarnDuration = threshold / 20;
914 } else
915 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
916 g_free (sleepLimit);
919 mono_os_sem_init (&global_suspend_semaphore, 1);
920 mono_os_sem_init (&suspend_semaphore, 0);
921 mono_os_mutex_init (&join_mutex);
923 mono_lls_init (&thread_list, NULL);
924 mono_thread_smr_init ();
925 mono_threads_suspend_init ();
926 mono_threads_coop_init ();
927 mono_threads_platform_init ();
929 mono_thread_info_set_inited ();
931 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
934 void
935 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
937 threads_callbacks = *callbacks;
940 void
941 mono_thread_info_signals_init (void)
943 mono_threads_suspend_init_signals ();
946 void
947 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
949 runtime_callbacks = *callbacks;
952 MonoThreadInfoRuntimeCallbacks *
953 mono_threads_get_runtime_callbacks (void)
955 return &runtime_callbacks;
958 static gboolean
959 mono_thread_info_core_resume (MonoThreadInfo *info)
961 gboolean res = FALSE;
963 switch (mono_threads_transition_request_resume (info)) {
964 case ResumeError:
965 res = FALSE;
966 break;
967 case ResumeOk:
968 res = TRUE;
969 break;
970 case ResumeInitSelfResume:
971 resume_self_suspended (info);
972 res = TRUE;
973 break;
974 case ResumeInitAsyncResume:
975 resume_async_suspended (info);
976 res = TRUE;
977 break;
978 case ResumeInitBlockingResume:
979 resume_blocking_suspended (info);
980 res = TRUE;
981 break;
984 return res;
988 * Current thread must hold the global_suspend_semaphore.
989 * The given MonoThreadInfo* is a suspended thread.
990 * Must be using hybrid suspend.
992 static gboolean
993 mono_thread_info_core_pulse (MonoThreadInfo *info)
995 gboolean res = FALSE;
997 switch (mono_threads_transition_request_pulse (info)) {
998 case PulseInitAsyncPulse:
999 resume_async_suspended (info);
1000 res = TRUE;
1001 break;
1003 return res;
1006 gboolean
1007 mono_thread_info_resume (MonoNativeThreadId tid)
1009 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
1010 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1011 MonoThreadInfo *info;
1013 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
1015 mono_thread_info_suspend_lock ();
1017 info = mono_thread_info_lookup (tid); /*info on HP1*/
1018 if (!info) {
1019 result = FALSE;
1020 goto cleanup;
1023 result = mono_thread_info_core_resume (info);
1025 //Wait for the pending resume to finish
1026 mono_threads_wait_pending_operations ();
1028 cleanup:
1029 mono_thread_info_suspend_unlock ();
1030 mono_hazard_pointer_clear (hp, 1);
1031 return result;
1034 static MonoThreadBeginSuspendResult
1035 begin_suspend_request_suspension_cordially (MonoThreadInfo *info);
1037 static MonoThreadBeginSuspendResult
1038 begin_suspend_peek_and_preempt (MonoThreadInfo *info);
1041 MonoThreadBeginSuspendResult
1042 mono_thread_info_begin_suspend (MonoThreadInfo *info, MonoThreadSuspendPhase phase)
1044 if (phase == MONO_THREAD_SUSPEND_PHASE_MOPUP && mono_threads_is_hybrid_suspension_enabled ())
1045 return begin_suspend_peek_and_preempt (info);
1046 else
1047 return begin_suspend_request_suspension_cordially (info);
1050 MonoThreadBeginSuspendResult
1051 begin_suspend_request_suspension_cordially (MonoThreadInfo *info)
1053 /* Ask the thread nicely to suspend. In hybrid suspend, blocking
1054 * threads are transitioned to blocking_suspend_requested, but not
1055 * preemptively suspend in the current phase.
1057 switch (mono_threads_transition_request_suspension (info)) {
1058 case ReqSuspendAlreadySuspended:
1059 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1060 case ReqSuspendAlreadySuspendedBlocking:
1061 if (mono_threads_is_hybrid_suspension_enabled ()) {
1062 /* This should only happen in the second phase of
1063 * hybrid suspend. It means that the first phase asked
1064 * the thread to suspend but did not signal it to
1065 * suspend preemptively. */
1066 g_assert_not_reached ();
1067 } else {
1068 // This state should not be possible if we're using preemptive
1069 // suspend on a blocking thread - there can only be a single
1070 // suspend initiator at a time (guarded by
1071 // mono_thread_info_suspend_lock), and we expect the victim
1072 // thread to finish the two-phase preemptive suspension
1073 // procedure (and reach the ReqSuspendAlreadySuspended stage)
1074 // before the next suspend initiator can begin.
1075 g_assert (mono_threads_is_blocking_transition_enabled ());
1077 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1079 case ReqSuspendInitSuspendBlocking:
1080 // in full cooperative mode just leave BLOCKING
1081 // threads running until they try to return to RUNNING, so
1082 // nothing to do, in hybrid coop preempt the thread.
1083 switch (begin_suspend_for_blocking_thread (info, FALSE, MONO_THREAD_SUSPEND_PHASE_INITIAL, NULL)) {
1084 case BeginSuspendFail:
1085 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1086 case BeginSuspendOkNoWait:
1087 if (mono_threads_is_hybrid_suspension_enabled ())
1088 return MONO_THREAD_BEGIN_SUSPEND_NEXT_PHASE;
1089 else {
1090 g_assert (mono_threads_is_cooperative_suspension_enabled ());
1091 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1093 case BeginSuspendOkPreemptive:
1094 case BeginSuspendOkCooperative:
1095 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1096 default:
1097 g_assert_not_reached ();
1099 case ReqSuspendInitSuspendRunning:
1100 // in full preemptive mode this should be a preemptive suspend
1101 // in full and hybrid cooperative modes this should be a coop suspend
1102 if (begin_suspend_for_running_thread (info, FALSE) != BeginSuspendFail)
1103 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1104 else
1105 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1106 default:
1107 g_assert_not_reached ();
1111 MonoThreadBeginSuspendResult
1112 begin_suspend_peek_and_preempt (MonoThreadInfo *info)
1114 /* This only makes sense for two-phase hybrid suspension:
1115 * requires that a suspension request transition was already performed for 'info',
1116 * and if it is still in blocking_suspend_requested, preemptively suspends it.
1118 g_assert (mono_threads_is_hybrid_suspension_enabled ());
1119 if (mono_threads_transition_peek_blocking_suspend_requested (info)) {
1120 // in full cooperative mode just leave BLOCKING
1121 // threads running until they try to return to RUNNING, so
1122 // nothing to do, in hybrid coop preempt the thread.
1123 switch (begin_suspend_for_blocking_thread (info, FALSE, MONO_THREAD_SUSPEND_PHASE_MOPUP, NULL)) {
1124 case BeginSuspendFail:
1125 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1126 case BeginSuspendOkNoWait:
1127 case BeginSuspendOkCooperative:
1128 /* can't happen - should've suspended in the previous phase */
1129 g_assert_not_reached ();
1130 case BeginSuspendOkPreemptive:
1131 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1132 default:
1133 g_assert_not_reached ();
1135 } else
1136 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1139 gboolean
1140 mono_thread_info_begin_resume (MonoThreadInfo *info)
1142 return mono_thread_info_core_resume (info);
1145 gboolean
1146 mono_thread_info_begin_pulse_resume_and_request_suspension (MonoThreadInfo *info)
1148 /* For two-phase suspend, we want to atomically resume the thread and
1149 * request that it try to cooperatively suspend again. Specifically,
1150 * we really don't want it to transition from GC Safe to GC Unsafe
1151 * because we then it could (in GC Unsafe) try to take a lock that's
1152 * held by another preemptively-suspended thread, essentially
1153 * recreating the same problem that two-phase suspend intends to
1154 * fix. */
1155 if (mono_threads_is_multiphase_stw_enabled ())
1156 return mono_thread_info_core_pulse (info);
1157 else
1158 return mono_thread_info_core_resume (info);
1161 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
1162 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
1164 static gboolean
1165 is_thread_in_critical_region (MonoThreadInfo *info)
1167 gpointer stack_start;
1168 MonoThreadUnwindState *state;
1170 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
1171 return TRUE;
1173 /* Are we inside a system critical region? */
1174 if (info->inside_critical_region)
1175 return TRUE;
1177 /* Are we inside a GC critical region? */
1178 if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
1179 return TRUE;
1182 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
1183 state = mono_thread_info_get_suspend_state (info);
1184 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
1185 return FALSE;
1187 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
1188 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
1189 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
1190 return TRUE;
1192 if (threads_callbacks.ip_in_critical_region)
1193 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
1195 return FALSE;
1198 gboolean
1199 mono_thread_info_in_critical_location (MonoThreadInfo *info)
1201 return is_thread_in_critical_region (info);
1205 The return value is only valid until a matching mono_thread_info_resume is called
1207 static MonoThreadInfo*
1208 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
1210 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1211 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
1212 if (!info)
1213 return NULL;
1215 BeginSuspendResult suspend_result = BeginSuspendFail;
1216 switch (mono_threads_transition_request_suspension (info)) {
1217 case ReqSuspendAlreadySuspended:
1218 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
1219 return info;
1220 case ReqSuspendInitSuspendRunning:
1221 suspend_result = begin_suspend_for_running_thread (info, interrupt_kernel);
1222 if (suspend_result == BeginSuspendFail) {
1223 mono_hazard_pointer_clear (hp, 1);
1224 return NULL;
1226 //Wait for the pending suspend to finish
1227 g_assert (suspend_result != BeginSuspendOkNoWait);
1228 mono_threads_wait_pending_operations ();
1230 if (!check_async_suspend (info, suspend_result)) {
1231 mono_thread_info_core_resume (info);
1232 mono_threads_wait_pending_operations ();
1233 mono_hazard_pointer_clear (hp, 1);
1234 return NULL;
1236 return info;
1237 case ReqSuspendAlreadySuspendedBlocking:
1238 // ReqSuspendAlreadySuspendedBlocking should not be possible if
1239 // we're using preemptive suspend on a blocking thread - a
1240 // suspend initiator holds the mono_thread_info_suspend_lock
1241 // (there is a single suspend initiator at a time), and we
1242 // expect the victim thread to finish the two-phase preemptive
1243 // suspension procedure (and reach the
1244 // ReqSuspendAlreadySuspended stage) before the next suspend
1245 // initiator can begin.
1246 g_assert (mono_threads_is_blocking_transition_enabled () && !mono_threads_is_hybrid_suspension_enabled ());
1248 // if we tried to preempt the thread already, do nothing.
1249 // otherwise (if it's running in blocking mode) try to abort the syscall.
1250 if (interrupt_kernel)
1251 mono_threads_suspend_abort_syscall (info);
1253 return info;
1254 case ReqSuspendInitSuspendBlocking: {
1255 gboolean did_interrupt = FALSE;
1256 suspend_result = begin_suspend_for_blocking_thread (info, interrupt_kernel, MONO_THREAD_SUSPEND_PHASE_MOPUP, &did_interrupt);
1257 if (suspend_result == BeginSuspendFail) {
1258 mono_hazard_pointer_clear (hp, 1);
1259 return NULL;
1262 if (suspend_result != BeginSuspendOkNoWait)
1263 mono_threads_wait_pending_operations ();
1265 if (!check_async_suspend (info, suspend_result)) {
1266 mono_thread_info_core_resume (info);
1267 mono_threads_wait_pending_operations ();
1268 mono_hazard_pointer_clear (hp, 1);
1269 return NULL;
1272 // if we tried to preempt the thread already, do nothing.
1273 // otherwise (if it's running in blocking mode) try to abort the syscall.
1274 if (interrupt_kernel && !did_interrupt)
1275 mono_threads_suspend_abort_syscall (info);
1277 return info;
1279 default:
1280 g_assert_not_reached ();
1282 g_assert_not_reached ();
1285 static MonoThreadInfo*
1286 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
1288 MonoThreadInfo *info = NULL;
1289 int sleep_duration = 0;
1290 for (;;) {
1291 if (!(info = suspend_sync (id, interrupt_kernel))) {
1292 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1293 return NULL;
1296 /*WARNING: We now are in interrupt context until we resume the thread. */
1297 if (!is_thread_in_critical_region (info))
1298 break;
1300 if (!mono_thread_info_core_resume (info)) {
1301 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1302 return NULL;
1304 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
1306 /* Wait for the pending resume to finish */
1307 mono_threads_wait_pending_operations ();
1309 if (sleep_duration == 0)
1310 mono_thread_info_yield ();
1311 else
1312 g_usleep (sleep_duration);
1314 sleep_duration += 10;
1316 return info;
1319 void
1320 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
1322 int result;
1323 MonoThreadInfo *info = NULL;
1324 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1326 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s)\n", (void*)id, interrupt_kernel ? "int" : "");
1327 /*FIXME: unify this with self-suspend*/
1328 g_assert (id != mono_native_thread_id_get ());
1330 /* This can block during stw */
1331 mono_thread_info_suspend_lock ();
1332 mono_threads_begin_global_suspend ();
1334 info = suspend_sync_nolock (id, interrupt_kernel);
1335 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s): info %p\n", (void*)id, interrupt_kernel ? "int" : "", info);
1336 if (!info)
1337 goto done;
1339 switch (result = callback (info, user_data)) {
1340 case MonoResumeThread:
1341 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): MonoResumeThread\n", (void*)id, interrupt_kernel ? "int" : "");
1342 mono_hazard_pointer_set (hp, 1, info);
1343 mono_thread_info_core_resume (info);
1344 mono_threads_wait_pending_operations ();
1345 #ifdef USE_WINDOWS_BACKEND
1346 // If we interrupt kernel but have blocking sync IO requests preventing the thread from running APC's
1347 // try to abort all sync blocking IO request. This must be done after thread has been resumed, but before releasing
1348 // global suspend lock (preventing other threads from supsending the thread).
1349 if (interrupt_kernel)
1350 mono_win32_abort_blocking_io_call (info);
1351 #endif
1352 break;
1353 case KeepSuspended:
1354 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): KeepSuspended\n", (void*)id, interrupt_kernel ? "int" : "");
1355 g_assert (!mono_threads_are_safepoints_enabled ());
1356 break;
1357 default:
1358 g_error ("Invalid suspend_and_run callback return value %d", result);
1361 done:
1362 mono_hazard_pointer_clear (hp, 1);
1363 mono_threads_end_global_suspend ();
1364 mono_thread_info_suspend_unlock ();
1368 Inject an assynchronous call into the target thread. The target thread must be suspended and
1369 only a single async call can be setup for a given suspend cycle.
1370 This async call must cause stack unwinding as the current implementation doesn't save enough state
1371 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1372 currently used only to deliver exceptions.
1374 void
1375 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1377 if (!mono_threads_are_safepoints_enabled ()) {
1378 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1379 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1380 * region or entering a gc unsafe region */
1381 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1383 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1384 g_assert (!info->async_target);
1385 info->async_target = target_func;
1386 /* This is not GC tracked */
1387 info->user_data = user_data;
1391 The suspend lock is held during any suspend in progress.
1392 A GC that has safepoints must take this lock as part of its
1393 STW to make sure no unsafe pending suspend is in progress.
1396 static void
1397 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1399 g_assert (info);
1400 g_assert (mono_thread_info_is_current (info));
1401 g_assert (mono_thread_info_is_live (info));
1403 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1405 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1406 g_assert (res != -1);
1408 MONO_EXIT_GC_SAFE_WITH_INFO;
1411 void
1412 mono_thread_info_suspend_lock (void)
1414 MonoThreadInfo *info;
1415 gint res;
1417 info = mono_thread_info_current_unchecked ();
1418 if (info && mono_thread_info_is_live (info)) {
1419 mono_thread_info_suspend_lock_with_info (info);
1420 return;
1423 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1424 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1426 res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1427 g_assert (res != -1);
1430 void
1431 mono_thread_info_suspend_unlock (void)
1433 mono_os_sem_post (&global_suspend_semaphore);
1436 /* Return the suspend state for the current thread. Note: the thread must be
1437 * already suspended in order for this function to be callable.
1439 MonoThreadUnwindState*
1440 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
1442 int cur_state = mono_thread_info_current_state (info);
1443 switch (cur_state) {
1444 case STATE_ASYNC_SUSPENDED:
1445 case STATE_BLOCKING_ASYNC_SUSPENDED:
1446 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
1447 case STATE_SELF_SUSPENDED:
1448 case STATE_BLOCKING_SELF_SUSPENDED:
1449 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
1450 case STATE_BLOCKING_SUSPEND_REQUESTED:
1451 // This state is only valid for full cooperative suspend. If
1452 // we're preemptively suspending blocking threads, this is not
1453 // a valid suspend state.
1454 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
1455 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
1456 break;
1457 default:
1458 break;
1461 STATE_RUNNING
1462 STATE_ASYNC_SUSPEND_REQUESTED
1463 STATE_BLOCKING: All those are invalid suspend states.
1464 STATE_BLOCKING_SUSPEND_REQUESTED: Invalid if we're preemptively suspending blocking threads.
1466 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), mono_thread_state_name (cur_state));
1470 * This is a very specific function whose only purpose is to
1471 * break a given thread from socket syscalls.
1473 * This only exists because linux won't fail a call to connect
1474 * if the underlying is closed.
1476 * TODO We should cleanup and unify this with the other syscall abort
1477 * facility.
1479 void
1480 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1482 MonoThreadHazardPointers *hp;
1483 MonoThreadInfo *info;
1485 if (tid == mono_native_thread_id_get ())
1486 return;
1488 mono_thread_info_suspend_lock ();
1489 hp = mono_hazard_pointer_get ();
1490 info = mono_thread_info_lookup (tid);
1491 if (!info) {
1492 mono_thread_info_suspend_unlock ();
1493 return;
1495 mono_threads_begin_global_suspend ();
1497 mono_threads_suspend_abort_syscall (info);
1498 mono_threads_wait_pending_operations ();
1500 mono_hazard_pointer_clear (hp, 1);
1502 mono_threads_end_global_suspend ();
1503 mono_thread_info_suspend_unlock ();
1507 * mono_thread_info_set_is_async_context:
1509 * Set whenever the current thread is in an async context. Some runtime functions might behave
1510 * differently while in an async context in order to be async safe.
1512 void
1513 mono_thread_info_set_is_async_context (gboolean async_context)
1515 MonoThreadInfo *info = mono_thread_info_current ();
1517 if (info) {
1518 // If this assert fails, that means there is recursion and/or
1519 // concurrency, such that setting async_context to FALSE
1520 // that all the callers do after this, is incorrect,
1521 // and this should instead be incremented/decremented.
1523 // As the value is only accessed via current(), that
1524 // limits the case to recursion, but increment/decrement
1525 // is still fast and correct and simple.
1526 g_assert (!async_context || !info->is_async_context);
1527 info->is_async_context = async_context;
1531 gboolean
1532 mono_thread_info_is_async_context (void)
1534 MonoThreadInfo *info = mono_thread_info_current ();
1536 if (info)
1537 return info->is_async_context;
1538 else
1539 return FALSE;
1543 * mono_thread_info_get_stack_bounds:
1545 * Return the address and size of the current threads stack. Return NULL as the
1546 * stack address if the stack address cannot be determined.
1548 void
1549 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1551 guint8 *current = (guint8 *)&stsize;
1552 mono_threads_platform_get_stack_bounds (staddr, stsize);
1553 if (!*staddr)
1554 return;
1556 /* Sanity check the result */
1557 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1559 /* When running under emacs, sometimes staddr is not aligned to a page size */
1560 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1563 gboolean
1564 mono_thread_info_yield (void)
1566 return mono_threads_platform_yield ();
1569 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1570 static MonoCoopMutex sleep_mutex;
1571 static MonoCoopCond sleep_cond;
1573 static void
1574 sleep_initialize (void)
1576 mono_coop_mutex_init (&sleep_mutex);
1577 mono_coop_cond_init (&sleep_cond);
1580 static void
1581 sleep_interrupt (gpointer data)
1583 mono_coop_mutex_lock (&sleep_mutex);
1584 mono_coop_cond_broadcast (&sleep_cond);
1585 mono_coop_mutex_unlock (&sleep_mutex);
1588 static inline guint32
1589 sleep_interruptable (guint32 ms, gboolean *alerted)
1591 gint64 now, end;
1593 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1595 g_assert (alerted);
1596 *alerted = FALSE;
1598 if (ms != MONO_INFINITE_WAIT)
1599 end = mono_msec_ticks() + ms;
1601 mono_lazy_initialize (&sleep_init, sleep_initialize);
1603 mono_coop_mutex_lock (&sleep_mutex);
1605 for (;;) {
1606 if (ms != MONO_INFINITE_WAIT) {
1607 now = mono_msec_ticks();
1608 if (now >= end)
1609 break;
1612 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1613 if (*alerted) {
1614 mono_coop_mutex_unlock (&sleep_mutex);
1615 return WAIT_IO_COMPLETION;
1618 if (ms != MONO_INFINITE_WAIT)
1619 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1620 else
1621 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1623 mono_thread_info_uninstall_interrupt (alerted);
1624 if (*alerted) {
1625 mono_coop_mutex_unlock (&sleep_mutex);
1626 return WAIT_IO_COMPLETION;
1630 mono_coop_mutex_unlock (&sleep_mutex);
1632 return 0;
1635 gint
1636 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1638 if (ms == 0) {
1639 MonoThreadInfo *info;
1641 mono_thread_info_yield ();
1643 info = mono_thread_info_current ();
1644 if (info && mono_thread_info_is_interrupt_state (info))
1645 return WAIT_IO_COMPLETION;
1647 return 0;
1650 if (alerted)
1651 return sleep_interruptable (ms, alerted);
1653 MONO_ENTER_GC_SAFE;
1655 if (ms == MONO_INFINITE_WAIT) {
1656 do {
1657 #ifdef HOST_WIN32
1658 Sleep (G_MAXUINT32);
1659 #else
1660 sleep (G_MAXUINT32);
1661 #endif
1662 } while (1);
1663 } else {
1664 int ret;
1665 #if defined (__linux__) && !defined(HOST_ANDROID)
1666 struct timespec start, target;
1668 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1669 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1670 g_assert (ret == 0);
1672 target = start;
1673 target.tv_sec += ms / 1000;
1674 target.tv_nsec += (ms % 1000) * 1000000;
1675 if (target.tv_nsec > 999999999) {
1676 target.tv_nsec -= 999999999;
1677 target.tv_sec ++;
1680 do {
1681 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1682 } while (ret != 0);
1683 #elif HOST_WIN32
1684 Sleep (ms);
1685 #else
1686 struct timespec req, rem;
1688 req.tv_sec = ms / 1000;
1689 req.tv_nsec = (ms % 1000) * 1000000;
1691 do {
1692 memset (&rem, 0, sizeof (rem));
1693 ret = nanosleep (&req, &rem);
1694 } while (ret != 0);
1695 #endif /* __linux__ */
1698 MONO_EXIT_GC_SAFE;
1700 return 0;
1703 gint
1704 mono_thread_info_usleep (guint64 us)
1706 MONO_ENTER_GC_SAFE;
1707 g_usleep (us);
1708 MONO_EXIT_GC_SAFE;
1709 return 0;
1712 gpointer
1713 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1715 return ((MonoThreadInfo*)info)->tls [key];
1719 * mono_threads_info_tls_set:
1721 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1722 * values of TLS variables for threads other than the current thread.
1723 * This should only be used for infrequently changing TLS variables, and it should
1724 * be paired with setting the real TLS variable since this provides no GC tracking.
1726 void
1727 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1729 ((MonoThreadInfo*)info)->tls [key] = value;
1733 * mono_thread_info_exit:
1735 * Exit the current thread.
1736 * This function doesn't return.
1738 void
1739 mono_thread_info_exit (gsize exit_code)
1741 mono_thread_info_detach ();
1743 mono_threads_platform_exit (0);
1747 * mono_threads_open_thread_handle:
1749 * Duplicate the handle. The handle needs to be closed by calling
1750 * mono_threads_close_thread_handle () when it is no longer needed.
1752 MonoThreadHandle*
1753 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1755 return mono_refcount_inc (thread_handle);
1758 void
1759 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1761 if (!thread_handle)
1762 return;
1763 mono_refcount_dec (thread_handle);
1766 static void
1767 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1769 mono_os_event_set (&thread_handle->event);
1772 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1774 struct _MonoThreadInfoInterruptToken {
1775 void (*callback) (gpointer data);
1776 gpointer data;
1780 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1782 * - @callback: must be able to be called from another thread and always cancel the wait
1783 * - @data: passed to the callback
1784 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1785 * if set to TRUE, it must mean that the thread is in interrupted state
1787 void
1788 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1790 MonoThreadInfo *info;
1791 MonoThreadInfoInterruptToken *previous_token, *token;
1793 g_assert (callback);
1795 g_assert (interrupted);
1796 *interrupted = FALSE;
1798 info = mono_thread_info_current ();
1799 g_assert (info);
1801 /* The memory of this token can be freed at 2 places:
1802 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1803 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1804 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1805 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1806 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1807 token->callback = callback;
1808 token->data = data;
1810 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, token, NULL);
1812 if (previous_token) {
1813 if (previous_token != INTERRUPT_STATE)
1814 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1816 g_free (token);
1818 *interrupted = TRUE;
1821 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1822 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1825 void
1826 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1828 MonoThreadInfo *info;
1829 MonoThreadInfoInterruptToken *previous_token;
1831 g_assert (interrupted);
1832 *interrupted = FALSE;
1834 info = mono_thread_info_current ();
1835 g_assert (info);
1837 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_xchg_ptr ((gpointer*) &info->interrupt_token, NULL);
1839 /* only the installer can uninstall the token */
1840 g_assert (previous_token);
1842 if (previous_token == INTERRUPT_STATE) {
1843 /* if it is interrupted, then it is going to be freed in finish interrupt */
1844 *interrupted = TRUE;
1845 } else {
1846 g_free (previous_token);
1849 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1850 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1853 static MonoThreadInfoInterruptToken*
1854 set_interrupt_state (MonoThreadInfo *info)
1856 MonoThreadInfoInterruptToken *token, *previous_token;
1858 g_assert (info);
1860 /* Atomically obtain the token the thread is
1861 * waiting on, and change it to a flag value. */
1863 do {
1864 previous_token = info->interrupt_token;
1866 /* Already interrupted */
1867 if (previous_token == INTERRUPT_STATE) {
1868 token = NULL;
1869 break;
1872 token = previous_token;
1873 } while (mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1875 return token;
1879 * mono_thread_info_prepare_interrupt:
1881 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1882 * - if the thread calls one of the WaitFor functions, the function will return with
1883 * WAIT_IO_COMPLETION instead of waiting
1884 * - if the thread was waiting when this function was called, the wait will be broken
1886 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1887 * didn't receive the interrupt signal yet, in this case it should call the wait function
1888 * again. This essentially means that the target thread will busy wait until it is ready to
1889 * process the interruption.
1891 MonoThreadInfoInterruptToken*
1892 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1894 MonoThreadInfoInterruptToken *token;
1896 token = set_interrupt_state (info);
1898 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1899 mono_thread_info_get_tid (info), token);
1901 return token;
1904 void
1905 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1907 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1909 if (token == NULL)
1910 return;
1912 g_assert (token->callback);
1914 token->callback (token->data);
1916 g_free (token);
1919 void
1920 mono_thread_info_self_interrupt (void)
1922 MonoThreadInfo *info;
1923 MonoThreadInfoInterruptToken *token;
1925 info = mono_thread_info_current ();
1926 g_assert (info);
1928 token = set_interrupt_state (info);
1929 g_assert (!token);
1931 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1932 mono_thread_info_get_tid (info));
1935 /* Clear the interrupted flag of the current thread, set with
1936 * mono_thread_info_self_interrupt, so it can wait again */
1937 void
1938 mono_thread_info_clear_self_interrupt (void)
1940 MonoThreadInfo *info;
1941 MonoThreadInfoInterruptToken *previous_token;
1943 info = mono_thread_info_current ();
1944 g_assert (info);
1946 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1947 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1949 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1952 gboolean
1953 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1955 g_assert (info);
1956 return mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1959 void
1960 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1962 g_assert (info);
1964 if (!mono_atomic_load_ptr ((gpointer*) &info->interrupt_token))
1965 g_string_append_printf (text, "not waiting");
1966 else if (mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1967 g_string_append_printf (text, "interrupted state");
1968 else
1969 g_string_append_printf (text, "waiting");
1972 gboolean
1973 mono_thread_info_is_current (MonoThreadInfo *info)
1975 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1978 MonoThreadInfoWaitRet
1979 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1981 MonoOSEventWaitRet res;
1983 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1984 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1985 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1986 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1987 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1988 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1989 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1990 else
1991 g_error ("%s: unknown res value %d", __func__, res);
1994 MonoThreadInfoWaitRet
1995 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1997 MonoOSEventWaitRet res;
1998 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1999 gint i;
2001 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
2002 if (background_change_event)
2003 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
2005 for (i = 0; i < nhandles; ++i)
2006 thread_events [i] = &thread_handles [i]->event;
2008 if (background_change_event)
2009 thread_events [nhandles ++] = background_change_event;
2011 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
2012 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
2013 return (MonoThreadInfoWaitRet)(MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0));
2014 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
2015 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
2016 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
2017 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2018 else
2019 g_error ("%s: unknown res value %d", __func__, res);
2023 * mono_threads_join_mutex:
2025 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
2026 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
2027 * The code inside the lock should not block.
2029 void
2030 mono_threads_join_lock (void)
2032 #ifdef TARGET_OSX
2033 mono_os_mutex_lock (&join_mutex);
2034 #endif
2037 void
2038 mono_threads_join_unlock (void)
2040 #ifdef TARGET_OSX
2041 mono_os_mutex_unlock (&join_mutex);
2042 #endif
2046 gboolean
2047 mono_thread_info_set_tools_data (void *data)
2049 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
2050 if (!info)
2051 return FALSE;
2052 if (info->tools_data)
2053 return FALSE;
2054 info->tools_data = data;
2055 return TRUE;
2058 void*
2059 mono_thread_info_get_tools_data (void)
2061 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
2063 return info ? info->tools_data : NULL;