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 #include <mono/utils/mono-compiler.h>
14 #include <mono/utils/mono-semaphore.h>
15 #include <mono/utils/mono-threads.h>
16 #include <mono/utils/mono-tls.h>
17 #include <mono/utils/hazard-pointer.h>
18 #include <mono/utils/mono-memory-model.h>
19 #include <mono/utils/mono-mmap.h>
20 #include <mono/utils/atomic.h>
21 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mach-support.h>
31 Mutex that makes sure only a single thread can be suspending others.
32 Suspend is a very racy operation since it requires restarting until
33 the target thread is not on an unsafe region.
35 We could implement this using critical regions, but would be much much
36 harder for an operation that is hardly performance critical.
38 The GC has to acquire this lock before starting a STW to make sure
39 a runtime suspend won't make it wronly see a thread in a safepoint
40 when it is in fact not.
42 static MonoSemType global_suspend_semaphore;
44 static size_t thread_info_size;
45 static MonoThreadInfoCallbacks threads_callbacks;
46 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
47 static MonoNativeTlsKey thread_info_key, thread_exited_key;
49 static __thread guint32 tls_small_id MONO_TLS_FAST;
51 static MonoNativeTlsKey small_id_key;
53 static MonoLinkedListSet thread_list;
54 static gboolean mono_threads_inited = FALSE;
56 static MonoSemType suspend_semaphore;
57 static size_t pending_suspends;
58 static gboolean unified_suspend_enabled;
60 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
63 #define SLEEP_DURATION_BEFORE_WARNING (10)
65 #define SLEEP_DURATION_BEFORE_ABORT 200
67 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
70 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
72 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
73 MONO_SEM_POST (&suspend_semaphore);
74 InterlockedIncrement (&abort_posts);
78 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
80 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
81 MONO_SEM_POST (&suspend_semaphore);
82 InterlockedIncrement (&suspend_posts);
86 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
88 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
89 MONO_SEM_POST (&suspend_semaphore);
90 InterlockedIncrement (&resume_posts);
94 resume_async_suspended (MonoThreadInfo *info)
96 g_assert (mono_threads_core_begin_async_resume (info));
100 resume_self_suspended (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
103 MONO_SEM_POST (&info->resume_semaphore);
107 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
109 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
110 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
114 resume_blocking_suspended (MonoThreadInfo* info)
116 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
117 MONO_SEM_POST (&info->resume_semaphore);
121 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
123 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
125 InterlockedIncrement (&pending_ops);
129 mono_threads_begin_global_suspend (void)
131 g_assert (pending_suspends == 0);
132 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
133 abort_posts, waits_done, pending_ops);
134 mono_threads_core_begin_global_suspend ();
138 mono_threads_end_global_suspend (void)
140 g_assert (pending_suspends == 0);
141 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
142 abort_posts, waits_done, pending_ops);
143 mono_threads_core_end_global_suspend ();
149 MonoThreadInfo *info;
150 MonoThreadInfo *cur = mono_thread_info_current ();
152 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
153 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
154 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
155 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
156 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
157 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
158 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
159 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
160 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
161 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
163 FOREACH_THREAD_SAFE (info) {
164 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" : "" );
165 } END_FOREACH_THREAD_SAFE
169 mono_threads_wait_pending_operations (void)
172 int c = pending_suspends;
174 /* Wait threads to park */
175 if (pending_suspends) {
176 MonoStopwatch suspension_time;
177 mono_stopwatch_start (&suspension_time);
178 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
179 for (i = 0; i < pending_suspends; ++i) {
180 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
181 InterlockedIncrement (&waits_done);
182 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
184 mono_stopwatch_stop (&suspension_time);
188 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
189 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);
191 mono_stopwatch_stop (&suspension_time);
192 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
196 pending_suspends = 0;
202 //Thread initialization code
204 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
207 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
210 mono_hazard_pointer_clear (hp, 0);
212 mono_hazard_pointer_clear (hp, 1);
214 mono_hazard_pointer_clear (hp, 2);
218 If return non null Hazard Pointer 1 holds the return value.
221 mono_thread_info_lookup (MonoNativeThreadId id)
223 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
225 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
226 mono_hazard_pointer_clear_all (hp, -1);
230 mono_hazard_pointer_clear_all (hp, 1);
231 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
235 mono_thread_info_insert (MonoThreadInfo *info)
237 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
239 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
240 mono_hazard_pointer_clear_all (hp, -1);
244 mono_hazard_pointer_clear_all (hp, -1);
249 mono_thread_info_remove (MonoThreadInfo *info)
251 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
254 THREADS_DEBUG ("removing info %p\n", info);
255 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
256 mono_hazard_pointer_clear_all (hp, -1);
261 free_thread_info (gpointer mem)
263 MonoThreadInfo *info = (MonoThreadInfo *) mem;
265 MONO_SEM_DESTROY (&info->resume_semaphore);
266 mono_threads_platform_free (info);
272 mono_thread_info_register_small_id (void)
274 int small_id = mono_thread_small_id_alloc ();
275 #ifdef HAVE_KW_THREAD
276 tls_small_id = small_id;
278 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
284 register_thread (MonoThreadInfo *info, gpointer baseptr)
287 guint8 *staddr = NULL;
288 int small_id = mono_thread_info_register_small_id ();
290 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
291 info->small_id = small_id;
293 MONO_SEM_INIT (&info->resume_semaphore, 0);
295 /*set TLS early so SMR works */
296 mono_native_tls_set_value (thread_info_key, info);
298 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
300 if (threads_callbacks.thread_register) {
301 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
302 // g_warning ("thread registation failed\n");
308 mono_thread_info_get_stack_bounds (&staddr, &stsize);
311 info->stack_start_limit = staddr;
312 info->stack_end = staddr + stsize;
314 mono_threads_platform_register (info);
317 Transition it before taking any locks or publishing itself to reduce the chance
318 of others witnessing a detached thread.
319 We can reasonably expect that until this thread gets published, no other thread will
320 try to manipulate it.
322 mono_threads_transition_attach (info);
323 mono_thread_info_suspend_lock ();
324 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
325 result = mono_thread_info_insert (info);
327 mono_thread_info_suspend_unlock ();
332 unregister_thread (void *arg)
334 MonoThreadInfo *info = (MonoThreadInfo *) arg;
335 int small_id = info->small_id;
338 THREADS_DEBUG ("unregistering info %p\n", info);
340 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
342 mono_threads_core_unregister (info);
345 * TLS destruction order is not reliable so small_id might be cleaned up
348 #ifndef HAVE_KW_THREAD
349 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
353 First perform the callback that requires no locks.
354 This callback has the potential of taking other locks, so we do it before.
355 After it completes, the thread remains functional.
357 if (threads_callbacks.thread_detach)
358 threads_callbacks.thread_detach (info);
361 Since the thread info lock is taken from within blocking sections, we can't check from there, so it must be done here.
362 This ensures that we won't lose any suspend requests as a suspend initiator must hold the lock.
363 Once we're holding the suspend lock, no threads can suspend us and once we unregister, no thread can find us.
365 MONO_PREPARE_BLOCKING;
366 mono_thread_info_suspend_lock ();
367 MONO_FINISH_BLOCKING;
370 Now perform the callback that must be done under locks.
371 This will render the thread useless and non-suspendable, so it must
372 be done while holding the suspend lock to give no other thread chance
375 if (threads_callbacks.thread_unregister)
376 threads_callbacks.thread_unregister (info);
377 mono_threads_unregister_current_thread (info);
378 mono_threads_transition_detach (info);
380 mono_thread_info_suspend_unlock ();
382 /*now it's safe to free the thread info.*/
383 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
384 mono_thread_small_id_free (small_id);
388 thread_exited_dtor (void *arg)
390 #if defined(__MACH__)
392 * Since we use pthread dtors to clean up thread data, if a thread
393 * is attached to the runtime by another pthread dtor after our dtor
394 * has ran, it will never be detached, leading to various problems
395 * since the thread ids etc. will be reused while they are still in
396 * the threads hashtables etc.
397 * Dtors are called in a loop until all user tls entries are 0,
398 * but the loop has a maximum count (4), so if we set the tls
399 * variable every time, it will remain set when system tls dtors
400 * are ran. This allows mono_thread_info_is_exiting () to detect
401 * whenever the thread is exiting, even if it is executed from a
402 * system tls dtor (i.e. obj-c dealloc methods).
404 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
409 * Removes the current thread from the thread list.
410 * This must be called from the thread unregister callback and nowhere else.
411 * The current thread must be passed as TLS might have already been cleaned up.
414 mono_threads_unregister_current_thread (MonoThreadInfo *info)
417 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
418 result = mono_thread_info_remove (info);
423 mono_thread_info_current_unchecked (void)
425 return (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
430 mono_thread_info_current (void)
432 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
436 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
439 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
440 The way to distinguish between before, during and after cleanup is the following:
442 -If the TLS key is set, cleanup has not begun;
443 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
444 -If the thread is nowhere to be found, cleanup has finished.
446 We cannot function after cleanup since there's no way to ensure what will happen.
450 /*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 */
451 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
457 mono_thread_info_get_small_id (void)
459 #ifdef HAVE_KW_THREAD
462 gpointer val = mono_native_tls_get_value (small_id_key);
465 return GPOINTER_TO_INT (val) - 1;
470 mono_thread_info_list_head (void)
476 * mono_threads_attach_tools_thread
478 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
480 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
481 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
484 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
485 * doing things like resolving backtraces in their background processing thread.
488 mono_threads_attach_tools_thread (void)
491 MonoThreadInfo *info;
493 /* Must only be called once */
494 g_assert (!mono_native_tls_get_value (thread_info_key));
496 while (!mono_threads_inited) {
500 info = mono_thread_info_attach (&dummy);
503 info->tools_thread = TRUE;
507 mono_thread_info_attach (void *baseptr)
509 MonoThreadInfo *info;
510 if (!mono_threads_inited)
513 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
514 * thread is created before an embedding API user initialized Mono. */
515 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
518 g_assert (mono_threads_inited);
521 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
523 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
524 THREADS_DEBUG ("attaching %p\n", info);
525 if (!register_thread (info, baseptr))
527 } else if (threads_callbacks.thread_attach) {
528 threads_callbacks.thread_attach (info);
534 mono_thread_info_detach (void)
536 MonoThreadInfo *info;
537 if (!mono_threads_inited)
539 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
540 * is created before an embedding API user initialized Mono. */
541 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
544 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
546 THREADS_DEBUG ("detaching %p\n", info);
547 unregister_thread (info);
548 mono_native_tls_set_value (thread_info_key, NULL);
553 * mono_thread_info_is_exiting:
555 * Return whenever the current thread is exiting, i.e. it is running pthread
559 mono_thread_info_is_exiting (void)
561 #if defined(__MACH__)
562 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
569 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
572 threads_callbacks = *callbacks;
573 thread_info_size = info_size;
575 res = mono_native_tls_alloc (&thread_info_key, NULL);
576 res = mono_native_tls_alloc (&thread_exited_key, NULL);
578 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
579 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
583 #ifndef HAVE_KW_THREAD
584 res = mono_native_tls_alloc (&small_id_key, NULL);
588 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || MONO_THREADS_PLATFORM_REQUIRES_UNIFIED_SUSPEND;
590 MONO_SEM_INIT (&global_suspend_semaphore, 1);
591 MONO_SEM_INIT (&suspend_semaphore, 0);
593 mono_lls_init (&thread_list, NULL);
594 mono_thread_smr_init ();
595 mono_threads_init_platform ();
597 #if defined(__MACH__)
598 mono_mach_init (thread_info_key);
601 mono_threads_inited = TRUE;
603 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
607 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
609 runtime_callbacks = *callbacks;
612 MonoThreadInfoRuntimeCallbacks *
613 mono_threads_get_runtime_callbacks (void)
615 return &runtime_callbacks;
619 The return value is only valid until a matching mono_thread_info_resume is called
621 static MonoThreadInfo*
622 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
624 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
625 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
627 *error_condition = "Thread not found";
631 switch (mono_threads_transition_request_async_suspension (info)) {
632 case AsyncSuspendAlreadySuspended:
633 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
635 case AsyncSuspendWait:
636 mono_threads_add_to_pending_operation_set (info);
638 case AsyncSuspendInitSuspend:
639 if (!mono_threads_core_begin_async_suspend (info, interrupt_kernel)) {
640 mono_hazard_pointer_clear (hp, 1);
641 *error_condition = "Could not suspend thread";
646 //Wait for the pending suspend to finish
647 mono_threads_wait_pending_operations ();
649 if (!mono_threads_core_check_suspend_result (info)) {
651 mono_hazard_pointer_clear (hp, 1);
652 *error_condition = "Post suspend failed";
659 Signal that the current thread wants to be suspended.
660 This function can be called without holding the suspend lock held.
661 To finish suspending, call mono_suspend_check.
664 mono_thread_info_begin_self_suspend (void)
666 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
670 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
671 mono_threads_transition_request_self_suspension (info);
675 mono_thread_info_end_self_suspend (void)
677 MonoThreadInfo *info;
679 info = mono_thread_info_current ();
682 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
684 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
686 /* commit the saved state and notify others if needed */
687 switch (mono_threads_transition_state_poll (info)) {
688 case SelfSuspendResumed:
690 case SelfSuspendWait:
691 mono_thread_info_wait_for_resume (info);
693 case SelfSuspendNotifyAndWait:
694 mono_threads_notify_initiator_of_suspend (info);
695 mono_thread_info_wait_for_resume (info);
696 mono_threads_notify_initiator_of_resume (info);
702 mono_thread_info_core_resume (MonoThreadInfo *info)
704 gboolean res = FALSE;
705 if (info->create_suspended) {
706 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
707 /* 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 */
708 info->create_suspended = FALSE;
709 mono_threads_core_resume_created (info, tid);
713 switch (mono_threads_transition_request_resume (info)) {
720 case ResumeInitSelfResume:
721 resume_self_suspended (info);
724 case ResumeInitAsyncResume:
725 resume_async_suspended (info);
728 case ResumeInitBlockingResume:
729 resume_blocking_suspended (info);
738 mono_thread_info_resume (MonoNativeThreadId tid)
740 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
741 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
742 MonoThreadInfo *info;
744 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
746 mono_thread_info_suspend_lock ();
748 info = mono_thread_info_lookup (tid); /*info on HP1*/
754 result = mono_thread_info_core_resume (info);
756 //Wait for the pending resume to finish
757 mono_threads_wait_pending_operations ();
760 mono_thread_info_suspend_unlock ();
761 mono_hazard_pointer_clear (hp, 1);
766 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
768 switch (mono_threads_transition_request_async_suspension (info)) {
769 case AsyncSuspendAlreadySuspended:
771 case AsyncSuspendWait:
772 mono_threads_add_to_pending_operation_set (info);
774 case AsyncSuspendInitSuspend:
775 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
777 g_assert_not_reached ();
782 mono_thread_info_begin_resume (MonoThreadInfo *info)
784 return mono_thread_info_core_resume (info);
788 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
789 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
792 is_thread_in_critical_region (MonoThreadInfo *info)
796 gpointer stack_start;
797 MonoThreadUnwindState *state;
799 /* Are we inside a system critical region? */
800 if (info->inside_critical_region)
803 /* Are we inside a GC critical region? */
804 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
808 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
809 state = mono_thread_info_get_suspend_state (info);
810 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
813 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
814 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
815 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
818 ji = mono_jit_info_table_find (
819 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
820 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
825 method = mono_jit_info_get_method (ji);
827 return threads_callbacks.mono_method_is_critical (method);
831 mono_thread_info_in_critical_location (MonoThreadInfo *info)
833 return is_thread_in_critical_region (info);
836 static MonoThreadInfo*
837 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
839 MonoThreadInfo *info = NULL;
840 int sleep_duration = 0;
842 const char *suspend_error = "Unknown error";
843 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
844 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
848 /*WARNING: We now are in interrupt context until we resume the thread. */
849 if (!is_thread_in_critical_region (info))
852 if (!mono_thread_info_core_resume (info)) {
853 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
856 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
858 /* Wait for the pending resume to finish */
859 mono_threads_wait_pending_operations ();
861 if (!sleep_duration) {
869 g_usleep (sleep_duration);
871 sleep_duration += 10;
877 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
880 MonoThreadInfo *info = NULL;
881 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
883 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
884 /*FIXME: unify this with self-suspend*/
885 g_assert (id != mono_native_thread_id_get ());
887 mono_thread_info_suspend_lock ();
888 mono_threads_begin_global_suspend ();
890 info = suspend_sync_nolock (id, interrupt_kernel);
894 switch (result = callback (info, user_data)) {
895 case MonoResumeThread:
896 mono_hazard_pointer_set (hp, 1, info);
897 mono_thread_info_core_resume (info);
898 mono_threads_wait_pending_operations ();
903 g_error ("Invalid suspend_and_run callback return value %d", result);
907 mono_hazard_pointer_clear (hp, 1);
908 mono_threads_end_global_suspend ();
909 mono_thread_info_suspend_unlock ();
914 If we are trying to suspend a target that is on a critical region
915 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
916 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
918 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
920 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
923 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
925 MonoThreadInfo *info = NULL;
927 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
928 /*FIXME: unify this with self-suspend*/
929 g_assert (id != mono_native_thread_id_get ());
931 mono_thread_info_suspend_lock ();
932 mono_threads_begin_global_suspend ();
934 info = suspend_sync_nolock (id, interrupt_kernel);
936 /* XXX this clears HP 1, so we restated it again */
937 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
938 mono_threads_end_global_suspend ();
939 mono_thread_info_suspend_unlock ();
945 Inject an assynchronous call into the target thread. The target thread must be suspended and
946 only a single async call can be setup for a given suspend cycle.
947 This async call must cause stack unwinding as the current implementation doesn't save enough state
948 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
949 currently used only to deliver exceptions.
952 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
954 /* An async call can only be setup on an async suspended thread */
955 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
956 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
957 g_assert (!info->async_target);
958 info->async_target = target_func;
959 /* This is not GC tracked */
960 info->user_data = user_data;
964 The suspend lock is held during any suspend in progress.
965 A GC that has safepoints must take this lock as part of its
966 STW to make sure no unsafe pending suspend is in progress.
969 mono_thread_info_suspend_lock (void)
971 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
975 mono_thread_info_suspend_unlock (void)
977 MONO_SEM_POST (&global_suspend_semaphore);
981 * This is a very specific function whose only purpose is to
982 * break a given thread from socket syscalls.
984 * This only exists because linux won't fail a call to connect
985 * if the underlying is closed.
987 * TODO We should cleanup and unify this with the other syscall abort
991 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
993 MonoThreadHazardPointers *hp;
994 MonoThreadInfo *info;
996 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
999 hp = mono_hazard_pointer_get ();
1000 info = mono_thread_info_lookup (tid); /*info on HP1*/
1004 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
1005 mono_hazard_pointer_clear (hp, 1);
1009 mono_thread_info_suspend_lock ();
1010 mono_threads_begin_global_suspend ();
1012 mono_threads_core_abort_syscall (info);
1013 mono_threads_wait_pending_operations ();
1015 mono_hazard_pointer_clear (hp, 1);
1017 mono_threads_end_global_suspend ();
1018 mono_thread_info_suspend_unlock ();
1022 mono_thread_info_unified_management_enabled (void)
1024 return unified_suspend_enabled;
1028 * mono_thread_info_set_is_async_context:
1030 * Set whenever the current thread is in an async context. Some runtime functions might behave
1031 * differently while in an async context in order to be async safe.
1034 mono_thread_info_set_is_async_context (gboolean async_context)
1036 MonoThreadInfo *info = mono_thread_info_current ();
1039 info->is_async_context = async_context;
1043 mono_thread_info_is_async_context (void)
1045 MonoThreadInfo *info = mono_thread_info_current ();
1048 return info->is_async_context;
1054 * mono_threads_create_thread:
1056 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1057 * Returns: a windows or io-layer handle for the thread.
1060 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1062 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1066 * mono_thread_info_get_stack_bounds:
1068 * Return the address and size of the current threads stack. Return NULL as the
1069 * stack address if the stack address cannot be determined.
1072 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1074 guint8 *current = (guint8 *)&stsize;
1075 mono_threads_core_get_stack_bounds (staddr, stsize);
1079 /* Sanity check the result */
1080 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1082 /* When running under emacs, sometimes staddr is not aligned to a page size */
1083 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1087 mono_thread_info_yield (void)
1089 return mono_threads_core_yield ();
1093 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1095 return ((MonoThreadInfo*)info)->tls [key];
1099 * mono_threads_info_tls_set:
1101 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1102 * values of TLS variables for threads other than the current thread.
1103 * This should only be used for infrequently changing TLS variables, and it should
1104 * be paired with setting the real TLS variable since this provides no GC tracking.
1107 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1109 ((MonoThreadInfo*)info)->tls [key] = value;
1113 * mono_thread_info_exit:
1115 * Exit the current thread.
1116 * This function doesn't return.
1119 mono_thread_info_exit (void)
1121 mono_threads_core_exit (0);
1125 * mono_thread_info_open_handle:
1127 * Return a io-layer/win32 handle for the current thread.
1128 * The handle need to be closed by calling CloseHandle () when it is no
1132 mono_thread_info_open_handle (void)
1134 return mono_threads_core_open_handle ();
1138 * mono_threads_open_thread_handle:
1140 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1141 * The handle need to be closed by calling CloseHandle () when it is no
1145 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1147 return mono_threads_core_open_thread_handle (handle, tid);
1151 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1153 mono_threads_core_set_name (tid, name);
1156 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1158 struct _MonoThreadInfoInterruptToken {
1159 void (*callback) (gpointer data);
1164 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1166 * - @callback: must be able to be called from another thread and always cancel the wait
1167 * - @data: passed to the callback
1168 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1169 * if set to TRUE, it must mean that the thread is in interrupted state
1172 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1174 MonoThreadInfo *info;
1175 MonoThreadInfoInterruptToken *previous_token, *token;
1177 g_assert (callback);
1179 g_assert (interrupted);
1180 *interrupted = FALSE;
1182 info = mono_thread_info_current ();
1185 /* The memory of this token can be freed at 2 places:
1186 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1187 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1188 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1189 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1190 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1191 token->callback = callback;
1194 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1196 if (previous_token) {
1197 if (previous_token != INTERRUPT_STATE)
1198 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1202 *interrupted = TRUE;
1205 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1206 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1210 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1212 MonoThreadInfo *info;
1213 MonoThreadInfoInterruptToken *previous_token;
1215 g_assert (interrupted);
1216 *interrupted = FALSE;
1218 info = mono_thread_info_current ();
1221 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1223 /* only the installer can uninstall the token */
1224 g_assert (previous_token);
1226 if (previous_token == INTERRUPT_STATE) {
1227 /* if it is interrupted, then it is going to be freed in finish interrupt */
1228 *interrupted = TRUE;
1230 g_free (previous_token);
1233 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1234 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1237 static MonoThreadInfoInterruptToken*
1238 set_interrupt_state (MonoThreadInfo *info)
1240 MonoThreadInfoInterruptToken *token, *previous_token;
1244 /* Atomically obtain the token the thread is
1245 * waiting on, and change it to a flag value. */
1248 previous_token = info->interrupt_token;
1250 /* Already interrupted */
1251 if (previous_token == INTERRUPT_STATE) {
1256 token = previous_token;
1257 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1263 * mono_thread_info_prepare_interrupt:
1265 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1266 * - if the thread calls one of the WaitFor functions, the function will return with
1267 * WAIT_IO_COMPLETION instead of waiting
1268 * - if the thread was waiting when this function was called, the wait will be broken
1270 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1271 * didn't receive the interrupt signal yet, in this case it should call the wait function
1272 * again. This essentially means that the target thread will busy wait until it is ready to
1273 * process the interruption.
1275 MonoThreadInfoInterruptToken*
1276 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1278 MonoThreadInfoInterruptToken *token;
1280 token = set_interrupt_state (info);
1282 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1283 mono_thread_info_get_tid (info), token);
1289 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1291 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1296 g_assert (token->callback);
1298 token->callback (token->data);
1304 mono_thread_info_self_interrupt (void)
1306 MonoThreadInfo *info;
1307 MonoThreadInfoInterruptToken *token;
1309 info = mono_thread_info_current ();
1312 token = set_interrupt_state (info);
1315 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1316 mono_thread_info_get_tid (info));
1319 /* Clear the interrupted flag of the current thread, set with
1320 * mono_thread_info_self_interrupt, so it can wait again */
1322 mono_thread_info_clear_self_interrupt ()
1324 MonoThreadInfo *info;
1325 MonoThreadInfoInterruptToken *previous_token;
1327 info = mono_thread_info_current ();
1330 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1331 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1333 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1337 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1340 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1344 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1348 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1349 g_string_append_printf (text, "not waiting");
1350 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1351 g_string_append_printf (text, "interrupted state");
1353 g_string_append_printf (text, "waiting");
1356 /* info must be self or be held in a hazard pointer. */
1358 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1360 MonoAsyncJob old_job;
1362 old_job = (MonoAsyncJob) info->service_requests;
1365 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1370 mono_threads_consume_async_jobs (void)
1372 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1375 return (MonoAsyncJob) 0;
1377 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);