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-os-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>
28 #include <mono/utils/mono-coop-mutex.h>
29 #include <mono/utils/mono-coop-semaphore.h>
34 #include <mono/utils/mach-support.h>
38 Mutex that makes sure only a single thread can be suspending others.
39 Suspend is a very racy operation since it requires restarting until
40 the target thread is not on an unsafe region.
42 We could implement this using critical regions, but would be much much
43 harder for an operation that is hardly performance critical.
45 The GC has to acquire this lock before starting a STW to make sure
46 a runtime suspend won't make it wronly see a thread in a safepoint
47 when it is in fact not.
49 static MonoCoopSem global_suspend_semaphore;
51 static size_t thread_info_size;
52 static MonoThreadInfoCallbacks threads_callbacks;
53 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
54 static MonoNativeTlsKey thread_info_key, thread_exited_key;
56 static __thread guint32 tls_small_id MONO_TLS_FAST;
58 static MonoNativeTlsKey small_id_key;
60 static MonoLinkedListSet thread_list;
61 static gboolean mono_threads_inited = FALSE;
63 static MonoSemType suspend_semaphore;
64 static size_t pending_suspends;
65 static gboolean unified_suspend_enabled;
67 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
70 #define SLEEP_DURATION_BEFORE_WARNING (10)
72 #define SLEEP_DURATION_BEFORE_ABORT 200
74 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
77 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
79 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
80 InterlockedIncrement (&abort_posts);
81 mono_os_sem_post (&suspend_semaphore);
85 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
87 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
88 InterlockedIncrement (&suspend_posts);
89 mono_os_sem_post (&suspend_semaphore);
93 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
95 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
96 InterlockedIncrement (&resume_posts);
97 mono_os_sem_post (&suspend_semaphore);
101 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
103 if (mono_threads_is_coop_enabled ()) {
104 /* There's nothing else to do after we async request the thread to suspend */
105 mono_threads_add_to_pending_operation_set (info);
109 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
113 check_async_suspend (MonoThreadInfo *info)
115 if (mono_threads_is_coop_enabled ()) {
116 /* Async suspend can't async fail on coop */
120 return mono_threads_core_check_suspend_result (info);
124 resume_async_suspended (MonoThreadInfo *info)
126 if (mono_threads_is_coop_enabled ())
127 g_assert_not_reached ();
129 g_assert (mono_threads_core_begin_async_resume (info));
133 resume_self_suspended (MonoThreadInfo* info)
135 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
136 mono_os_sem_post (&info->resume_semaphore);
140 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
143 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
144 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
145 g_assert (res != -1);
149 resume_blocking_suspended (MonoThreadInfo* info)
151 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
152 mono_os_sem_post (&info->resume_semaphore);
156 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
158 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
160 InterlockedIncrement (&pending_ops);
164 mono_threads_begin_global_suspend (void)
166 size_t ps = pending_suspends;
167 if (G_UNLIKELY (ps != 0))
168 g_error ("pending_suspends = %d, but must be 0", ps);
169 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,
170 abort_posts, waits_done, pending_ops);
171 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
172 mono_threads_coop_begin_global_suspend ();
176 mono_threads_end_global_suspend (void)
178 size_t ps = pending_suspends;
179 if (G_UNLIKELY (ps != 0))
180 g_error ("pending_suspends = %d, but must be 0", ps);
181 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
182 abort_posts, waits_done, pending_ops);
183 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
184 mono_threads_coop_end_global_suspend ();
190 MonoThreadInfo *info;
191 MonoThreadInfo *cur = mono_thread_info_current ();
193 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
194 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
195 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
196 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
197 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
198 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
199 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
200 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
201 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
202 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
204 FOREACH_THREAD_SAFE (info) {
206 char thread_name [256] = { 0 };
207 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
209 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" : "" );
211 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" : "" );
214 } END_FOREACH_THREAD_SAFE
218 mono_threads_wait_pending_operations (void)
221 int c = pending_suspends;
223 /* Wait threads to park */
224 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
225 if (pending_suspends) {
226 MonoStopwatch suspension_time;
227 mono_stopwatch_start (&suspension_time);
228 for (i = 0; i < pending_suspends; ++i) {
229 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
230 InterlockedIncrement (&waits_done);
231 if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE))
233 mono_stopwatch_stop (&suspension_time);
237 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
238 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);
240 mono_stopwatch_stop (&suspension_time);
241 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
245 pending_suspends = 0;
251 //Thread initialization code
253 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
256 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
259 mono_hazard_pointer_clear (hp, 0);
261 mono_hazard_pointer_clear (hp, 1);
263 mono_hazard_pointer_clear (hp, 2);
267 If return non null Hazard Pointer 1 holds the return value.
270 mono_thread_info_lookup (MonoNativeThreadId id)
272 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
274 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
275 mono_hazard_pointer_clear_all (hp, -1);
279 mono_hazard_pointer_clear_all (hp, 1);
280 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
284 mono_thread_info_insert (MonoThreadInfo *info)
286 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
288 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
289 mono_hazard_pointer_clear_all (hp, -1);
293 mono_hazard_pointer_clear_all (hp, -1);
298 mono_thread_info_remove (MonoThreadInfo *info)
300 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
303 THREADS_DEBUG ("removing info %p\n", info);
304 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
305 mono_hazard_pointer_clear_all (hp, -1);
310 free_thread_info (gpointer mem)
312 MonoThreadInfo *info = (MonoThreadInfo *) mem;
314 mono_os_sem_destroy (&info->resume_semaphore);
315 mono_threads_platform_free (info);
321 mono_thread_info_register_small_id (void)
323 int small_id = mono_thread_small_id_alloc ();
324 #ifdef HAVE_KW_THREAD
325 tls_small_id = small_id;
327 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
333 register_thread (MonoThreadInfo *info, gpointer baseptr)
336 guint8 *staddr = NULL;
337 int small_id = mono_thread_info_register_small_id ();
339 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
340 info->small_id = small_id;
342 mono_os_sem_init (&info->resume_semaphore, 0);
344 /*set TLS early so SMR works */
345 mono_native_tls_set_value (thread_info_key, info);
347 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
349 if (threads_callbacks.thread_register) {
350 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
351 // g_warning ("thread registation failed\n");
357 mono_thread_info_get_stack_bounds (&staddr, &stsize);
360 info->stack_start_limit = staddr;
361 info->stack_end = staddr + stsize;
363 info->stackdata = g_byte_array_new ();
365 mono_threads_platform_register (info);
368 Transition it before taking any locks or publishing itself to reduce the chance
369 of others witnessing a detached thread.
370 We can reasonably expect that until this thread gets published, no other thread will
371 try to manipulate it.
373 mono_threads_transition_attach (info);
374 mono_thread_info_suspend_lock ();
375 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
376 result = mono_thread_info_insert (info);
378 mono_thread_info_suspend_unlock ();
383 unregister_thread (void *arg)
385 MonoThreadInfo *info = (MonoThreadInfo *) arg;
386 int small_id = info->small_id;
389 THREADS_DEBUG ("unregistering info %p\n", info);
391 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
393 mono_threads_core_unregister (info);
396 * TLS destruction order is not reliable so small_id might be cleaned up
399 #ifndef HAVE_KW_THREAD
400 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
404 First perform the callback that requires no locks.
405 This callback has the potential of taking other locks, so we do it before.
406 After it completes, the thread remains functional.
408 if (threads_callbacks.thread_detach)
409 threads_callbacks.thread_detach (info);
411 mono_thread_info_suspend_lock ();
414 Now perform the callback that must be done under locks.
415 This will render the thread useless and non-suspendable, so it must
416 be done while holding the suspend lock to give no other thread chance
419 if (threads_callbacks.thread_unregister)
420 threads_callbacks.thread_unregister (info);
421 mono_threads_unregister_current_thread (info);
422 mono_threads_transition_detach (info);
424 mono_thread_info_suspend_unlock ();
426 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
428 /*now it's safe to free the thread info.*/
429 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
430 mono_thread_small_id_free (small_id);
434 thread_exited_dtor (void *arg)
436 #if defined(__MACH__)
438 * Since we use pthread dtors to clean up thread data, if a thread
439 * is attached to the runtime by another pthread dtor after our dtor
440 * has ran, it will never be detached, leading to various problems
441 * since the thread ids etc. will be reused while they are still in
442 * the threads hashtables etc.
443 * Dtors are called in a loop until all user tls entries are 0,
444 * but the loop has a maximum count (4), so if we set the tls
445 * variable every time, it will remain set when system tls dtors
446 * are ran. This allows mono_thread_info_is_exiting () to detect
447 * whenever the thread is exiting, even if it is executed from a
448 * system tls dtor (i.e. obj-c dealloc methods).
450 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
455 * Removes the current thread from the thread list.
456 * This must be called from the thread unregister callback and nowhere else.
457 * The current thread must be passed as TLS might have already been cleaned up.
460 mono_threads_unregister_current_thread (MonoThreadInfo *info)
463 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
464 result = mono_thread_info_remove (info);
469 mono_thread_info_current_unchecked (void)
471 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
476 mono_thread_info_current (void)
478 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
482 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
485 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
486 The way to distinguish between before, during and after cleanup is the following:
488 -If the TLS key is set, cleanup has not begun;
489 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
490 -If the thread is nowhere to be found, cleanup has finished.
492 We cannot function after cleanup since there's no way to ensure what will happen.
496 /*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 */
497 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
503 mono_thread_info_get_small_id (void)
505 #ifdef HAVE_KW_THREAD
508 gpointer val = mono_native_tls_get_value (small_id_key);
511 return GPOINTER_TO_INT (val) - 1;
516 mono_thread_info_list_head (void)
522 * mono_threads_attach_tools_thread
524 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
526 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
527 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
530 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
531 * doing things like resolving backtraces in their background processing thread.
534 mono_threads_attach_tools_thread (void)
537 MonoThreadInfo *info;
539 /* Must only be called once */
540 g_assert (!mono_native_tls_get_value (thread_info_key));
542 while (!mono_threads_inited) {
546 info = mono_thread_info_attach (&dummy);
549 info->tools_thread = TRUE;
553 mono_thread_info_attach (void *baseptr)
555 MonoThreadInfo *info;
556 if (!mono_threads_inited)
559 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
560 * thread is created before an embedding API user initialized Mono. */
561 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
564 g_assert (mono_threads_inited);
567 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
569 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
570 THREADS_DEBUG ("attaching %p\n", info);
571 if (!register_thread (info, baseptr))
573 } else if (threads_callbacks.thread_attach) {
574 threads_callbacks.thread_attach (info);
580 mono_thread_info_detach (void)
582 MonoThreadInfo *info;
583 if (!mono_threads_inited)
585 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
586 * is created before an embedding API user initialized Mono. */
587 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
590 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
592 THREADS_DEBUG ("detaching %p\n", info);
593 unregister_thread (info);
594 mono_native_tls_set_value (thread_info_key, NULL);
599 * mono_thread_info_is_exiting:
601 * Return whenever the current thread is exiting, i.e. it is running pthread
605 mono_thread_info_is_exiting (void)
607 #if defined(__MACH__)
608 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
615 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
618 threads_callbacks = *callbacks;
619 thread_info_size = info_size;
621 res = mono_native_tls_alloc (&thread_info_key, NULL);
622 res = mono_native_tls_alloc (&thread_exited_key, NULL);
624 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
625 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
630 #ifndef HAVE_KW_THREAD
631 res = mono_native_tls_alloc (&small_id_key, NULL);
635 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
637 mono_coop_sem_init (&global_suspend_semaphore, 1);
638 mono_os_sem_init (&suspend_semaphore, 0);
640 mono_lls_init (&thread_list, NULL);
641 mono_thread_smr_init ();
642 mono_threads_init_platform ();
643 mono_threads_init_coop ();
644 mono_threads_init_abort_syscall ();
646 #if defined(__MACH__)
647 mono_mach_init (thread_info_key);
650 mono_threads_inited = TRUE;
652 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
656 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
658 runtime_callbacks = *callbacks;
661 MonoThreadInfoRuntimeCallbacks *
662 mono_threads_get_runtime_callbacks (void)
664 return &runtime_callbacks;
668 The return value is only valid until a matching mono_thread_info_resume is called
670 static MonoThreadInfo*
671 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
673 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
674 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
676 *error_condition = "Thread not found";
680 switch (mono_threads_transition_request_async_suspension (info)) {
681 case AsyncSuspendAlreadySuspended:
682 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
684 case AsyncSuspendWait:
685 mono_threads_add_to_pending_operation_set (info);
687 case AsyncSuspendInitSuspend:
688 if (!begin_async_suspend (info, interrupt_kernel)) {
689 mono_hazard_pointer_clear (hp, 1);
690 *error_condition = "Could not suspend thread";
695 //Wait for the pending suspend to finish
696 mono_threads_wait_pending_operations ();
698 if (!check_async_suspend (info)) {
699 mono_hazard_pointer_clear (hp, 1);
700 *error_condition = "Post suspend failed";
707 Signal that the current thread wants to be suspended.
708 This function can be called without holding the suspend lock held.
709 To finish suspending, call mono_suspend_check.
712 mono_thread_info_begin_self_suspend (void)
714 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
718 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
719 mono_threads_transition_request_self_suspension (info);
723 mono_thread_info_end_self_suspend (void)
725 MonoThreadInfo *info;
727 info = mono_thread_info_current ();
730 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
732 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
734 /* commit the saved state and notify others if needed */
735 switch (mono_threads_transition_state_poll (info)) {
736 case SelfSuspendResumed:
738 case SelfSuspendWait:
739 mono_thread_info_wait_for_resume (info);
741 case SelfSuspendNotifyAndWait:
742 mono_threads_notify_initiator_of_suspend (info);
743 mono_thread_info_wait_for_resume (info);
744 mono_threads_notify_initiator_of_resume (info);
750 mono_thread_info_core_resume (MonoThreadInfo *info)
752 gboolean res = FALSE;
753 if (info->create_suspended) {
754 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
755 /* 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 */
756 info->create_suspended = FALSE;
757 mono_threads_core_resume_created (info, tid);
761 switch (mono_threads_transition_request_resume (info)) {
768 case ResumeInitSelfResume:
769 resume_self_suspended (info);
772 case ResumeInitAsyncResume:
773 resume_async_suspended (info);
776 case ResumeInitBlockingResume:
777 resume_blocking_suspended (info);
786 mono_thread_info_resume (MonoNativeThreadId tid)
788 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
789 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
790 MonoThreadInfo *info;
792 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
794 mono_thread_info_suspend_lock ();
796 info = mono_thread_info_lookup (tid); /*info on HP1*/
802 result = mono_thread_info_core_resume (info);
804 //Wait for the pending resume to finish
805 mono_threads_wait_pending_operations ();
808 mono_thread_info_suspend_unlock ();
809 mono_hazard_pointer_clear (hp, 1);
814 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
816 switch (mono_threads_transition_request_async_suspension (info)) {
817 case AsyncSuspendAlreadySuspended:
819 case AsyncSuspendWait:
820 mono_threads_add_to_pending_operation_set (info);
822 case AsyncSuspendInitSuspend:
823 return begin_async_suspend (info, interrupt_kernel);
825 g_assert_not_reached ();
830 mono_thread_info_begin_resume (MonoThreadInfo *info)
832 return mono_thread_info_core_resume (info);
836 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
838 return check_async_suspend (info);
842 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
843 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
846 is_thread_in_critical_region (MonoThreadInfo *info)
850 gpointer stack_start;
851 MonoThreadUnwindState *state;
853 /* Are we inside a system critical region? */
854 if (info->inside_critical_region)
857 /* Are we inside a GC critical region? */
858 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
862 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
863 state = mono_thread_info_get_suspend_state (info);
864 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
867 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
868 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
869 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
872 ji = mono_jit_info_table_find (
873 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
874 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
879 method = mono_jit_info_get_method (ji);
881 return threads_callbacks.mono_method_is_critical (method);
885 mono_thread_info_in_critical_location (MonoThreadInfo *info)
887 return is_thread_in_critical_region (info);
890 static MonoThreadInfo*
891 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
893 MonoThreadInfo *info = NULL;
894 int sleep_duration = 0;
896 const char *suspend_error = "Unknown error";
897 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
898 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
902 /*WARNING: We now are in interrupt context until we resume the thread. */
903 if (!is_thread_in_critical_region (info))
906 if (!mono_thread_info_core_resume (info)) {
907 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
910 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
912 /* Wait for the pending resume to finish */
913 mono_threads_wait_pending_operations ();
915 if (!sleep_duration) {
923 g_usleep (sleep_duration);
925 sleep_duration += 10;
931 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
934 MonoThreadInfo *info = NULL;
935 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
937 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
938 /*FIXME: unify this with self-suspend*/
939 g_assert (id != mono_native_thread_id_get ());
941 /* This can block during stw */
942 mono_thread_info_suspend_lock ();
943 mono_threads_begin_global_suspend ();
945 info = suspend_sync_nolock (id, interrupt_kernel);
949 switch (result = callback (info, user_data)) {
950 case MonoResumeThread:
951 mono_hazard_pointer_set (hp, 1, info);
952 mono_thread_info_core_resume (info);
953 mono_threads_wait_pending_operations ();
958 g_error ("Invalid suspend_and_run callback return value %d", result);
962 mono_hazard_pointer_clear (hp, 1);
963 mono_threads_end_global_suspend ();
964 mono_thread_info_suspend_unlock ();
969 If we are trying to suspend a target that is on a critical region
970 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
971 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
973 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
975 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
978 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
980 MonoThreadInfo *info = NULL;
982 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
983 /*FIXME: unify this with self-suspend*/
984 g_assert (id != mono_native_thread_id_get ());
986 mono_thread_info_suspend_lock ();
987 mono_threads_begin_global_suspend ();
989 info = suspend_sync_nolock (id, interrupt_kernel);
991 /* XXX this clears HP 1, so we restated it again */
992 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
993 mono_threads_end_global_suspend ();
994 mono_thread_info_suspend_unlock ();
1000 Inject an assynchronous call into the target thread. The target thread must be suspended and
1001 only a single async call can be setup for a given suspend cycle.
1002 This async call must cause stack unwinding as the current implementation doesn't save enough state
1003 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1004 currently used only to deliver exceptions.
1007 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1009 /* An async call can only be setup on an async suspended thread */
1010 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1011 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1012 g_assert (!info->async_target);
1013 info->async_target = target_func;
1014 /* This is not GC tracked */
1015 info->user_data = user_data;
1019 The suspend lock is held during any suspend in progress.
1020 A GC that has safepoints must take this lock as part of its
1021 STW to make sure no unsafe pending suspend is in progress.
1024 mono_thread_info_suspend_lock (void)
1026 int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1027 g_assert (res != -1);
1031 mono_thread_info_suspend_unlock (void)
1033 mono_coop_sem_post (&global_suspend_semaphore);
1037 * This is a very specific function whose only purpose is to
1038 * break a given thread from socket syscalls.
1040 * This only exists because linux won't fail a call to connect
1041 * if the underlying is closed.
1043 * TODO We should cleanup and unify this with the other syscall abort
1047 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1049 MonoThreadHazardPointers *hp;
1050 MonoThreadInfo *info;
1052 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1055 hp = mono_hazard_pointer_get ();
1056 info = mono_thread_info_lookup (tid);
1060 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1061 mono_hazard_pointer_clear (hp, 1);
1065 mono_thread_info_suspend_lock ();
1066 mono_threads_begin_global_suspend ();
1068 mono_threads_core_abort_syscall (info);
1069 mono_threads_wait_pending_operations ();
1071 mono_hazard_pointer_clear (hp, 1);
1073 mono_threads_end_global_suspend ();
1074 mono_thread_info_suspend_unlock ();
1078 mono_thread_info_unified_management_enabled (void)
1080 return unified_suspend_enabled;
1084 * mono_thread_info_set_is_async_context:
1086 * Set whenever the current thread is in an async context. Some runtime functions might behave
1087 * differently while in an async context in order to be async safe.
1090 mono_thread_info_set_is_async_context (gboolean async_context)
1092 MonoThreadInfo *info = mono_thread_info_current ();
1095 info->is_async_context = async_context;
1099 mono_thread_info_is_async_context (void)
1101 MonoThreadInfo *info = mono_thread_info_current ();
1104 return info->is_async_context;
1110 * mono_threads_create_thread:
1112 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1113 * Returns: a windows or io-layer handle for the thread.
1116 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1118 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1122 * mono_thread_info_get_stack_bounds:
1124 * Return the address and size of the current threads stack. Return NULL as the
1125 * stack address if the stack address cannot be determined.
1128 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1130 guint8 *current = (guint8 *)&stsize;
1131 mono_threads_core_get_stack_bounds (staddr, stsize);
1135 /* Sanity check the result */
1136 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1138 /* When running under emacs, sometimes staddr is not aligned to a page size */
1139 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1143 mono_thread_info_yield (void)
1145 return mono_threads_core_yield ();
1147 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1148 static MonoCoopMutex sleep_mutex;
1149 static MonoCoopCond sleep_cond;
1152 sleep_initialize (void)
1154 mono_coop_mutex_init (&sleep_mutex);
1155 mono_coop_cond_init (&sleep_cond);
1159 sleep_interrupt (gpointer data)
1161 mono_coop_mutex_lock (&sleep_mutex);
1162 mono_coop_cond_broadcast (&sleep_cond);
1163 mono_coop_mutex_unlock (&sleep_mutex);
1166 static inline guint32
1167 sleep_interruptable (guint32 ms, gboolean *alerted)
1169 guint32 start, now, end;
1171 g_assert (INFINITE == G_MAXUINT32);
1176 start = mono_msec_ticks ();
1178 if (start < G_MAXUINT32 - ms) {
1181 /* start + ms would overflow guint32 */
1185 mono_lazy_initialize (&sleep_init, sleep_initialize);
1187 mono_coop_mutex_lock (&sleep_mutex);
1189 for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
1190 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1192 mono_coop_mutex_unlock (&sleep_mutex);
1193 return WAIT_IO_COMPLETION;
1197 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1199 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1201 mono_thread_info_uninstall_interrupt (alerted);
1203 mono_coop_mutex_unlock (&sleep_mutex);
1204 return WAIT_IO_COMPLETION;
1208 mono_coop_mutex_unlock (&sleep_mutex);
1214 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1217 MonoThreadInfo *info;
1219 mono_thread_info_yield ();
1221 info = mono_thread_info_current ();
1222 if (info && mono_thread_info_is_interrupt_state (info))
1223 return WAIT_IO_COMPLETION;
1229 return sleep_interruptable (ms, alerted);
1231 MONO_PREPARE_BLOCKING;
1233 if (ms == INFINITE) {
1236 Sleep (G_MAXUINT32);
1238 sleep (G_MAXUINT32);
1243 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1244 struct timespec start, target;
1246 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1247 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1248 g_assert (ret == 0);
1251 target.tv_sec += ms / 1000;
1252 target.tv_nsec += (ms % 1000) * 1000000;
1253 if (target.tv_nsec > 999999999) {
1254 target.tv_nsec -= 999999999;
1259 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1264 struct timespec req, rem;
1266 req.tv_sec = ms / 1000;
1267 req.tv_nsec = (ms % 1000) * 1000000;
1270 memset (&rem, 0, sizeof (rem));
1271 ret = nanosleep (&req, &rem);
1273 #endif /* __linux__ */
1276 MONO_FINISH_BLOCKING;
1282 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1284 return ((MonoThreadInfo*)info)->tls [key];
1288 * mono_threads_info_tls_set:
1290 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1291 * values of TLS variables for threads other than the current thread.
1292 * This should only be used for infrequently changing TLS variables, and it should
1293 * be paired with setting the real TLS variable since this provides no GC tracking.
1296 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1298 ((MonoThreadInfo*)info)->tls [key] = value;
1302 * mono_thread_info_exit:
1304 * Exit the current thread.
1305 * This function doesn't return.
1308 mono_thread_info_exit (void)
1310 mono_threads_core_exit (0);
1314 * mono_thread_info_open_handle:
1316 * Return a io-layer/win32 handle for the current thread.
1317 * The handle need to be closed by calling CloseHandle () when it is no
1321 mono_thread_info_open_handle (void)
1323 return mono_threads_core_open_handle ();
1327 * mono_threads_open_thread_handle:
1329 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1330 * The handle need to be closed by calling CloseHandle () when it is no
1334 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1336 return mono_threads_core_open_thread_handle (handle, tid);
1340 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1342 mono_threads_core_set_name (tid, name);
1345 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1347 struct _MonoThreadInfoInterruptToken {
1348 void (*callback) (gpointer data);
1353 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1355 * - @callback: must be able to be called from another thread and always cancel the wait
1356 * - @data: passed to the callback
1357 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1358 * if set to TRUE, it must mean that the thread is in interrupted state
1361 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1363 MonoThreadInfo *info;
1364 MonoThreadInfoInterruptToken *previous_token, *token;
1366 g_assert (callback);
1368 g_assert (interrupted);
1369 *interrupted = FALSE;
1371 info = mono_thread_info_current ();
1374 /* The memory of this token can be freed at 2 places:
1375 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1376 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1377 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1378 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1379 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1380 token->callback = callback;
1383 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1385 if (previous_token) {
1386 if (previous_token != INTERRUPT_STATE)
1387 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1391 *interrupted = TRUE;
1394 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1395 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1399 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1401 MonoThreadInfo *info;
1402 MonoThreadInfoInterruptToken *previous_token;
1404 g_assert (interrupted);
1405 *interrupted = FALSE;
1407 info = mono_thread_info_current ();
1410 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1412 /* only the installer can uninstall the token */
1413 g_assert (previous_token);
1415 if (previous_token == INTERRUPT_STATE) {
1416 /* if it is interrupted, then it is going to be freed in finish interrupt */
1417 *interrupted = TRUE;
1419 g_free (previous_token);
1422 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1423 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1426 static MonoThreadInfoInterruptToken*
1427 set_interrupt_state (MonoThreadInfo *info)
1429 MonoThreadInfoInterruptToken *token, *previous_token;
1433 /* Atomically obtain the token the thread is
1434 * waiting on, and change it to a flag value. */
1437 previous_token = info->interrupt_token;
1439 /* Already interrupted */
1440 if (previous_token == INTERRUPT_STATE) {
1445 token = previous_token;
1446 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1452 * mono_thread_info_prepare_interrupt:
1454 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1455 * - if the thread calls one of the WaitFor functions, the function will return with
1456 * WAIT_IO_COMPLETION instead of waiting
1457 * - if the thread was waiting when this function was called, the wait will be broken
1459 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1460 * didn't receive the interrupt signal yet, in this case it should call the wait function
1461 * again. This essentially means that the target thread will busy wait until it is ready to
1462 * process the interruption.
1464 MonoThreadInfoInterruptToken*
1465 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1467 MonoThreadInfoInterruptToken *token;
1469 token = set_interrupt_state (info);
1471 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1472 mono_thread_info_get_tid (info), token);
1478 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1480 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1485 g_assert (token->callback);
1487 token->callback (token->data);
1493 mono_thread_info_self_interrupt (void)
1495 MonoThreadInfo *info;
1496 MonoThreadInfoInterruptToken *token;
1498 info = mono_thread_info_current ();
1501 token = set_interrupt_state (info);
1504 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1505 mono_thread_info_get_tid (info));
1508 /* Clear the interrupted flag of the current thread, set with
1509 * mono_thread_info_self_interrupt, so it can wait again */
1511 mono_thread_info_clear_self_interrupt ()
1513 MonoThreadInfo *info;
1514 MonoThreadInfoInterruptToken *previous_token;
1516 info = mono_thread_info_current ();
1519 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1520 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1522 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1526 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1529 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1533 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1537 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1538 g_string_append_printf (text, "not waiting");
1539 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1540 g_string_append_printf (text, "interrupted state");
1542 g_string_append_printf (text, "waiting");
1545 /* info must be self or be held in a hazard pointer. */
1547 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1549 MonoAsyncJob old_job;
1551 old_job = (MonoAsyncJob) info->service_requests;
1554 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1559 mono_threads_consume_async_jobs (void)
1561 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1564 return (MonoAsyncJob) 0;
1566 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);