2 * mono-threads.c: Low-level threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 /* enable pthread extensions */
15 #define _DARWIN_C_SOURCE
18 #include <mono/utils/mono-compiler.h>
19 #include <mono/utils/mono-semaphore.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-lazy-init.h>
33 #include <mono/utils/mach-support.h>
37 Mutex that makes sure only a single thread can be suspending others.
38 Suspend is a very racy operation since it requires restarting until
39 the target thread is not on an unsafe region.
41 We could implement this using critical regions, but would be much much
42 harder for an operation that is hardly performance critical.
44 The GC has to acquire this lock before starting a STW to make sure
45 a runtime suspend won't make it wronly see a thread in a safepoint
46 when it is in fact not.
48 static MonoSemType global_suspend_semaphore;
50 static size_t thread_info_size;
51 static MonoThreadInfoCallbacks threads_callbacks;
52 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
53 static MonoNativeTlsKey thread_info_key, thread_exited_key;
55 static __thread guint32 tls_small_id MONO_TLS_FAST;
57 static MonoNativeTlsKey small_id_key;
59 static MonoLinkedListSet thread_list;
60 static gboolean mono_threads_inited = FALSE;
62 static MonoSemType suspend_semaphore;
63 static size_t pending_suspends;
64 static gboolean unified_suspend_enabled;
66 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
69 #define SLEEP_DURATION_BEFORE_WARNING (10)
71 #define SLEEP_DURATION_BEFORE_ABORT 200
73 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
76 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
78 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
79 InterlockedIncrement (&abort_posts);
80 MONO_SEM_POST (&suspend_semaphore);
84 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
86 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
87 InterlockedIncrement (&suspend_posts);
88 MONO_SEM_POST (&suspend_semaphore);
92 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
94 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
95 InterlockedIncrement (&resume_posts);
96 MONO_SEM_POST (&suspend_semaphore);
100 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
102 if (mono_threads_is_coop_enabled ()) {
103 /* There's nothing else to do after we async request the thread to suspend */
104 mono_threads_add_to_pending_operation_set (info);
108 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
112 check_async_suspend (MonoThreadInfo *info)
114 if (mono_threads_is_coop_enabled ()) {
115 /* Async suspend can't async fail on coop */
119 return mono_threads_core_check_suspend_result (info);
123 resume_async_suspended (MonoThreadInfo *info)
125 if (mono_threads_is_coop_enabled ())
126 g_assert_not_reached ();
128 g_assert (mono_threads_core_begin_async_resume (info));
132 resume_self_suspended (MonoThreadInfo* info)
134 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
135 MONO_SEM_POST (&info->resume_semaphore);
139 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
141 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
142 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
146 resume_blocking_suspended (MonoThreadInfo* info)
148 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
149 MONO_SEM_POST (&info->resume_semaphore);
153 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
155 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
157 InterlockedIncrement (&pending_ops);
161 mono_threads_begin_global_suspend (void)
163 size_t ps = pending_suspends;
164 if (G_UNLIKELY (ps != 0))
165 g_error ("pending_suspends = %d, but must be 0", ps);
166 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,
167 abort_posts, waits_done, pending_ops);
168 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
169 mono_threads_coop_begin_global_suspend ();
173 mono_threads_end_global_suspend (void)
175 size_t ps = pending_suspends;
176 if (G_UNLIKELY (ps != 0))
177 g_error ("pending_suspends = %d, but must be 0", ps);
178 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
179 abort_posts, waits_done, pending_ops);
180 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
181 mono_threads_coop_end_global_suspend ();
187 MonoThreadInfo *info;
188 MonoThreadInfo *cur = mono_thread_info_current ();
190 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
191 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
192 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
193 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
194 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
195 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
196 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
197 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
198 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
199 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
201 FOREACH_THREAD_SAFE (info) {
203 char thread_name [256] = { 0 };
204 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
206 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" : "" );
208 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" : "" );
211 } END_FOREACH_THREAD_SAFE
215 mono_threads_wait_pending_operations (void)
218 int c = pending_suspends;
220 /* Wait threads to park */
221 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
222 if (pending_suspends) {
223 MonoStopwatch suspension_time;
224 mono_stopwatch_start (&suspension_time);
225 for (i = 0; i < pending_suspends; ++i) {
226 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
227 InterlockedIncrement (&waits_done);
228 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
230 mono_stopwatch_stop (&suspension_time);
234 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
235 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), SLEEP_DURATION_BEFORE_ABORT);
237 mono_stopwatch_stop (&suspension_time);
238 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
242 pending_suspends = 0;
248 //Thread initialization code
250 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
253 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
256 mono_hazard_pointer_clear (hp, 0);
258 mono_hazard_pointer_clear (hp, 1);
260 mono_hazard_pointer_clear (hp, 2);
264 If return non null Hazard Pointer 1 holds the return value.
267 mono_thread_info_lookup (MonoNativeThreadId id)
269 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
271 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
272 mono_hazard_pointer_clear_all (hp, -1);
276 mono_hazard_pointer_clear_all (hp, 1);
277 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
281 mono_thread_info_insert (MonoThreadInfo *info)
283 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
285 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
286 mono_hazard_pointer_clear_all (hp, -1);
290 mono_hazard_pointer_clear_all (hp, -1);
295 mono_thread_info_remove (MonoThreadInfo *info)
297 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
300 THREADS_DEBUG ("removing info %p\n", info);
301 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
302 mono_hazard_pointer_clear_all (hp, -1);
307 free_thread_info (gpointer mem)
309 MonoThreadInfo *info = (MonoThreadInfo *) mem;
311 MONO_SEM_DESTROY (&info->resume_semaphore);
312 mono_threads_platform_free (info);
318 mono_thread_info_register_small_id (void)
320 int small_id = mono_thread_small_id_alloc ();
321 #ifdef HAVE_KW_THREAD
322 tls_small_id = small_id;
324 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
330 register_thread (MonoThreadInfo *info, gpointer baseptr)
333 guint8 *staddr = NULL;
334 int small_id = mono_thread_info_register_small_id ();
336 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
337 info->small_id = small_id;
339 MONO_SEM_INIT (&info->resume_semaphore, 0);
341 /*set TLS early so SMR works */
342 mono_native_tls_set_value (thread_info_key, info);
344 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
346 if (threads_callbacks.thread_register) {
347 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
348 // g_warning ("thread registation failed\n");
354 mono_thread_info_get_stack_bounds (&staddr, &stsize);
357 info->stack_start_limit = staddr;
358 info->stack_end = staddr + stsize;
360 info->stackdata = g_byte_array_new ();
362 mono_threads_platform_register (info);
365 Transition it before taking any locks or publishing itself to reduce the chance
366 of others witnessing a detached thread.
367 We can reasonably expect that until this thread gets published, no other thread will
368 try to manipulate it.
370 mono_threads_transition_attach (info);
371 mono_thread_info_suspend_lock ();
372 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
373 result = mono_thread_info_insert (info);
375 mono_thread_info_suspend_unlock ();
380 unregister_thread (void *arg)
382 MonoThreadInfo *info = (MonoThreadInfo *) arg;
383 int small_id = info->small_id;
386 THREADS_DEBUG ("unregistering info %p\n", info);
388 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
390 mono_threads_core_unregister (info);
393 * TLS destruction order is not reliable so small_id might be cleaned up
396 #ifndef HAVE_KW_THREAD
397 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
401 First perform the callback that requires no locks.
402 This callback has the potential of taking other locks, so we do it before.
403 After it completes, the thread remains functional.
405 if (threads_callbacks.thread_detach)
406 threads_callbacks.thread_detach (info);
408 mono_thread_info_suspend_lock ();
411 Now perform the callback that must be done under locks.
412 This will render the thread useless and non-suspendable, so it must
413 be done while holding the suspend lock to give no other thread chance
416 if (threads_callbacks.thread_unregister)
417 threads_callbacks.thread_unregister (info);
418 mono_threads_unregister_current_thread (info);
419 mono_threads_transition_detach (info);
421 mono_thread_info_suspend_unlock ();
423 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
425 /*now it's safe to free the thread info.*/
426 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
427 mono_thread_small_id_free (small_id);
431 thread_exited_dtor (void *arg)
433 #if defined(__MACH__)
435 * Since we use pthread dtors to clean up thread data, if a thread
436 * is attached to the runtime by another pthread dtor after our dtor
437 * has ran, it will never be detached, leading to various problems
438 * since the thread ids etc. will be reused while they are still in
439 * the threads hashtables etc.
440 * Dtors are called in a loop until all user tls entries are 0,
441 * but the loop has a maximum count (4), so if we set the tls
442 * variable every time, it will remain set when system tls dtors
443 * are ran. This allows mono_thread_info_is_exiting () to detect
444 * whenever the thread is exiting, even if it is executed from a
445 * system tls dtor (i.e. obj-c dealloc methods).
447 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
452 * Removes the current thread from the thread list.
453 * This must be called from the thread unregister callback and nowhere else.
454 * The current thread must be passed as TLS might have already been cleaned up.
457 mono_threads_unregister_current_thread (MonoThreadInfo *info)
460 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
461 result = mono_thread_info_remove (info);
466 mono_thread_info_current_unchecked (void)
468 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
473 mono_thread_info_current (void)
475 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
479 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
482 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
483 The way to distinguish between before, during and after cleanup is the following:
485 -If the TLS key is set, cleanup has not begun;
486 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
487 -If the thread is nowhere to be found, cleanup has finished.
489 We cannot function after cleanup since there's no way to ensure what will happen.
493 /*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 */
494 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
500 mono_thread_info_get_small_id (void)
502 #ifdef HAVE_KW_THREAD
505 gpointer val = mono_native_tls_get_value (small_id_key);
508 return GPOINTER_TO_INT (val) - 1;
513 mono_thread_info_list_head (void)
519 * mono_threads_attach_tools_thread
521 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
523 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
524 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
527 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
528 * doing things like resolving backtraces in their background processing thread.
531 mono_threads_attach_tools_thread (void)
534 MonoThreadInfo *info;
536 /* Must only be called once */
537 g_assert (!mono_native_tls_get_value (thread_info_key));
539 while (!mono_threads_inited) {
543 info = mono_thread_info_attach (&dummy);
546 info->tools_thread = TRUE;
550 mono_thread_info_attach (void *baseptr)
552 MonoThreadInfo *info;
553 if (!mono_threads_inited)
556 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
557 * thread is created before an embedding API user initialized Mono. */
558 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
561 g_assert (mono_threads_inited);
564 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
566 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
567 THREADS_DEBUG ("attaching %p\n", info);
568 if (!register_thread (info, baseptr))
570 } else if (threads_callbacks.thread_attach) {
571 threads_callbacks.thread_attach (info);
577 mono_thread_info_detach (void)
579 MonoThreadInfo *info;
580 if (!mono_threads_inited)
582 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
583 * is created before an embedding API user initialized Mono. */
584 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
587 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
589 THREADS_DEBUG ("detaching %p\n", info);
590 unregister_thread (info);
591 mono_native_tls_set_value (thread_info_key, NULL);
596 * mono_thread_info_is_exiting:
598 * Return whenever the current thread is exiting, i.e. it is running pthread
602 mono_thread_info_is_exiting (void)
604 #if defined(__MACH__)
605 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
612 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
615 threads_callbacks = *callbacks;
616 thread_info_size = info_size;
618 res = mono_native_tls_alloc (&thread_info_key, NULL);
619 res = mono_native_tls_alloc (&thread_exited_key, NULL);
621 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
622 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
627 #ifndef HAVE_KW_THREAD
628 res = mono_native_tls_alloc (&small_id_key, NULL);
632 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
634 MONO_SEM_INIT (&global_suspend_semaphore, 1);
635 MONO_SEM_INIT (&suspend_semaphore, 0);
637 mono_lls_init (&thread_list, NULL);
638 mono_thread_smr_init ();
639 mono_threads_init_platform ();
640 mono_threads_init_coop ();
641 mono_threads_init_abort_syscall ();
643 #if defined(__MACH__)
644 mono_mach_init (thread_info_key);
647 mono_threads_inited = TRUE;
649 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
653 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
655 runtime_callbacks = *callbacks;
658 MonoThreadInfoRuntimeCallbacks *
659 mono_threads_get_runtime_callbacks (void)
661 return &runtime_callbacks;
665 The return value is only valid until a matching mono_thread_info_resume is called
667 static MonoThreadInfo*
668 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
670 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
671 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
673 *error_condition = "Thread not found";
677 switch (mono_threads_transition_request_async_suspension (info)) {
678 case AsyncSuspendAlreadySuspended:
679 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
681 case AsyncSuspendWait:
682 mono_threads_add_to_pending_operation_set (info);
684 case AsyncSuspendInitSuspend:
685 if (!begin_async_suspend (info, interrupt_kernel)) {
686 mono_hazard_pointer_clear (hp, 1);
687 *error_condition = "Could not suspend thread";
692 //Wait for the pending suspend to finish
693 mono_threads_wait_pending_operations ();
695 if (!check_async_suspend (info)) {
696 mono_hazard_pointer_clear (hp, 1);
697 *error_condition = "Post suspend failed";
704 Signal that the current thread wants to be suspended.
705 This function can be called without holding the suspend lock held.
706 To finish suspending, call mono_suspend_check.
709 mono_thread_info_begin_self_suspend (void)
711 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
715 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
716 mono_threads_transition_request_self_suspension (info);
720 mono_thread_info_end_self_suspend (void)
722 MonoThreadInfo *info;
724 info = mono_thread_info_current ();
727 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
729 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
731 /* commit the saved state and notify others if needed */
732 switch (mono_threads_transition_state_poll (info)) {
733 case SelfSuspendResumed:
735 case SelfSuspendWait:
736 mono_thread_info_wait_for_resume (info);
738 case SelfSuspendNotifyAndWait:
739 mono_threads_notify_initiator_of_suspend (info);
740 mono_thread_info_wait_for_resume (info);
741 mono_threads_notify_initiator_of_resume (info);
747 mono_thread_info_core_resume (MonoThreadInfo *info)
749 gboolean res = FALSE;
750 if (info->create_suspended) {
751 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
752 /* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
753 info->create_suspended = FALSE;
754 mono_threads_core_resume_created (info, tid);
758 switch (mono_threads_transition_request_resume (info)) {
765 case ResumeInitSelfResume:
766 resume_self_suspended (info);
769 case ResumeInitAsyncResume:
770 resume_async_suspended (info);
773 case ResumeInitBlockingResume:
774 resume_blocking_suspended (info);
783 mono_thread_info_resume (MonoNativeThreadId tid)
785 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
786 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
787 MonoThreadInfo *info;
789 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
791 mono_thread_info_suspend_lock ();
793 info = mono_thread_info_lookup (tid); /*info on HP1*/
799 result = mono_thread_info_core_resume (info);
801 //Wait for the pending resume to finish
802 mono_threads_wait_pending_operations ();
805 mono_thread_info_suspend_unlock ();
806 mono_hazard_pointer_clear (hp, 1);
811 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
813 switch (mono_threads_transition_request_async_suspension (info)) {
814 case AsyncSuspendAlreadySuspended:
816 case AsyncSuspendWait:
817 mono_threads_add_to_pending_operation_set (info);
819 case AsyncSuspendInitSuspend:
820 return begin_async_suspend (info, interrupt_kernel);
822 g_assert_not_reached ();
827 mono_thread_info_begin_resume (MonoThreadInfo *info)
829 return mono_thread_info_core_resume (info);
833 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
835 return check_async_suspend (info);
839 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
840 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
843 is_thread_in_critical_region (MonoThreadInfo *info)
847 gpointer stack_start;
848 MonoThreadUnwindState *state;
850 /* Are we inside a system critical region? */
851 if (info->inside_critical_region)
854 /* Are we inside a GC critical region? */
855 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
859 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
860 state = mono_thread_info_get_suspend_state (info);
861 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
864 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
865 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
866 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
869 ji = mono_jit_info_table_find (
870 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
871 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
876 method = mono_jit_info_get_method (ji);
878 return threads_callbacks.mono_method_is_critical (method);
882 mono_thread_info_in_critical_location (MonoThreadInfo *info)
884 return is_thread_in_critical_region (info);
887 static MonoThreadInfo*
888 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
890 MonoThreadInfo *info = NULL;
891 int sleep_duration = 0;
893 const char *suspend_error = "Unknown error";
894 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
895 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
899 /*WARNING: We now are in interrupt context until we resume the thread. */
900 if (!is_thread_in_critical_region (info))
903 if (!mono_thread_info_core_resume (info)) {
904 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
907 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
909 /* Wait for the pending resume to finish */
910 mono_threads_wait_pending_operations ();
912 if (!sleep_duration) {
920 g_usleep (sleep_duration);
922 sleep_duration += 10;
928 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
931 MonoThreadInfo *info = NULL;
932 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
934 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
935 /*FIXME: unify this with self-suspend*/
936 g_assert (id != mono_native_thread_id_get ());
938 /* This can block during stw */
939 mono_thread_info_suspend_lock ();
940 mono_threads_begin_global_suspend ();
942 info = suspend_sync_nolock (id, interrupt_kernel);
946 switch (result = callback (info, user_data)) {
947 case MonoResumeThread:
948 mono_hazard_pointer_set (hp, 1, info);
949 mono_thread_info_core_resume (info);
950 mono_threads_wait_pending_operations ();
955 g_error ("Invalid suspend_and_run callback return value %d", result);
959 mono_hazard_pointer_clear (hp, 1);
960 mono_threads_end_global_suspend ();
961 mono_thread_info_suspend_unlock ();
966 If we are trying to suspend a target that is on a critical region
967 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
968 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
970 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
972 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
975 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
977 MonoThreadInfo *info = NULL;
979 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
980 /*FIXME: unify this with self-suspend*/
981 g_assert (id != mono_native_thread_id_get ());
983 mono_thread_info_suspend_lock ();
984 mono_threads_begin_global_suspend ();
986 info = suspend_sync_nolock (id, interrupt_kernel);
988 /* XXX this clears HP 1, so we restated it again */
989 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
990 mono_threads_end_global_suspend ();
991 mono_thread_info_suspend_unlock ();
997 Inject an assynchronous call into the target thread. The target thread must be suspended and
998 only a single async call can be setup for a given suspend cycle.
999 This async call must cause stack unwinding as the current implementation doesn't save enough state
1000 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1001 currently used only to deliver exceptions.
1004 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1006 /* An async call can only be setup on an async suspended thread */
1007 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1008 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1009 g_assert (!info->async_target);
1010 info->async_target = target_func;
1011 /* This is not GC tracked */
1012 info->user_data = user_data;
1016 The suspend lock is held during any suspend in progress.
1017 A GC that has safepoints must take this lock as part of its
1018 STW to make sure no unsafe pending suspend is in progress.
1021 mono_thread_info_suspend_lock (void)
1024 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
1025 MONO_FINISH_TRY_BLOCKING;
1029 mono_thread_info_suspend_unlock (void)
1031 MONO_SEM_POST (&global_suspend_semaphore);
1035 * This is a very specific function whose only purpose is to
1036 * break a given thread from socket syscalls.
1038 * This only exists because linux won't fail a call to connect
1039 * if the underlying is closed.
1041 * TODO We should cleanup and unify this with the other syscall abort
1045 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1047 MonoThreadHazardPointers *hp;
1048 MonoThreadInfo *info;
1050 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1053 hp = mono_hazard_pointer_get ();
1054 info = mono_thread_info_lookup (tid);
1058 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1059 mono_hazard_pointer_clear (hp, 1);
1063 mono_thread_info_suspend_lock ();
1064 mono_threads_begin_global_suspend ();
1066 mono_threads_core_abort_syscall (info);
1067 mono_threads_wait_pending_operations ();
1069 mono_hazard_pointer_clear (hp, 1);
1071 mono_threads_end_global_suspend ();
1072 mono_thread_info_suspend_unlock ();
1076 mono_thread_info_unified_management_enabled (void)
1078 return unified_suspend_enabled;
1082 * mono_thread_info_set_is_async_context:
1084 * Set whenever the current thread is in an async context. Some runtime functions might behave
1085 * differently while in an async context in order to be async safe.
1088 mono_thread_info_set_is_async_context (gboolean async_context)
1090 MonoThreadInfo *info = mono_thread_info_current ();
1093 info->is_async_context = async_context;
1097 mono_thread_info_is_async_context (void)
1099 MonoThreadInfo *info = mono_thread_info_current ();
1102 return info->is_async_context;
1108 * mono_threads_create_thread:
1110 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1111 * Returns: a windows or io-layer handle for the thread.
1114 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1116 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1120 * mono_thread_info_get_stack_bounds:
1122 * Return the address and size of the current threads stack. Return NULL as the
1123 * stack address if the stack address cannot be determined.
1126 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1128 guint8 *current = (guint8 *)&stsize;
1129 mono_threads_core_get_stack_bounds (staddr, stsize);
1133 /* Sanity check the result */
1134 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1136 /* When running under emacs, sometimes staddr is not aligned to a page size */
1137 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1141 mono_thread_info_yield (void)
1143 return mono_threads_core_yield ();
1145 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1146 static mono_mutex_t sleep_mutex;
1147 static mono_cond_t sleep_cond;
1150 sleep_initialize (void)
1152 mono_mutex_init (&sleep_mutex);
1153 mono_cond_init (&sleep_cond, NULL);
1157 sleep_interrupt (gpointer data)
1159 mono_mutex_lock (&sleep_mutex);
1160 mono_cond_broadcast (&sleep_cond);
1161 mono_mutex_unlock (&sleep_mutex);
1164 static inline guint32
1165 sleep_interruptable (guint32 ms, gboolean *alerted)
1167 guint32 start, now, end;
1169 g_assert (INFINITE == G_MAXUINT32);
1174 start = mono_msec_ticks ();
1176 if (start < G_MAXUINT32 - ms) {
1179 /* start + ms would overflow guint32 */
1183 mono_lazy_initialize (&sleep_init, sleep_initialize);
1185 mono_mutex_lock (&sleep_mutex);
1187 for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
1188 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1190 mono_mutex_unlock (&sleep_mutex);
1191 return WAIT_IO_COMPLETION;
1195 mono_cond_timedwait_ms (&sleep_cond, &sleep_mutex, end - now);
1197 mono_cond_wait (&sleep_cond, &sleep_mutex);
1199 mono_thread_info_uninstall_interrupt (alerted);
1201 mono_mutex_unlock (&sleep_mutex);
1202 return WAIT_IO_COMPLETION;
1206 mono_mutex_unlock (&sleep_mutex);
1212 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1215 MonoThreadInfo *info;
1217 mono_thread_info_yield ();
1219 info = mono_thread_info_current ();
1220 if (info && mono_thread_info_is_interrupt_state (info))
1221 return WAIT_IO_COMPLETION;
1227 return sleep_interruptable (ms, alerted);
1229 if (ms == INFINITE) {
1232 Sleep (G_MAXUINT32);
1234 sleep (G_MAXUINT32);
1239 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1240 struct timespec start, target;
1242 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1243 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1244 g_assert (ret == 0);
1247 target.tv_sec += ms / 1000;
1248 target.tv_nsec += (ms % 1000) * 1000000;
1249 if (target.tv_nsec > 999999999) {
1250 target.tv_nsec -= 999999999;
1255 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1260 struct timespec req, rem;
1262 req.tv_sec = ms / 1000;
1263 req.tv_nsec = (ms % 1000) * 1000000;
1266 memset (&rem, 0, sizeof (rem));
1267 ret = nanosleep (&req, &rem);
1269 #endif /* __linux__ */
1276 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1278 return ((MonoThreadInfo*)info)->tls [key];
1282 * mono_threads_info_tls_set:
1284 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1285 * values of TLS variables for threads other than the current thread.
1286 * This should only be used for infrequently changing TLS variables, and it should
1287 * be paired with setting the real TLS variable since this provides no GC tracking.
1290 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1292 ((MonoThreadInfo*)info)->tls [key] = value;
1296 * mono_thread_info_exit:
1298 * Exit the current thread.
1299 * This function doesn't return.
1302 mono_thread_info_exit (void)
1304 mono_threads_core_exit (0);
1308 * mono_thread_info_open_handle:
1310 * Return a io-layer/win32 handle for the current thread.
1311 * The handle need to be closed by calling CloseHandle () when it is no
1315 mono_thread_info_open_handle (void)
1317 return mono_threads_core_open_handle ();
1321 * mono_threads_open_thread_handle:
1323 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1324 * The handle need to be closed by calling CloseHandle () when it is no
1328 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1330 return mono_threads_core_open_thread_handle (handle, tid);
1334 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1336 mono_threads_core_set_name (tid, name);
1339 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1341 struct _MonoThreadInfoInterruptToken {
1342 void (*callback) (gpointer data);
1347 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1349 * - @callback: must be able to be called from another thread and always cancel the wait
1350 * - @data: passed to the callback
1351 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1352 * if set to TRUE, it must mean that the thread is in interrupted state
1355 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1357 MonoThreadInfo *info;
1358 MonoThreadInfoInterruptToken *previous_token, *token;
1360 g_assert (callback);
1362 g_assert (interrupted);
1363 *interrupted = FALSE;
1365 info = mono_thread_info_current ();
1368 /* The memory of this token can be freed at 2 places:
1369 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1370 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1371 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1372 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1373 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1374 token->callback = callback;
1377 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1379 if (previous_token) {
1380 if (previous_token != INTERRUPT_STATE)
1381 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1385 *interrupted = TRUE;
1388 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1389 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1393 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1395 MonoThreadInfo *info;
1396 MonoThreadInfoInterruptToken *previous_token;
1398 g_assert (interrupted);
1399 *interrupted = FALSE;
1401 info = mono_thread_info_current ();
1404 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1406 /* only the installer can uninstall the token */
1407 g_assert (previous_token);
1409 if (previous_token == INTERRUPT_STATE) {
1410 /* if it is interrupted, then it is going to be freed in finish interrupt */
1411 *interrupted = TRUE;
1413 g_free (previous_token);
1416 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1417 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1420 static MonoThreadInfoInterruptToken*
1421 set_interrupt_state (MonoThreadInfo *info)
1423 MonoThreadInfoInterruptToken *token, *previous_token;
1427 /* Atomically obtain the token the thread is
1428 * waiting on, and change it to a flag value. */
1431 previous_token = info->interrupt_token;
1433 /* Already interrupted */
1434 if (previous_token == INTERRUPT_STATE) {
1439 token = previous_token;
1440 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1446 * mono_thread_info_prepare_interrupt:
1448 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1449 * - if the thread calls one of the WaitFor functions, the function will return with
1450 * WAIT_IO_COMPLETION instead of waiting
1451 * - if the thread was waiting when this function was called, the wait will be broken
1453 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1454 * didn't receive the interrupt signal yet, in this case it should call the wait function
1455 * again. This essentially means that the target thread will busy wait until it is ready to
1456 * process the interruption.
1458 MonoThreadInfoInterruptToken*
1459 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1461 MonoThreadInfoInterruptToken *token;
1463 token = set_interrupt_state (info);
1465 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1466 mono_thread_info_get_tid (info), token);
1472 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1474 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1479 g_assert (token->callback);
1481 token->callback (token->data);
1487 mono_thread_info_self_interrupt (void)
1489 MonoThreadInfo *info;
1490 MonoThreadInfoInterruptToken *token;
1492 info = mono_thread_info_current ();
1495 token = set_interrupt_state (info);
1498 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1499 mono_thread_info_get_tid (info));
1502 /* Clear the interrupted flag of the current thread, set with
1503 * mono_thread_info_self_interrupt, so it can wait again */
1505 mono_thread_info_clear_self_interrupt ()
1507 MonoThreadInfo *info;
1508 MonoThreadInfoInterruptToken *previous_token;
1510 info = mono_thread_info_current ();
1513 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1514 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1516 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1520 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1523 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1527 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1531 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1532 g_string_append_printf (text, "not waiting");
1533 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1534 g_string_append_printf (text, "interrupted state");
1536 g_string_append_printf (text, "waiting");
1539 /* info must be self or be held in a hazard pointer. */
1541 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1543 MonoAsyncJob old_job;
1545 old_job = (MonoAsyncJob) info->service_requests;
1548 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1553 mono_threads_consume_async_jobs (void)
1555 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1558 return (MonoAsyncJob) 0;
1560 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);