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>
32 #include <mono/utils/mach-support.h>
36 Mutex that makes sure only a single thread can be suspending others.
37 Suspend is a very racy operation since it requires restarting until
38 the target thread is not on an unsafe region.
40 We could implement this using critical regions, but would be much much
41 harder for an operation that is hardly performance critical.
43 The GC has to acquire this lock before starting a STW to make sure
44 a runtime suspend won't make it wronly see a thread in a safepoint
45 when it is in fact not.
47 static MonoSemType global_suspend_semaphore;
49 static size_t thread_info_size;
50 static MonoThreadInfoCallbacks threads_callbacks;
51 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
52 static MonoNativeTlsKey thread_info_key, thread_exited_key;
54 static __thread guint32 tls_small_id MONO_TLS_FAST;
56 static MonoNativeTlsKey small_id_key;
58 static MonoLinkedListSet thread_list;
59 static gboolean mono_threads_inited = FALSE;
61 static MonoSemType suspend_semaphore;
62 static size_t pending_suspends;
63 static gboolean unified_suspend_enabled;
65 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
68 #define SLEEP_DURATION_BEFORE_WARNING (10)
70 #define SLEEP_DURATION_BEFORE_ABORT 200
72 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
75 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
77 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
78 InterlockedIncrement (&abort_posts);
79 MONO_SEM_POST (&suspend_semaphore);
83 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
85 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
86 InterlockedIncrement (&suspend_posts);
87 MONO_SEM_POST (&suspend_semaphore);
91 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
93 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
94 InterlockedIncrement (&resume_posts);
95 MONO_SEM_POST (&suspend_semaphore);
99 resume_async_suspended (MonoThreadInfo *info)
101 g_assert (mono_threads_core_begin_async_resume (info));
105 resume_self_suspended (MonoThreadInfo* info)
107 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
108 MONO_SEM_POST (&info->resume_semaphore);
112 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
114 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
115 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
119 resume_blocking_suspended (MonoThreadInfo* info)
121 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
122 MONO_SEM_POST (&info->resume_semaphore);
126 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
128 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
130 InterlockedIncrement (&pending_ops);
134 mono_threads_begin_global_suspend (void)
136 g_assert (pending_suspends == 0);
137 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,
138 abort_posts, waits_done, pending_ops);
139 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
140 mono_threads_core_begin_global_suspend ();
144 mono_threads_end_global_suspend (void)
146 g_assert (pending_suspends == 0);
147 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
148 abort_posts, waits_done, pending_ops);
149 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
150 mono_threads_core_end_global_suspend ();
156 MonoThreadInfo *info;
157 MonoThreadInfo *cur = mono_thread_info_current ();
159 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
160 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
161 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
162 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
163 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
164 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
165 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
166 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
167 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
168 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
170 FOREACH_THREAD_SAFE (info) {
172 char thread_name [256] = { 0 };
173 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
175 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" : "" );
177 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" : "" );
180 } END_FOREACH_THREAD_SAFE
184 mono_threads_wait_pending_operations (void)
187 int c = pending_suspends;
189 /* Wait threads to park */
190 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
191 if (pending_suspends) {
192 MonoStopwatch suspension_time;
193 mono_stopwatch_start (&suspension_time);
194 for (i = 0; i < pending_suspends; ++i) {
195 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
196 InterlockedIncrement (&waits_done);
197 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
199 mono_stopwatch_stop (&suspension_time);
203 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
204 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);
206 mono_stopwatch_stop (&suspension_time);
207 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
211 pending_suspends = 0;
217 //Thread initialization code
219 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
222 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
225 mono_hazard_pointer_clear (hp, 0);
227 mono_hazard_pointer_clear (hp, 1);
229 mono_hazard_pointer_clear (hp, 2);
233 If return non null Hazard Pointer 1 holds the return value.
236 mono_thread_info_lookup (MonoNativeThreadId id)
238 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
240 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
241 mono_hazard_pointer_clear_all (hp, -1);
245 mono_hazard_pointer_clear_all (hp, 1);
246 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
250 mono_thread_info_insert (MonoThreadInfo *info)
252 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
254 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
255 mono_hazard_pointer_clear_all (hp, -1);
259 mono_hazard_pointer_clear_all (hp, -1);
264 mono_thread_info_remove (MonoThreadInfo *info)
266 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
269 THREADS_DEBUG ("removing info %p\n", info);
270 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
271 mono_hazard_pointer_clear_all (hp, -1);
276 free_thread_info (gpointer mem)
278 MonoThreadInfo *info = (MonoThreadInfo *) mem;
280 MONO_SEM_DESTROY (&info->resume_semaphore);
281 mono_threads_platform_free (info);
287 mono_thread_info_register_small_id (void)
289 int small_id = mono_thread_small_id_alloc ();
290 #ifdef HAVE_KW_THREAD
291 tls_small_id = small_id;
293 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
299 register_thread (MonoThreadInfo *info, gpointer baseptr)
302 guint8 *staddr = NULL;
303 int small_id = mono_thread_info_register_small_id ();
305 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
306 info->small_id = small_id;
308 MONO_SEM_INIT (&info->resume_semaphore, 0);
310 /*set TLS early so SMR works */
311 mono_native_tls_set_value (thread_info_key, info);
313 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
315 if (threads_callbacks.thread_register) {
316 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
317 // g_warning ("thread registation failed\n");
323 mono_thread_info_get_stack_bounds (&staddr, &stsize);
326 info->stack_start_limit = staddr;
327 info->stack_end = staddr + stsize;
329 mono_threads_platform_register (info);
332 Transition it before taking any locks or publishing itself to reduce the chance
333 of others witnessing a detached thread.
334 We can reasonably expect that until this thread gets published, no other thread will
335 try to manipulate it.
337 mono_threads_transition_attach (info);
338 mono_thread_info_suspend_lock ();
339 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
340 result = mono_thread_info_insert (info);
342 mono_thread_info_suspend_unlock ();
347 unregister_thread (void *arg)
349 MonoThreadInfo *info = (MonoThreadInfo *) arg;
350 int small_id = info->small_id;
353 THREADS_DEBUG ("unregistering info %p\n", info);
355 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
357 mono_threads_core_unregister (info);
360 * TLS destruction order is not reliable so small_id might be cleaned up
363 #ifndef HAVE_KW_THREAD
364 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
368 First perform the callback that requires no locks.
369 This callback has the potential of taking other locks, so we do it before.
370 After it completes, the thread remains functional.
372 if (threads_callbacks.thread_detach)
373 threads_callbacks.thread_detach (info);
375 mono_thread_info_suspend_lock ();
378 Now perform the callback that must be done under locks.
379 This will render the thread useless and non-suspendable, so it must
380 be done while holding the suspend lock to give no other thread chance
383 if (threads_callbacks.thread_unregister)
384 threads_callbacks.thread_unregister (info);
385 mono_threads_unregister_current_thread (info);
386 mono_threads_transition_detach (info);
388 mono_thread_info_suspend_unlock ();
390 /*now it's safe to free the thread info.*/
391 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
392 mono_thread_small_id_free (small_id);
396 thread_exited_dtor (void *arg)
398 #if defined(__MACH__)
400 * Since we use pthread dtors to clean up thread data, if a thread
401 * is attached to the runtime by another pthread dtor after our dtor
402 * has ran, it will never be detached, leading to various problems
403 * since the thread ids etc. will be reused while they are still in
404 * the threads hashtables etc.
405 * Dtors are called in a loop until all user tls entries are 0,
406 * but the loop has a maximum count (4), so if we set the tls
407 * variable every time, it will remain set when system tls dtors
408 * are ran. This allows mono_thread_info_is_exiting () to detect
409 * whenever the thread is exiting, even if it is executed from a
410 * system tls dtor (i.e. obj-c dealloc methods).
412 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
417 * Removes the current thread from the thread list.
418 * This must be called from the thread unregister callback and nowhere else.
419 * The current thread must be passed as TLS might have already been cleaned up.
422 mono_threads_unregister_current_thread (MonoThreadInfo *info)
425 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
426 result = mono_thread_info_remove (info);
431 mono_thread_info_current_unchecked (void)
433 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
438 mono_thread_info_current (void)
440 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
444 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
447 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
448 The way to distinguish between before, during and after cleanup is the following:
450 -If the TLS key is set, cleanup has not begun;
451 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
452 -If the thread is nowhere to be found, cleanup has finished.
454 We cannot function after cleanup since there's no way to ensure what will happen.
458 /*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 */
459 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
465 mono_thread_info_get_small_id (void)
467 #ifdef HAVE_KW_THREAD
470 gpointer val = mono_native_tls_get_value (small_id_key);
473 return GPOINTER_TO_INT (val) - 1;
478 mono_thread_info_list_head (void)
484 * mono_threads_attach_tools_thread
486 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
488 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
489 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
492 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
493 * doing things like resolving backtraces in their background processing thread.
496 mono_threads_attach_tools_thread (void)
499 MonoThreadInfo *info;
501 /* Must only be called once */
502 g_assert (!mono_native_tls_get_value (thread_info_key));
504 while (!mono_threads_inited) {
508 info = mono_thread_info_attach (&dummy);
511 info->tools_thread = TRUE;
515 mono_thread_info_attach (void *baseptr)
517 MonoThreadInfo *info;
518 if (!mono_threads_inited)
521 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
522 * thread is created before an embedding API user initialized Mono. */
523 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
526 g_assert (mono_threads_inited);
529 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
531 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
532 THREADS_DEBUG ("attaching %p\n", info);
533 if (!register_thread (info, baseptr))
535 } else if (threads_callbacks.thread_attach) {
536 threads_callbacks.thread_attach (info);
542 mono_thread_info_detach (void)
544 MonoThreadInfo *info;
545 if (!mono_threads_inited)
547 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
548 * is created before an embedding API user initialized Mono. */
549 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
552 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
554 THREADS_DEBUG ("detaching %p\n", info);
555 unregister_thread (info);
556 mono_native_tls_set_value (thread_info_key, NULL);
561 * mono_thread_info_is_exiting:
563 * Return whenever the current thread is exiting, i.e. it is running pthread
567 mono_thread_info_is_exiting (void)
569 #if defined(__MACH__)
570 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
577 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
580 threads_callbacks = *callbacks;
581 thread_info_size = info_size;
583 res = mono_native_tls_alloc (&thread_info_key, NULL);
584 res = mono_native_tls_alloc (&thread_exited_key, NULL);
586 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
587 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
592 #ifndef HAVE_KW_THREAD
593 res = mono_native_tls_alloc (&small_id_key, NULL);
597 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || MONO_THREADS_PLATFORM_REQUIRES_UNIFIED_SUSPEND;
599 MONO_SEM_INIT (&global_suspend_semaphore, 1);
600 MONO_SEM_INIT (&suspend_semaphore, 0);
602 mono_lls_init (&thread_list, NULL);
603 mono_thread_smr_init ();
604 mono_threads_init_platform ();
605 mono_threads_init_abort_syscall ();
607 #if defined(__MACH__)
608 mono_mach_init (thread_info_key);
611 mono_threads_inited = TRUE;
613 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
617 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
619 runtime_callbacks = *callbacks;
622 MonoThreadInfoRuntimeCallbacks *
623 mono_threads_get_runtime_callbacks (void)
625 return &runtime_callbacks;
629 The return value is only valid until a matching mono_thread_info_resume is called
631 static MonoThreadInfo*
632 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
634 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
635 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
637 *error_condition = "Thread not found";
641 switch (mono_threads_transition_request_async_suspension (info)) {
642 case AsyncSuspendAlreadySuspended:
643 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
645 case AsyncSuspendWait:
646 mono_threads_add_to_pending_operation_set (info);
648 case AsyncSuspendInitSuspend:
649 if (!mono_threads_core_begin_async_suspend (info, interrupt_kernel)) {
650 mono_hazard_pointer_clear (hp, 1);
651 *error_condition = "Could not suspend thread";
656 //Wait for the pending suspend to finish
657 mono_threads_wait_pending_operations ();
659 if (!mono_threads_core_check_suspend_result (info)) {
661 mono_hazard_pointer_clear (hp, 1);
662 *error_condition = "Post suspend failed";
669 Signal that the current thread wants to be suspended.
670 This function can be called without holding the suspend lock held.
671 To finish suspending, call mono_suspend_check.
674 mono_thread_info_begin_self_suspend (void)
676 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
680 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
681 mono_threads_transition_request_self_suspension (info);
685 mono_thread_info_end_self_suspend (void)
687 MonoThreadInfo *info;
689 info = mono_thread_info_current ();
692 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
694 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
696 /* commit the saved state and notify others if needed */
697 switch (mono_threads_transition_state_poll (info)) {
698 case SelfSuspendResumed:
700 case SelfSuspendWait:
701 mono_thread_info_wait_for_resume (info);
703 case SelfSuspendNotifyAndWait:
704 mono_threads_notify_initiator_of_suspend (info);
705 mono_thread_info_wait_for_resume (info);
706 mono_threads_notify_initiator_of_resume (info);
712 mono_thread_info_core_resume (MonoThreadInfo *info)
714 gboolean res = FALSE;
715 if (info->create_suspended) {
716 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
717 /* 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 */
718 info->create_suspended = FALSE;
719 mono_threads_core_resume_created (info, tid);
723 switch (mono_threads_transition_request_resume (info)) {
730 case ResumeInitSelfResume:
731 resume_self_suspended (info);
734 case ResumeInitAsyncResume:
735 resume_async_suspended (info);
738 case ResumeInitBlockingResume:
739 resume_blocking_suspended (info);
748 mono_thread_info_resume (MonoNativeThreadId tid)
750 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
751 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
752 MonoThreadInfo *info;
754 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
756 mono_thread_info_suspend_lock ();
758 info = mono_thread_info_lookup (tid); /*info on HP1*/
764 result = mono_thread_info_core_resume (info);
766 //Wait for the pending resume to finish
767 mono_threads_wait_pending_operations ();
770 mono_thread_info_suspend_unlock ();
771 mono_hazard_pointer_clear (hp, 1);
776 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
778 switch (mono_threads_transition_request_async_suspension (info)) {
779 case AsyncSuspendAlreadySuspended:
781 case AsyncSuspendWait:
782 mono_threads_add_to_pending_operation_set (info);
784 case AsyncSuspendInitSuspend:
785 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
787 g_assert_not_reached ();
792 mono_thread_info_begin_resume (MonoThreadInfo *info)
794 return mono_thread_info_core_resume (info);
798 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
799 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
802 is_thread_in_critical_region (MonoThreadInfo *info)
806 gpointer stack_start;
807 MonoThreadUnwindState *state;
809 /* Are we inside a system critical region? */
810 if (info->inside_critical_region)
813 /* Are we inside a GC critical region? */
814 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
818 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
819 state = mono_thread_info_get_suspend_state (info);
820 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
823 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
824 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
825 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
828 ji = mono_jit_info_table_find (
829 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
830 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
835 method = mono_jit_info_get_method (ji);
837 return threads_callbacks.mono_method_is_critical (method);
841 mono_thread_info_in_critical_location (MonoThreadInfo *info)
843 return is_thread_in_critical_region (info);
846 static MonoThreadInfo*
847 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
849 MonoThreadInfo *info = NULL;
850 int sleep_duration = 0;
852 const char *suspend_error = "Unknown error";
853 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
854 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
858 /*WARNING: We now are in interrupt context until we resume the thread. */
859 if (!is_thread_in_critical_region (info))
862 if (!mono_thread_info_core_resume (info)) {
863 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
866 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
868 /* Wait for the pending resume to finish */
869 mono_threads_wait_pending_operations ();
871 if (!sleep_duration) {
879 g_usleep (sleep_duration);
881 sleep_duration += 10;
887 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
890 MonoThreadInfo *info = NULL;
891 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
893 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
894 /*FIXME: unify this with self-suspend*/
895 g_assert (id != mono_native_thread_id_get ());
897 /* This can block during stw */
898 mono_thread_info_suspend_lock ();
899 mono_threads_begin_global_suspend ();
901 info = suspend_sync_nolock (id, interrupt_kernel);
905 switch (result = callback (info, user_data)) {
906 case MonoResumeThread:
907 mono_hazard_pointer_set (hp, 1, info);
908 mono_thread_info_core_resume (info);
909 mono_threads_wait_pending_operations ();
914 g_error ("Invalid suspend_and_run callback return value %d", result);
918 mono_hazard_pointer_clear (hp, 1);
919 mono_threads_end_global_suspend ();
920 mono_thread_info_suspend_unlock ();
925 If we are trying to suspend a target that is on a critical region
926 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
927 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
929 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
931 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
934 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
936 MonoThreadInfo *info = NULL;
938 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
939 /*FIXME: unify this with self-suspend*/
940 g_assert (id != mono_native_thread_id_get ());
942 mono_thread_info_suspend_lock ();
943 mono_threads_begin_global_suspend ();
945 info = suspend_sync_nolock (id, interrupt_kernel);
947 /* XXX this clears HP 1, so we restated it again */
948 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
949 mono_threads_end_global_suspend ();
950 mono_thread_info_suspend_unlock ();
956 Inject an assynchronous call into the target thread. The target thread must be suspended and
957 only a single async call can be setup for a given suspend cycle.
958 This async call must cause stack unwinding as the current implementation doesn't save enough state
959 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
960 currently used only to deliver exceptions.
963 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
965 /* An async call can only be setup on an async suspended thread */
966 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
967 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
968 g_assert (!info->async_target);
969 info->async_target = target_func;
970 /* This is not GC tracked */
971 info->user_data = user_data;
975 The suspend lock is held during any suspend in progress.
976 A GC that has safepoints must take this lock as part of its
977 STW to make sure no unsafe pending suspend is in progress.
980 mono_thread_info_suspend_lock (void)
983 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
984 MONO_FINISH_TRY_BLOCKING;
988 mono_thread_info_suspend_unlock (void)
990 MONO_SEM_POST (&global_suspend_semaphore);
994 * This is a very specific function whose only purpose is to
995 * break a given thread from socket syscalls.
997 * This only exists because linux won't fail a call to connect
998 * if the underlying is closed.
1000 * TODO We should cleanup and unify this with the other syscall abort
1004 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1006 MonoThreadHazardPointers *hp;
1007 MonoThreadInfo *info;
1009 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1012 hp = mono_hazard_pointer_get ();
1013 info = mono_thread_info_lookup (tid); /*info on HP1*/
1017 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
1018 mono_hazard_pointer_clear (hp, 1);
1022 mono_thread_info_suspend_lock ();
1023 mono_threads_begin_global_suspend ();
1025 mono_threads_core_abort_syscall (info);
1026 mono_threads_wait_pending_operations ();
1028 mono_hazard_pointer_clear (hp, 1);
1030 mono_threads_end_global_suspend ();
1031 mono_thread_info_suspend_unlock ();
1035 mono_thread_info_unified_management_enabled (void)
1037 return unified_suspend_enabled;
1041 * mono_thread_info_set_is_async_context:
1043 * Set whenever the current thread is in an async context. Some runtime functions might behave
1044 * differently while in an async context in order to be async safe.
1047 mono_thread_info_set_is_async_context (gboolean async_context)
1049 MonoThreadInfo *info = mono_thread_info_current ();
1052 info->is_async_context = async_context;
1056 mono_thread_info_is_async_context (void)
1058 MonoThreadInfo *info = mono_thread_info_current ();
1061 return info->is_async_context;
1067 * mono_threads_create_thread:
1069 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1070 * Returns: a windows or io-layer handle for the thread.
1073 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1075 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1079 * mono_thread_info_get_stack_bounds:
1081 * Return the address and size of the current threads stack. Return NULL as the
1082 * stack address if the stack address cannot be determined.
1085 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1087 guint8 *current = (guint8 *)&stsize;
1088 mono_threads_core_get_stack_bounds (staddr, stsize);
1092 /* Sanity check the result */
1093 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1095 /* When running under emacs, sometimes staddr is not aligned to a page size */
1096 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1100 mono_thread_info_yield (void)
1102 return mono_threads_core_yield ();
1106 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1108 return ((MonoThreadInfo*)info)->tls [key];
1112 * mono_threads_info_tls_set:
1114 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1115 * values of TLS variables for threads other than the current thread.
1116 * This should only be used for infrequently changing TLS variables, and it should
1117 * be paired with setting the real TLS variable since this provides no GC tracking.
1120 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1122 ((MonoThreadInfo*)info)->tls [key] = value;
1126 * mono_thread_info_exit:
1128 * Exit the current thread.
1129 * This function doesn't return.
1132 mono_thread_info_exit (void)
1134 mono_threads_core_exit (0);
1138 * mono_thread_info_open_handle:
1140 * Return a io-layer/win32 handle for the current thread.
1141 * The handle need to be closed by calling CloseHandle () when it is no
1145 mono_thread_info_open_handle (void)
1147 return mono_threads_core_open_handle ();
1151 * mono_threads_open_thread_handle:
1153 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1154 * The handle need to be closed by calling CloseHandle () when it is no
1158 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1160 return mono_threads_core_open_thread_handle (handle, tid);
1164 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1166 mono_threads_core_set_name (tid, name);
1169 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1171 struct _MonoThreadInfoInterruptToken {
1172 void (*callback) (gpointer data);
1177 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1179 * - @callback: must be able to be called from another thread and always cancel the wait
1180 * - @data: passed to the callback
1181 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1182 * if set to TRUE, it must mean that the thread is in interrupted state
1185 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1187 MonoThreadInfo *info;
1188 MonoThreadInfoInterruptToken *previous_token, *token;
1190 g_assert (callback);
1192 g_assert (interrupted);
1193 *interrupted = FALSE;
1195 info = mono_thread_info_current ();
1198 /* The memory of this token can be freed at 2 places:
1199 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1200 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1201 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1202 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1203 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1204 token->callback = callback;
1207 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1209 if (previous_token) {
1210 if (previous_token != INTERRUPT_STATE)
1211 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1215 *interrupted = TRUE;
1218 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1219 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1223 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1225 MonoThreadInfo *info;
1226 MonoThreadInfoInterruptToken *previous_token;
1228 g_assert (interrupted);
1229 *interrupted = FALSE;
1231 info = mono_thread_info_current ();
1234 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1236 /* only the installer can uninstall the token */
1237 g_assert (previous_token);
1239 if (previous_token == INTERRUPT_STATE) {
1240 /* if it is interrupted, then it is going to be freed in finish interrupt */
1241 *interrupted = TRUE;
1243 g_free (previous_token);
1246 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1247 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1250 static MonoThreadInfoInterruptToken*
1251 set_interrupt_state (MonoThreadInfo *info)
1253 MonoThreadInfoInterruptToken *token, *previous_token;
1257 /* Atomically obtain the token the thread is
1258 * waiting on, and change it to a flag value. */
1261 previous_token = info->interrupt_token;
1263 /* Already interrupted */
1264 if (previous_token == INTERRUPT_STATE) {
1269 token = previous_token;
1270 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1276 * mono_thread_info_prepare_interrupt:
1278 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1279 * - if the thread calls one of the WaitFor functions, the function will return with
1280 * WAIT_IO_COMPLETION instead of waiting
1281 * - if the thread was waiting when this function was called, the wait will be broken
1283 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1284 * didn't receive the interrupt signal yet, in this case it should call the wait function
1285 * again. This essentially means that the target thread will busy wait until it is ready to
1286 * process the interruption.
1288 MonoThreadInfoInterruptToken*
1289 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1291 MonoThreadInfoInterruptToken *token;
1293 token = set_interrupt_state (info);
1295 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1296 mono_thread_info_get_tid (info), token);
1302 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1304 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1309 g_assert (token->callback);
1311 token->callback (token->data);
1317 mono_thread_info_self_interrupt (void)
1319 MonoThreadInfo *info;
1320 MonoThreadInfoInterruptToken *token;
1322 info = mono_thread_info_current ();
1325 token = set_interrupt_state (info);
1328 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1329 mono_thread_info_get_tid (info));
1332 /* Clear the interrupted flag of the current thread, set with
1333 * mono_thread_info_self_interrupt, so it can wait again */
1335 mono_thread_info_clear_self_interrupt ()
1337 MonoThreadInfo *info;
1338 MonoThreadInfoInterruptToken *previous_token;
1340 info = mono_thread_info_current ();
1343 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1344 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1346 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1350 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1353 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1357 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1361 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1362 g_string_append_printf (text, "not waiting");
1363 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1364 g_string_append_printf (text, "interrupted state");
1366 g_string_append_printf (text, "waiting");
1369 /* info must be self or be held in a hazard pointer. */
1371 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1373 MonoAsyncJob old_job;
1375 old_job = (MonoAsyncJob) info->service_requests;
1378 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1383 mono_threads_consume_async_jobs (void)
1385 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1388 return (MonoAsyncJob) 0;
1390 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);