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 long sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
75 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
77 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
80 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
82 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
83 InterlockedIncrement (&abort_posts);
84 mono_os_sem_post (&suspend_semaphore);
88 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
90 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
91 InterlockedIncrement (&suspend_posts);
92 mono_os_sem_post (&suspend_semaphore);
96 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
98 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
99 InterlockedIncrement (&resume_posts);
100 mono_os_sem_post (&suspend_semaphore);
104 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
106 if (mono_threads_is_coop_enabled ()) {
107 /* There's nothing else to do after we async request the thread to suspend */
108 mono_threads_add_to_pending_operation_set (info);
112 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
116 check_async_suspend (MonoThreadInfo *info)
118 if (mono_threads_is_coop_enabled ()) {
119 /* Async suspend can't async fail on coop */
123 return mono_threads_core_check_suspend_result (info);
127 resume_async_suspended (MonoThreadInfo *info)
129 if (mono_threads_is_coop_enabled ())
130 g_assert_not_reached ();
132 g_assert (mono_threads_core_begin_async_resume (info));
136 resume_self_suspended (MonoThreadInfo* info)
138 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
139 mono_os_sem_post (&info->resume_semaphore);
143 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
146 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
147 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
148 g_assert (res != -1);
152 resume_blocking_suspended (MonoThreadInfo* info)
154 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
155 mono_os_sem_post (&info->resume_semaphore);
159 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
161 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
163 InterlockedIncrement (&pending_ops);
167 mono_threads_begin_global_suspend (void)
169 size_t ps = pending_suspends;
170 if (G_UNLIKELY (ps != 0))
171 g_error ("pending_suspends = %d, but must be 0", ps);
172 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,
173 abort_posts, waits_done, pending_ops);
174 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
175 mono_threads_coop_begin_global_suspend ();
179 mono_threads_end_global_suspend (void)
181 size_t ps = pending_suspends;
182 if (G_UNLIKELY (ps != 0))
183 g_error ("pending_suspends = %d, but must be 0", ps);
184 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
185 abort_posts, waits_done, pending_ops);
186 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
187 mono_threads_coop_end_global_suspend ();
193 MonoThreadInfo *info;
194 MonoThreadInfo *cur = mono_thread_info_current ();
196 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
197 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
198 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
199 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
200 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
201 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
202 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
203 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
204 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
205 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
207 FOREACH_THREAD_SAFE (info) {
209 char thread_name [256] = { 0 };
210 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
212 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" : "" );
214 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" : "" );
217 } END_FOREACH_THREAD_SAFE
221 mono_threads_wait_pending_operations (void)
224 int c = pending_suspends;
226 /* Wait threads to park */
227 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
228 if (pending_suspends) {
229 MonoStopwatch suspension_time;
230 mono_stopwatch_start (&suspension_time);
231 for (i = 0; i < pending_suspends; ++i) {
232 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
233 InterlockedIncrement (&waits_done);
234 if (!mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE))
236 mono_stopwatch_stop (&suspension_time);
240 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
241 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
243 mono_stopwatch_stop (&suspension_time);
244 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
248 pending_suspends = 0;
254 //Thread initialization code
256 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
259 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
262 mono_hazard_pointer_clear (hp, 0);
264 mono_hazard_pointer_clear (hp, 1);
266 mono_hazard_pointer_clear (hp, 2);
270 If return non null Hazard Pointer 1 holds the return value.
273 mono_thread_info_lookup (MonoNativeThreadId id)
275 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
277 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
278 mono_hazard_pointer_clear_all (hp, -1);
282 mono_hazard_pointer_clear_all (hp, 1);
283 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
287 mono_thread_info_insert (MonoThreadInfo *info)
289 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
291 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
292 mono_hazard_pointer_clear_all (hp, -1);
296 mono_hazard_pointer_clear_all (hp, -1);
301 mono_thread_info_remove (MonoThreadInfo *info)
303 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
306 THREADS_DEBUG ("removing info %p\n", info);
307 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
308 mono_hazard_pointer_clear_all (hp, -1);
313 free_thread_info (gpointer mem)
315 MonoThreadInfo *info = (MonoThreadInfo *) mem;
317 mono_os_sem_destroy (&info->resume_semaphore);
318 mono_threads_platform_free (info);
324 mono_thread_info_register_small_id (void)
326 int small_id = mono_thread_small_id_alloc ();
327 #ifdef HAVE_KW_THREAD
328 tls_small_id = small_id;
330 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
336 register_thread (MonoThreadInfo *info, gpointer baseptr)
339 guint8 *staddr = NULL;
340 int small_id = mono_thread_info_register_small_id ();
342 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
343 info->small_id = small_id;
345 mono_os_sem_init (&info->resume_semaphore, 0);
347 /*set TLS early so SMR works */
348 mono_native_tls_set_value (thread_info_key, info);
350 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
352 if (threads_callbacks.thread_register) {
353 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
354 // g_warning ("thread registation failed\n");
360 mono_thread_info_get_stack_bounds (&staddr, &stsize);
363 info->stack_start_limit = staddr;
364 info->stack_end = staddr + stsize;
366 info->stackdata = g_byte_array_new ();
368 mono_threads_platform_register (info);
371 Transition it before taking any locks or publishing itself to reduce the chance
372 of others witnessing a detached thread.
373 We can reasonably expect that until this thread gets published, no other thread will
374 try to manipulate it.
376 mono_threads_transition_attach (info);
377 mono_thread_info_suspend_lock ();
378 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
379 result = mono_thread_info_insert (info);
381 mono_thread_info_suspend_unlock ();
386 unregister_thread (void *arg)
388 MonoThreadInfo *info = (MonoThreadInfo *) arg;
389 int small_id = info->small_id;
392 THREADS_DEBUG ("unregistering info %p\n", info);
394 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
396 mono_threads_core_unregister (info);
399 * TLS destruction order is not reliable so small_id might be cleaned up
402 #ifndef HAVE_KW_THREAD
403 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
407 First perform the callback that requires no locks.
408 This callback has the potential of taking other locks, so we do it before.
409 After it completes, the thread remains functional.
411 if (threads_callbacks.thread_detach)
412 threads_callbacks.thread_detach (info);
414 mono_thread_info_suspend_lock ();
417 Now perform the callback that must be done under locks.
418 This will render the thread useless and non-suspendable, so it must
419 be done while holding the suspend lock to give no other thread chance
422 if (threads_callbacks.thread_unregister)
423 threads_callbacks.thread_unregister (info);
424 mono_threads_unregister_current_thread (info);
425 mono_threads_transition_detach (info);
427 mono_thread_info_suspend_unlock ();
429 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
431 /*now it's safe to free the thread info.*/
432 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
433 mono_thread_small_id_free (small_id);
437 thread_exited_dtor (void *arg)
439 #if defined(__MACH__)
441 * Since we use pthread dtors to clean up thread data, if a thread
442 * is attached to the runtime by another pthread dtor after our dtor
443 * has ran, it will never be detached, leading to various problems
444 * since the thread ids etc. will be reused while they are still in
445 * the threads hashtables etc.
446 * Dtors are called in a loop until all user tls entries are 0,
447 * but the loop has a maximum count (4), so if we set the tls
448 * variable every time, it will remain set when system tls dtors
449 * are ran. This allows mono_thread_info_is_exiting () to detect
450 * whenever the thread is exiting, even if it is executed from a
451 * system tls dtor (i.e. obj-c dealloc methods).
453 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
458 * Removes the current thread from the thread list.
459 * This must be called from the thread unregister callback and nowhere else.
460 * The current thread must be passed as TLS might have already been cleaned up.
463 mono_threads_unregister_current_thread (MonoThreadInfo *info)
466 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
467 result = mono_thread_info_remove (info);
472 mono_thread_info_current_unchecked (void)
474 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
479 mono_thread_info_current (void)
481 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
485 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
488 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
489 The way to distinguish between before, during and after cleanup is the following:
491 -If the TLS key is set, cleanup has not begun;
492 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
493 -If the thread is nowhere to be found, cleanup has finished.
495 We cannot function after cleanup since there's no way to ensure what will happen.
499 /*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 */
500 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
506 mono_thread_info_get_small_id (void)
508 #ifdef HAVE_KW_THREAD
511 gpointer val = mono_native_tls_get_value (small_id_key);
514 return GPOINTER_TO_INT (val) - 1;
519 mono_thread_info_list_head (void)
525 * mono_threads_attach_tools_thread
527 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
529 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
530 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
533 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
534 * doing things like resolving backtraces in their background processing thread.
537 mono_threads_attach_tools_thread (void)
540 MonoThreadInfo *info;
542 /* Must only be called once */
543 g_assert (!mono_native_tls_get_value (thread_info_key));
545 while (!mono_threads_inited) {
546 mono_thread_info_usleep (10);
549 info = mono_thread_info_attach (&dummy);
552 info->tools_thread = TRUE;
556 mono_thread_info_attach (void *baseptr)
558 MonoThreadInfo *info;
559 if (!mono_threads_inited)
562 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
563 * thread is created before an embedding API user initialized Mono. */
564 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
567 g_assert (mono_threads_inited);
570 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
572 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
573 THREADS_DEBUG ("attaching %p\n", info);
574 if (!register_thread (info, baseptr))
576 } else if (threads_callbacks.thread_attach) {
577 threads_callbacks.thread_attach (info);
583 mono_thread_info_detach (void)
585 MonoThreadInfo *info;
586 if (!mono_threads_inited)
588 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
589 * is created before an embedding API user initialized Mono. */
590 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
593 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
595 THREADS_DEBUG ("detaching %p\n", info);
596 unregister_thread (info);
597 mono_native_tls_set_value (thread_info_key, NULL);
602 * mono_thread_info_is_exiting:
604 * Return whenever the current thread is exiting, i.e. it is running pthread
608 mono_thread_info_is_exiting (void)
610 #if defined(__MACH__)
611 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
618 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
621 threads_callbacks = *callbacks;
622 thread_info_size = info_size;
625 res = mono_native_tls_alloc (&thread_info_key, NULL);
626 res = mono_native_tls_alloc (&thread_exited_key, NULL);
628 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
629 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
634 #ifndef HAVE_KW_THREAD
635 res = mono_native_tls_alloc (&small_id_key, NULL);
639 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
641 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
642 long threshold = strtol(sleepLimit, NULL, 10);
643 if ((errno == 0) && (threshold >= 40)) {
644 sleepAbortDuration = threshold;
645 sleepWarnDuration = threshold / 20;
647 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
650 mono_coop_sem_init (&global_suspend_semaphore, 1);
651 mono_os_sem_init (&suspend_semaphore, 0);
653 mono_lls_init (&thread_list, NULL);
654 mono_thread_smr_init ();
655 mono_threads_init_platform ();
656 mono_threads_init_coop ();
657 mono_threads_init_abort_syscall ();
659 #if defined(__MACH__)
660 mono_mach_init (thread_info_key);
663 mono_threads_inited = TRUE;
665 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
669 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
671 runtime_callbacks = *callbacks;
674 MonoThreadInfoRuntimeCallbacks *
675 mono_threads_get_runtime_callbacks (void)
677 return &runtime_callbacks;
681 Signal that the current thread wants to be suspended.
682 This function can be called without holding the suspend lock held.
683 To finish suspending, call mono_suspend_check.
686 mono_thread_info_begin_self_suspend (void)
688 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
692 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
693 mono_threads_transition_request_self_suspension (info);
697 mono_thread_info_end_self_suspend (void)
699 MonoThreadInfo *info;
701 info = mono_thread_info_current ();
704 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
706 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
708 /* commit the saved state and notify others if needed */
709 switch (mono_threads_transition_state_poll (info)) {
710 case SelfSuspendResumed:
712 case SelfSuspendWait:
713 mono_thread_info_wait_for_resume (info);
715 case SelfSuspendNotifyAndWait:
716 mono_threads_notify_initiator_of_suspend (info);
717 mono_thread_info_wait_for_resume (info);
718 mono_threads_notify_initiator_of_resume (info);
724 mono_thread_info_core_resume (MonoThreadInfo *info)
726 gboolean res = FALSE;
727 if (info->create_suspended) {
728 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
729 /* 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 */
730 info->create_suspended = FALSE;
731 mono_threads_core_resume_created (info, tid);
735 switch (mono_threads_transition_request_resume (info)) {
742 case ResumeInitSelfResume:
743 resume_self_suspended (info);
746 case ResumeInitAsyncResume:
747 resume_async_suspended (info);
750 case ResumeInitBlockingResume:
751 resume_blocking_suspended (info);
760 mono_thread_info_resume (MonoNativeThreadId tid)
762 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
763 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
764 MonoThreadInfo *info;
766 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
768 mono_thread_info_suspend_lock ();
770 info = mono_thread_info_lookup (tid); /*info on HP1*/
776 result = mono_thread_info_core_resume (info);
778 //Wait for the pending resume to finish
779 mono_threads_wait_pending_operations ();
782 mono_thread_info_suspend_unlock ();
783 mono_hazard_pointer_clear (hp, 1);
788 mono_thread_info_begin_suspend (MonoThreadInfo *info)
790 switch (mono_threads_transition_request_async_suspension (info)) {
791 case AsyncSuspendAlreadySuspended:
792 case AsyncSuspendBlocking:
794 case AsyncSuspendWait:
795 mono_threads_add_to_pending_operation_set (info);
797 case AsyncSuspendInitSuspend:
798 return begin_async_suspend (info, FALSE);
800 g_assert_not_reached ();
805 mono_thread_info_begin_resume (MonoThreadInfo *info)
807 return mono_thread_info_core_resume (info);
811 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
813 return check_async_suspend (info);
817 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
818 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
821 is_thread_in_critical_region (MonoThreadInfo *info)
825 gpointer stack_start;
826 MonoThreadUnwindState *state;
828 /* Are we inside a system critical region? */
829 if (info->inside_critical_region)
832 /* Are we inside a GC critical region? */
833 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
837 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
838 state = mono_thread_info_get_suspend_state (info);
839 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
842 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
843 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
844 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
847 ji = mono_jit_info_table_find (
848 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
849 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
854 method = mono_jit_info_get_method (ji);
856 return threads_callbacks.mono_method_is_critical (method);
860 mono_thread_info_in_critical_location (MonoThreadInfo *info)
862 return is_thread_in_critical_region (info);
866 The return value is only valid until a matching mono_thread_info_resume is called
868 static MonoThreadInfo*
869 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
871 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
872 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
876 switch (mono_threads_transition_request_async_suspension (info)) {
877 case AsyncSuspendAlreadySuspended:
878 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
880 case AsyncSuspendWait:
881 mono_threads_add_to_pending_operation_set (info);
883 case AsyncSuspendInitSuspend:
884 if (!begin_async_suspend (info, interrupt_kernel)) {
885 mono_hazard_pointer_clear (hp, 1);
889 case AsyncSuspendBlocking:
890 if (interrupt_kernel)
891 mono_threads_core_abort_syscall (info);
895 g_assert_not_reached ();
898 //Wait for the pending suspend to finish
899 mono_threads_wait_pending_operations ();
901 if (!check_async_suspend (info)) {
902 mono_hazard_pointer_clear (hp, 1);
908 static MonoThreadInfo*
909 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
911 MonoThreadInfo *info = NULL;
912 int sleep_duration = 0;
914 if (!(info = suspend_sync (id, interrupt_kernel))) {
915 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
919 /*WARNING: We now are in interrupt context until we resume the thread. */
920 if (!is_thread_in_critical_region (info))
923 if (!mono_thread_info_core_resume (info)) {
924 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
927 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
929 /* Wait for the pending resume to finish */
930 mono_threads_wait_pending_operations ();
932 if (sleep_duration == 0)
933 mono_thread_info_yield ();
935 g_usleep (sleep_duration);
937 sleep_duration += 10;
943 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
946 MonoThreadInfo *info = NULL;
947 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
949 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
950 /*FIXME: unify this with self-suspend*/
951 g_assert (id != mono_native_thread_id_get ());
953 /* This can block during stw */
954 mono_thread_info_suspend_lock ();
955 mono_threads_begin_global_suspend ();
957 info = suspend_sync_nolock (id, interrupt_kernel);
961 switch (result = callback (info, user_data)) {
962 case MonoResumeThread:
963 mono_hazard_pointer_set (hp, 1, info);
964 mono_thread_info_core_resume (info);
965 mono_threads_wait_pending_operations ();
970 g_error ("Invalid suspend_and_run callback return value %d", result);
974 mono_hazard_pointer_clear (hp, 1);
975 mono_threads_end_global_suspend ();
976 mono_thread_info_suspend_unlock ();
980 Inject an assynchronous call into the target thread. The target thread must be suspended and
981 only a single async call can be setup for a given suspend cycle.
982 This async call must cause stack unwinding as the current implementation doesn't save enough state
983 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
984 currently used only to deliver exceptions.
987 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
989 /* An async call can only be setup on an async suspended thread */
990 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
991 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
992 g_assert (!info->async_target);
993 info->async_target = target_func;
994 /* This is not GC tracked */
995 info->user_data = user_data;
999 The suspend lock is held during any suspend in progress.
1000 A GC that has safepoints must take this lock as part of its
1001 STW to make sure no unsafe pending suspend is in progress.
1004 mono_thread_info_suspend_lock (void)
1006 int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1007 g_assert (res != -1);
1011 mono_thread_info_suspend_unlock (void)
1013 mono_coop_sem_post (&global_suspend_semaphore);
1017 * This is a very specific function whose only purpose is to
1018 * break a given thread from socket syscalls.
1020 * This only exists because linux won't fail a call to connect
1021 * if the underlying is closed.
1023 * TODO We should cleanup and unify this with the other syscall abort
1027 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1029 MonoThreadHazardPointers *hp;
1030 MonoThreadInfo *info;
1032 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1035 hp = mono_hazard_pointer_get ();
1036 info = mono_thread_info_lookup (tid);
1040 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1041 mono_hazard_pointer_clear (hp, 1);
1045 mono_thread_info_suspend_lock ();
1046 mono_threads_begin_global_suspend ();
1048 mono_threads_core_abort_syscall (info);
1049 mono_threads_wait_pending_operations ();
1051 mono_hazard_pointer_clear (hp, 1);
1053 mono_threads_end_global_suspend ();
1054 mono_thread_info_suspend_unlock ();
1058 mono_thread_info_unified_management_enabled (void)
1060 return unified_suspend_enabled;
1064 * mono_thread_info_set_is_async_context:
1066 * Set whenever the current thread is in an async context. Some runtime functions might behave
1067 * differently while in an async context in order to be async safe.
1070 mono_thread_info_set_is_async_context (gboolean async_context)
1072 MonoThreadInfo *info = mono_thread_info_current ();
1075 info->is_async_context = async_context;
1079 mono_thread_info_is_async_context (void)
1081 MonoThreadInfo *info = mono_thread_info_current ();
1084 return info->is_async_context;
1090 * mono_threads_create_thread:
1092 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1093 * Returns: a windows or io-layer handle for the thread.
1096 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1098 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1102 * mono_thread_info_get_stack_bounds:
1104 * Return the address and size of the current threads stack. Return NULL as the
1105 * stack address if the stack address cannot be determined.
1108 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1110 guint8 *current = (guint8 *)&stsize;
1111 mono_threads_core_get_stack_bounds (staddr, stsize);
1115 /* Sanity check the result */
1116 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1118 /* When running under emacs, sometimes staddr is not aligned to a page size */
1119 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1123 mono_thread_info_yield (void)
1125 return mono_threads_core_yield ();
1127 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1128 static MonoCoopMutex sleep_mutex;
1129 static MonoCoopCond sleep_cond;
1132 sleep_initialize (void)
1134 mono_coop_mutex_init (&sleep_mutex);
1135 mono_coop_cond_init (&sleep_cond);
1139 sleep_interrupt (gpointer data)
1141 mono_coop_mutex_lock (&sleep_mutex);
1142 mono_coop_cond_broadcast (&sleep_cond);
1143 mono_coop_mutex_unlock (&sleep_mutex);
1146 static inline guint32
1147 sleep_interruptable (guint32 ms, gboolean *alerted)
1149 guint32 start, now, end;
1151 g_assert (INFINITE == G_MAXUINT32);
1156 start = mono_msec_ticks ();
1158 if (start < G_MAXUINT32 - ms) {
1161 /* start + ms would overflow guint32 */
1165 mono_lazy_initialize (&sleep_init, sleep_initialize);
1167 mono_coop_mutex_lock (&sleep_mutex);
1169 for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
1170 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1172 mono_coop_mutex_unlock (&sleep_mutex);
1173 return WAIT_IO_COMPLETION;
1177 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1179 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1181 mono_thread_info_uninstall_interrupt (alerted);
1183 mono_coop_mutex_unlock (&sleep_mutex);
1184 return WAIT_IO_COMPLETION;
1188 mono_coop_mutex_unlock (&sleep_mutex);
1194 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1197 MonoThreadInfo *info;
1199 mono_thread_info_yield ();
1201 info = mono_thread_info_current ();
1202 if (info && mono_thread_info_is_interrupt_state (info))
1203 return WAIT_IO_COMPLETION;
1209 return sleep_interruptable (ms, alerted);
1211 MONO_PREPARE_BLOCKING;
1213 if (ms == INFINITE) {
1216 Sleep (G_MAXUINT32);
1218 sleep (G_MAXUINT32);
1223 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1224 struct timespec start, target;
1226 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1227 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1228 g_assert (ret == 0);
1231 target.tv_sec += ms / 1000;
1232 target.tv_nsec += (ms % 1000) * 1000000;
1233 if (target.tv_nsec > 999999999) {
1234 target.tv_nsec -= 999999999;
1239 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1244 struct timespec req, rem;
1246 req.tv_sec = ms / 1000;
1247 req.tv_nsec = (ms % 1000) * 1000000;
1250 memset (&rem, 0, sizeof (rem));
1251 ret = nanosleep (&req, &rem);
1253 #endif /* __linux__ */
1256 MONO_FINISH_BLOCKING;
1262 mono_thread_info_usleep (guint64 us)
1264 MONO_PREPARE_BLOCKING;
1266 MONO_FINISH_BLOCKING;
1271 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1273 return ((MonoThreadInfo*)info)->tls [key];
1277 * mono_threads_info_tls_set:
1279 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1280 * values of TLS variables for threads other than the current thread.
1281 * This should only be used for infrequently changing TLS variables, and it should
1282 * be paired with setting the real TLS variable since this provides no GC tracking.
1285 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1287 ((MonoThreadInfo*)info)->tls [key] = value;
1291 * mono_thread_info_exit:
1293 * Exit the current thread.
1294 * This function doesn't return.
1297 mono_thread_info_exit (void)
1299 mono_threads_core_exit (0);
1303 * mono_thread_info_open_handle:
1305 * Return a io-layer/win32 handle for the current thread.
1306 * The handle need to be closed by calling CloseHandle () when it is no
1310 mono_thread_info_open_handle (void)
1312 return mono_threads_core_open_handle ();
1316 * mono_threads_open_thread_handle:
1318 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1319 * The handle need to be closed by calling CloseHandle () when it is no
1323 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1325 return mono_threads_core_open_thread_handle (handle, tid);
1329 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1331 mono_threads_core_set_name (tid, name);
1334 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1336 struct _MonoThreadInfoInterruptToken {
1337 void (*callback) (gpointer data);
1342 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1344 * - @callback: must be able to be called from another thread and always cancel the wait
1345 * - @data: passed to the callback
1346 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1347 * if set to TRUE, it must mean that the thread is in interrupted state
1350 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1352 MonoThreadInfo *info;
1353 MonoThreadInfoInterruptToken *previous_token, *token;
1355 g_assert (callback);
1357 g_assert (interrupted);
1358 *interrupted = FALSE;
1360 info = mono_thread_info_current ();
1363 /* The memory of this token can be freed at 2 places:
1364 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1365 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1366 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1367 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1368 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1369 token->callback = callback;
1372 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1374 if (previous_token) {
1375 if (previous_token != INTERRUPT_STATE)
1376 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1380 *interrupted = TRUE;
1383 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1384 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1388 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1390 MonoThreadInfo *info;
1391 MonoThreadInfoInterruptToken *previous_token;
1393 g_assert (interrupted);
1394 *interrupted = FALSE;
1396 info = mono_thread_info_current ();
1399 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1401 /* only the installer can uninstall the token */
1402 g_assert (previous_token);
1404 if (previous_token == INTERRUPT_STATE) {
1405 /* if it is interrupted, then it is going to be freed in finish interrupt */
1406 *interrupted = TRUE;
1408 g_free (previous_token);
1411 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1412 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1415 static MonoThreadInfoInterruptToken*
1416 set_interrupt_state (MonoThreadInfo *info)
1418 MonoThreadInfoInterruptToken *token, *previous_token;
1422 /* Atomically obtain the token the thread is
1423 * waiting on, and change it to a flag value. */
1426 previous_token = info->interrupt_token;
1428 /* Already interrupted */
1429 if (previous_token == INTERRUPT_STATE) {
1434 token = previous_token;
1435 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1441 * mono_thread_info_prepare_interrupt:
1443 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1444 * - if the thread calls one of the WaitFor functions, the function will return with
1445 * WAIT_IO_COMPLETION instead of waiting
1446 * - if the thread was waiting when this function was called, the wait will be broken
1448 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1449 * didn't receive the interrupt signal yet, in this case it should call the wait function
1450 * again. This essentially means that the target thread will busy wait until it is ready to
1451 * process the interruption.
1453 MonoThreadInfoInterruptToken*
1454 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1456 MonoThreadInfoInterruptToken *token;
1458 token = set_interrupt_state (info);
1460 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1461 mono_thread_info_get_tid (info), token);
1467 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1469 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1474 g_assert (token->callback);
1476 token->callback (token->data);
1482 mono_thread_info_self_interrupt (void)
1484 MonoThreadInfo *info;
1485 MonoThreadInfoInterruptToken *token;
1487 info = mono_thread_info_current ();
1490 token = set_interrupt_state (info);
1493 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1494 mono_thread_info_get_tid (info));
1497 /* Clear the interrupted flag of the current thread, set with
1498 * mono_thread_info_self_interrupt, so it can wait again */
1500 mono_thread_info_clear_self_interrupt ()
1502 MonoThreadInfo *info;
1503 MonoThreadInfoInterruptToken *previous_token;
1505 info = mono_thread_info_current ();
1508 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1509 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1511 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1515 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1518 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1522 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1526 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1527 g_string_append_printf (text, "not waiting");
1528 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1529 g_string_append_printf (text, "interrupted state");
1531 g_string_append_printf (text, "waiting");
1534 /* info must be self or be held in a hazard pointer. */
1536 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1538 MonoAsyncJob old_job;
1540 old_job = (MonoAsyncJob) info->service_requests;
1543 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1548 mono_threads_consume_async_jobs (void)
1550 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1553 return (MonoAsyncJob) 0;
1555 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);