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)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 /* enable pthread extensions */
16 #define _DARWIN_C_SOURCE
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-os-semaphore.h>
21 #include <mono/utils/mono-threads.h>
22 #include <mono/utils/mono-tls.h>
23 #include <mono/utils/hazard-pointer.h>
24 #include <mono/utils/mono-memory-model.h>
25 #include <mono/utils/mono-mmap.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-time.h>
28 #include <mono/utils/mono-lazy-init.h>
29 #include <mono/utils/mono-coop-mutex.h>
30 #include <mono/utils/mono-coop-semaphore.h>
31 #include <mono/utils/mono-threads-coop.h>
36 #include <mono/utils/mach-support.h>
40 Mutex that makes sure only a single thread can be suspending others.
41 Suspend is a very racy operation since it requires restarting until
42 the target thread is not on an unsafe region.
44 We could implement this using critical regions, but would be much much
45 harder for an operation that is hardly performance critical.
47 The GC has to acquire this lock before starting a STW to make sure
48 a runtime suspend won't make it wronly see a thread in a safepoint
49 when it is in fact not.
51 This has to be a naked locking primitive, and not a coop aware one, as
52 it needs to be usable when destroying thread_info_key, the TLS key for
53 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
54 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
55 to an assertion error. We then simply switch state manually in
56 mono_thread_info_suspend_lock_with_info.
58 static MonoSemType global_suspend_semaphore;
60 static size_t thread_info_size;
61 static MonoThreadInfoCallbacks threads_callbacks;
62 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
63 static MonoNativeTlsKey thread_info_key, thread_exited_key;
65 static __thread guint32 tls_small_id MONO_TLS_FAST;
67 static MonoNativeTlsKey small_id_key;
69 static MonoLinkedListSet thread_list;
70 static gboolean mono_threads_inited = FALSE;
72 static MonoSemType suspend_semaphore;
73 static size_t pending_suspends;
74 static gboolean unified_suspend_enabled;
76 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
79 #define SLEEP_DURATION_BEFORE_WARNING (10)
81 #define SLEEP_DURATION_BEFORE_ABORT 200
83 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
86 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
88 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
89 InterlockedIncrement (&abort_posts);
90 mono_os_sem_post (&suspend_semaphore);
94 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
96 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
97 InterlockedIncrement (&suspend_posts);
98 mono_os_sem_post (&suspend_semaphore);
102 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
104 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
105 InterlockedIncrement (&resume_posts);
106 mono_os_sem_post (&suspend_semaphore);
110 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
112 if (mono_threads_is_coop_enabled ()) {
113 /* There's nothing else to do after we async request the thread to suspend */
114 mono_threads_add_to_pending_operation_set (info);
118 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
122 check_async_suspend (MonoThreadInfo *info)
124 if (mono_threads_is_coop_enabled ()) {
125 /* Async suspend can't async fail on coop */
129 return mono_threads_core_check_suspend_result (info);
133 resume_async_suspended (MonoThreadInfo *info)
135 if (mono_threads_is_coop_enabled ())
136 g_assert_not_reached ();
138 g_assert (mono_threads_core_begin_async_resume (info));
142 resume_self_suspended (MonoThreadInfo* info)
144 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
145 mono_os_sem_post (&info->resume_semaphore);
149 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
152 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
153 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
154 g_assert (res != -1);
158 resume_blocking_suspended (MonoThreadInfo* info)
160 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
161 mono_os_sem_post (&info->resume_semaphore);
165 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
167 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
169 InterlockedIncrement (&pending_ops);
173 mono_threads_begin_global_suspend (void)
175 size_t ps = pending_suspends;
176 if (G_UNLIKELY (ps != 0))
177 g_error ("pending_suspends = %d, but must be 0", ps);
178 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
179 abort_posts, waits_done, pending_ops);
180 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
181 mono_threads_coop_begin_global_suspend ();
185 mono_threads_end_global_suspend (void)
187 size_t ps = pending_suspends;
188 if (G_UNLIKELY (ps != 0))
189 g_error ("pending_suspends = %d, but must be 0", ps);
190 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
191 abort_posts, waits_done, pending_ops);
192 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
193 mono_threads_coop_end_global_suspend ();
199 MonoThreadInfo *cur = mono_thread_info_current ();
201 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
202 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
203 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
204 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
205 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
206 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
207 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
212 FOREACH_THREAD_SAFE (info) {
214 char thread_name [256] = { 0 };
215 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
217 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" : "" );
219 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" : "" );
221 } FOREACH_THREAD_SAFE_END
225 mono_threads_wait_pending_operations (void)
228 int c = pending_suspends;
230 /* Wait threads to park */
231 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
232 if (pending_suspends) {
233 MonoStopwatch suspension_time;
234 mono_stopwatch_start (&suspension_time);
235 for (i = 0; i < pending_suspends; ++i) {
236 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
237 InterlockedIncrement (&waits_done);
238 if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE))
240 mono_stopwatch_stop (&suspension_time);
244 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
245 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);
247 mono_stopwatch_stop (&suspension_time);
248 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
252 pending_suspends = 0;
258 //Thread initialization code
260 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
263 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
266 mono_hazard_pointer_clear (hp, 0);
268 mono_hazard_pointer_clear (hp, 1);
270 mono_hazard_pointer_clear (hp, 2);
274 If return non null Hazard Pointer 1 holds the return value.
277 mono_thread_info_lookup (MonoNativeThreadId id)
279 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
281 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
282 mono_hazard_pointer_clear_all (hp, -1);
286 mono_hazard_pointer_clear_all (hp, 1);
287 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
291 mono_thread_info_insert (MonoThreadInfo *info)
293 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
295 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
296 mono_hazard_pointer_clear_all (hp, -1);
300 mono_hazard_pointer_clear_all (hp, -1);
305 mono_thread_info_remove (MonoThreadInfo *info)
307 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
310 THREADS_DEBUG ("removing info %p\n", info);
311 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
312 mono_hazard_pointer_clear_all (hp, -1);
317 free_thread_info (gpointer mem)
319 MonoThreadInfo *info = (MonoThreadInfo *) mem;
321 mono_os_sem_destroy (&info->resume_semaphore);
322 mono_threads_platform_free (info);
328 mono_thread_info_register_small_id (void)
330 int small_id = mono_thread_small_id_alloc ();
331 #ifdef HAVE_KW_THREAD
332 tls_small_id = small_id;
334 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
340 register_thread (MonoThreadInfo *info, gpointer baseptr)
343 guint8 *staddr = NULL;
344 int small_id = mono_thread_info_register_small_id ();
346 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
347 info->small_id = small_id;
349 mono_os_sem_init (&info->resume_semaphore, 0);
351 /*set TLS early so SMR works */
352 mono_native_tls_set_value (thread_info_key, info);
354 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
356 if (threads_callbacks.thread_register) {
357 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
358 // g_warning ("thread registation failed\n");
364 mono_thread_info_get_stack_bounds (&staddr, &stsize);
367 info->stack_start_limit = staddr;
368 info->stack_end = staddr + stsize;
370 info->stackdata = g_byte_array_new ();
372 mono_threads_platform_register (info);
375 Transition it before taking any locks or publishing itself to reduce the chance
376 of others witnessing a detached thread.
377 We can reasonably expect that until this thread gets published, no other thread will
378 try to manipulate it.
380 mono_threads_transition_attach (info);
381 mono_thread_info_suspend_lock ();
382 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
383 result = mono_thread_info_insert (info);
385 mono_thread_info_suspend_unlock ();
390 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
393 unregister_thread (void *arg)
395 gpointer gc_unsafe_stackdata;
396 MonoThreadInfo *info;
399 info = (MonoThreadInfo *) arg;
402 small_id = info->small_id;
404 /* We only enter the GC unsafe region, as when exiting this function, the thread
405 * will be detached, and the current MonoThreadInfo* will be destroyed. */
406 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
408 THREADS_DEBUG ("unregistering info %p\n", info);
410 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
412 mono_threads_core_unregister (info);
415 * TLS destruction order is not reliable so small_id might be cleaned up
418 #ifndef HAVE_KW_THREAD
419 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
423 First perform the callback that requires no locks.
424 This callback has the potential of taking other locks, so we do it before.
425 After it completes, the thread remains functional.
427 if (threads_callbacks.thread_detach)
428 threads_callbacks.thread_detach (info);
430 mono_thread_info_suspend_lock_with_info (info);
433 Now perform the callback that must be done under locks.
434 This will render the thread useless and non-suspendable, so it must
435 be done while holding the suspend lock to give no other thread chance
438 if (threads_callbacks.thread_unregister)
439 threads_callbacks.thread_unregister (info);
440 mono_threads_unregister_current_thread (info);
441 mono_threads_transition_detach (info);
443 mono_thread_info_suspend_unlock ();
445 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
447 /*now it's safe to free the thread info.*/
448 mono_thread_hazardous_try_free (info, free_thread_info);
449 /* Pump the HP queue */
450 mono_thread_hazardous_try_free_some ();
452 mono_thread_small_id_free (small_id);
456 thread_exited_dtor (void *arg)
458 #if defined(__MACH__)
460 * Since we use pthread dtors to clean up thread data, if a thread
461 * is attached to the runtime by another pthread dtor after our dtor
462 * has ran, it will never be detached, leading to various problems
463 * since the thread ids etc. will be reused while they are still in
464 * the threads hashtables etc.
465 * Dtors are called in a loop until all user tls entries are 0,
466 * but the loop has a maximum count (4), so if we set the tls
467 * variable every time, it will remain set when system tls dtors
468 * are ran. This allows mono_thread_info_is_exiting () to detect
469 * whenever the thread is exiting, even if it is executed from a
470 * system tls dtor (i.e. obj-c dealloc methods).
472 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
477 * Removes the current thread from the thread list.
478 * This must be called from the thread unregister callback and nowhere else.
479 * The current thread must be passed as TLS might have already been cleaned up.
482 mono_threads_unregister_current_thread (MonoThreadInfo *info)
485 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
486 result = mono_thread_info_remove (info);
491 mono_thread_info_current_unchecked (void)
493 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
498 mono_thread_info_current (void)
500 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
504 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
507 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
508 The way to distinguish between before, during and after cleanup is the following:
510 -If the TLS key is set, cleanup has not begun;
511 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
512 -If the thread is nowhere to be found, cleanup has finished.
514 We cannot function after cleanup since there's no way to ensure what will happen.
518 /*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 */
519 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
525 mono_thread_info_get_small_id (void)
527 #ifdef HAVE_KW_THREAD
530 gpointer val = mono_native_tls_get_value (small_id_key);
533 return GPOINTER_TO_INT (val) - 1;
538 mono_thread_info_list_head (void)
544 * mono_threads_attach_tools_thread
546 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
548 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
549 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
552 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
553 * doing things like resolving backtraces in their background processing thread.
556 mono_threads_attach_tools_thread (void)
559 MonoThreadInfo *info;
561 /* Must only be called once */
562 g_assert (!mono_native_tls_get_value (thread_info_key));
564 while (!mono_threads_inited) {
565 mono_thread_info_usleep (10);
568 info = mono_thread_info_attach (&dummy);
571 info->tools_thread = TRUE;
575 mono_thread_info_attach (void *baseptr)
577 MonoThreadInfo *info;
578 if (!mono_threads_inited)
581 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
582 * thread is created before an embedding API user initialized Mono. */
583 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
586 g_assert (mono_threads_inited);
589 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
591 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
592 THREADS_DEBUG ("attaching %p\n", info);
593 if (!register_thread (info, baseptr))
595 } else if (threads_callbacks.thread_attach) {
596 threads_callbacks.thread_attach (info);
602 mono_thread_info_detach (void)
604 MonoThreadInfo *info;
605 if (!mono_threads_inited)
607 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
608 * is created before an embedding API user initialized Mono. */
609 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
612 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
614 THREADS_DEBUG ("detaching %p\n", info);
615 unregister_thread (info);
616 mono_native_tls_set_value (thread_info_key, NULL);
621 * mono_thread_info_is_exiting:
623 * Return whenever the current thread is exiting, i.e. it is running pthread
627 mono_thread_info_is_exiting (void)
629 #if defined(__MACH__)
630 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
637 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
640 threads_callbacks = *callbacks;
641 thread_info_size = info_size;
643 res = mono_native_tls_alloc (&thread_info_key, NULL);
644 res = mono_native_tls_alloc (&thread_exited_key, NULL);
646 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
647 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
652 #ifndef HAVE_KW_THREAD
653 res = mono_native_tls_alloc (&small_id_key, NULL);
657 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
659 mono_os_sem_init (&global_suspend_semaphore, 1);
660 mono_os_sem_init (&suspend_semaphore, 0);
662 mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
663 mono_thread_smr_init ();
664 mono_threads_init_platform ();
665 mono_threads_init_coop ();
666 mono_threads_init_abort_syscall ();
668 #if defined(__MACH__)
669 mono_mach_init (thread_info_key);
672 mono_threads_inited = TRUE;
674 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
678 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
680 runtime_callbacks = *callbacks;
683 MonoThreadInfoRuntimeCallbacks *
684 mono_threads_get_runtime_callbacks (void)
686 return &runtime_callbacks;
690 Signal that the current thread wants to be suspended.
691 This function can be called without holding the suspend lock held.
692 To finish suspending, call mono_suspend_check.
695 mono_thread_info_begin_self_suspend (void)
697 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
701 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
702 mono_threads_transition_request_self_suspension (info);
706 mono_thread_info_end_self_suspend (void)
708 MonoThreadInfo *info;
710 info = mono_thread_info_current ();
713 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
715 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
717 /* commit the saved state and notify others if needed */
718 switch (mono_threads_transition_state_poll (info)) {
719 case SelfSuspendResumed:
721 case SelfSuspendWait:
722 mono_thread_info_wait_for_resume (info);
724 case SelfSuspendNotifyAndWait:
725 mono_threads_notify_initiator_of_suspend (info);
726 mono_thread_info_wait_for_resume (info);
727 mono_threads_notify_initiator_of_resume (info);
733 mono_thread_info_core_resume (MonoThreadInfo *info)
735 gboolean res = FALSE;
736 if (info->create_suspended) {
737 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
738 /* 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 */
739 info->create_suspended = FALSE;
740 mono_threads_core_resume_created (info, tid);
744 switch (mono_threads_transition_request_resume (info)) {
751 case ResumeInitSelfResume:
752 resume_self_suspended (info);
755 case ResumeInitAsyncResume:
756 resume_async_suspended (info);
759 case ResumeInitBlockingResume:
760 resume_blocking_suspended (info);
769 mono_thread_info_resume (MonoNativeThreadId tid)
771 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
772 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
773 MonoThreadInfo *info;
775 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
777 mono_thread_info_suspend_lock ();
779 info = mono_thread_info_lookup (tid); /*info on HP1*/
785 result = mono_thread_info_core_resume (info);
787 //Wait for the pending resume to finish
788 mono_threads_wait_pending_operations ();
791 mono_thread_info_suspend_unlock ();
792 mono_hazard_pointer_clear (hp, 1);
797 mono_thread_info_begin_suspend (MonoThreadInfo *info)
799 switch (mono_threads_transition_request_async_suspension (info)) {
800 case AsyncSuspendAlreadySuspended:
801 case AsyncSuspendBlocking:
803 case AsyncSuspendWait:
804 mono_threads_add_to_pending_operation_set (info);
806 case AsyncSuspendInitSuspend:
807 return begin_async_suspend (info, FALSE);
809 g_assert_not_reached ();
814 mono_thread_info_begin_resume (MonoThreadInfo *info)
816 return mono_thread_info_core_resume (info);
820 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
821 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
824 is_thread_in_critical_region (MonoThreadInfo *info)
828 gpointer stack_start;
829 MonoThreadUnwindState *state;
831 /* Are we inside a system critical region? */
832 if (info->inside_critical_region)
835 /* Are we inside a GC critical region? */
836 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
840 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
841 state = mono_thread_info_get_suspend_state (info);
842 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
845 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
846 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
847 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
850 ji = mono_jit_info_table_find (
851 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
852 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
857 method = mono_jit_info_get_method (ji);
859 return threads_callbacks.mono_method_is_critical (method);
863 mono_thread_info_in_critical_location (MonoThreadInfo *info)
865 return is_thread_in_critical_region (info);
869 The return value is only valid until a matching mono_thread_info_resume is called
871 static MonoThreadInfo*
872 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
874 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
875 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
879 switch (mono_threads_transition_request_async_suspension (info)) {
880 case AsyncSuspendAlreadySuspended:
881 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
883 case AsyncSuspendWait:
884 mono_threads_add_to_pending_operation_set (info);
886 case AsyncSuspendInitSuspend:
887 if (!begin_async_suspend (info, interrupt_kernel)) {
888 mono_hazard_pointer_clear (hp, 1);
892 case AsyncSuspendBlocking:
893 if (interrupt_kernel)
894 mono_threads_core_abort_syscall (info);
898 g_assert_not_reached ();
901 //Wait for the pending suspend to finish
902 mono_threads_wait_pending_operations ();
904 if (!check_async_suspend (info)) {
905 mono_thread_info_core_resume (info);
906 mono_threads_wait_pending_operations ();
907 mono_hazard_pointer_clear (hp, 1);
913 static MonoThreadInfo*
914 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
916 MonoThreadInfo *info = NULL;
917 int sleep_duration = 0;
919 if (!(info = suspend_sync (id, interrupt_kernel))) {
920 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
924 /*WARNING: We now are in interrupt context until we resume the thread. */
925 if (!is_thread_in_critical_region (info))
928 if (!mono_thread_info_core_resume (info)) {
929 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
932 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
934 /* Wait for the pending resume to finish */
935 mono_threads_wait_pending_operations ();
937 if (sleep_duration == 0)
938 mono_thread_info_yield ();
940 g_usleep (sleep_duration);
942 sleep_duration += 10;
948 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
951 MonoThreadInfo *info = NULL;
952 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
954 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
955 /*FIXME: unify this with self-suspend*/
956 g_assert (id != mono_native_thread_id_get ());
958 /* This can block during stw */
959 mono_thread_info_suspend_lock ();
960 mono_threads_begin_global_suspend ();
962 info = suspend_sync_nolock (id, interrupt_kernel);
966 switch (result = callback (info, user_data)) {
967 case MonoResumeThread:
968 mono_hazard_pointer_set (hp, 1, info);
969 mono_thread_info_core_resume (info);
970 mono_threads_wait_pending_operations ();
975 g_error ("Invalid suspend_and_run callback return value %d", result);
979 mono_hazard_pointer_clear (hp, 1);
980 mono_threads_end_global_suspend ();
981 mono_thread_info_suspend_unlock ();
985 Inject an assynchronous call into the target thread. The target thread must be suspended and
986 only a single async call can be setup for a given suspend cycle.
987 This async call must cause stack unwinding as the current implementation doesn't save enough state
988 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
989 currently used only to deliver exceptions.
992 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
994 /* An async call can only be setup on an async suspended thread */
995 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
996 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
997 g_assert (!info->async_target);
998 info->async_target = target_func;
999 /* This is not GC tracked */
1000 info->user_data = user_data;
1004 The suspend lock is held during any suspend in progress.
1005 A GC that has safepoints must take this lock as part of its
1006 STW to make sure no unsafe pending suspend is in progress.
1010 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1014 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1016 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1017 g_assert (res != -1);
1019 MONO_EXIT_GC_SAFE_WITH_INFO;
1023 mono_thread_info_suspend_lock (void)
1025 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1029 mono_thread_info_suspend_unlock (void)
1031 mono_os_sem_post (&global_suspend_semaphore);
1035 * This is a very specific function whose only purpose is to
1036 * break a given thread from socket syscalls.
1038 * This only exists because linux won't fail a call to connect
1039 * if the underlying is closed.
1041 * TODO We should cleanup and unify this with the other syscall abort
1045 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1047 MonoThreadHazardPointers *hp;
1048 MonoThreadInfo *info;
1050 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1053 hp = mono_hazard_pointer_get ();
1054 info = mono_thread_info_lookup (tid);
1058 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1059 mono_hazard_pointer_clear (hp, 1);
1063 mono_thread_info_suspend_lock ();
1064 mono_threads_begin_global_suspend ();
1066 mono_threads_core_abort_syscall (info);
1067 mono_threads_wait_pending_operations ();
1069 mono_hazard_pointer_clear (hp, 1);
1071 mono_threads_end_global_suspend ();
1072 mono_thread_info_suspend_unlock ();
1076 mono_thread_info_unified_management_enabled (void)
1078 return unified_suspend_enabled;
1082 * mono_thread_info_set_is_async_context:
1084 * Set whenever the current thread is in an async context. Some runtime functions might behave
1085 * differently while in an async context in order to be async safe.
1088 mono_thread_info_set_is_async_context (gboolean async_context)
1090 MonoThreadInfo *info = mono_thread_info_current ();
1093 info->is_async_context = async_context;
1097 mono_thread_info_is_async_context (void)
1099 MonoThreadInfo *info = mono_thread_info_current ();
1102 return info->is_async_context;
1108 * mono_threads_create_thread:
1110 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1111 * Returns: a windows or io-layer handle for the thread.
1114 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1116 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1120 * mono_thread_info_get_stack_bounds:
1122 * Return the address and size of the current threads stack. Return NULL as the
1123 * stack address if the stack address cannot be determined.
1126 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1128 guint8 *current = (guint8 *)&stsize;
1129 mono_threads_core_get_stack_bounds (staddr, stsize);
1133 /* Sanity check the result */
1134 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1136 /* When running under emacs, sometimes staddr is not aligned to a page size */
1137 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1141 mono_thread_info_yield (void)
1143 return mono_threads_core_yield ();
1145 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1146 static MonoCoopMutex sleep_mutex;
1147 static MonoCoopCond sleep_cond;
1150 sleep_initialize (void)
1152 mono_coop_mutex_init (&sleep_mutex);
1153 mono_coop_cond_init (&sleep_cond);
1157 sleep_interrupt (gpointer data)
1159 mono_coop_mutex_lock (&sleep_mutex);
1160 mono_coop_cond_broadcast (&sleep_cond);
1161 mono_coop_mutex_unlock (&sleep_mutex);
1164 static inline guint32
1165 sleep_interruptable (guint32 ms, gboolean *alerted)
1169 g_assert (INFINITE == G_MAXUINT32);
1175 end = mono_100ns_ticks () + (ms * 1000 * 10);
1177 mono_lazy_initialize (&sleep_init, sleep_initialize);
1179 mono_coop_mutex_lock (&sleep_mutex);
1182 if (ms != INFINITE) {
1183 now = mono_100ns_ticks ();
1188 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1190 mono_coop_mutex_unlock (&sleep_mutex);
1191 return WAIT_IO_COMPLETION;
1195 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1197 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1199 mono_thread_info_uninstall_interrupt (alerted);
1201 mono_coop_mutex_unlock (&sleep_mutex);
1202 return WAIT_IO_COMPLETION;
1206 mono_coop_mutex_unlock (&sleep_mutex);
1212 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1215 MonoThreadInfo *info;
1217 mono_thread_info_yield ();
1219 info = mono_thread_info_current ();
1220 if (info && mono_thread_info_is_interrupt_state (info))
1221 return WAIT_IO_COMPLETION;
1227 return sleep_interruptable (ms, alerted);
1231 if (ms == INFINITE) {
1234 Sleep (G_MAXUINT32);
1236 sleep (G_MAXUINT32);
1241 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1242 struct timespec start, target;
1244 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1245 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1246 g_assert (ret == 0);
1249 target.tv_sec += ms / 1000;
1250 target.tv_nsec += (ms % 1000) * 1000000;
1251 if (target.tv_nsec > 999999999) {
1252 target.tv_nsec -= 999999999;
1257 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1262 struct timespec req, rem;
1264 req.tv_sec = ms / 1000;
1265 req.tv_nsec = (ms % 1000) * 1000000;
1268 memset (&rem, 0, sizeof (rem));
1269 ret = nanosleep (&req, &rem);
1271 #endif /* __linux__ */
1280 mono_thread_info_usleep (guint64 us)
1289 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1291 return ((MonoThreadInfo*)info)->tls [key];
1295 * mono_threads_info_tls_set:
1297 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1298 * values of TLS variables for threads other than the current thread.
1299 * This should only be used for infrequently changing TLS variables, and it should
1300 * be paired with setting the real TLS variable since this provides no GC tracking.
1303 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1305 ((MonoThreadInfo*)info)->tls [key] = value;
1309 * mono_thread_info_exit:
1311 * Exit the current thread.
1312 * This function doesn't return.
1315 mono_thread_info_exit (void)
1317 mono_threads_core_exit (0);
1321 * mono_thread_info_open_handle:
1323 * Return a io-layer/win32 handle for the current thread.
1324 * The handle need to be closed by calling CloseHandle () when it is no
1328 mono_thread_info_open_handle (void)
1330 return mono_threads_core_open_handle ();
1334 * mono_threads_open_thread_handle:
1336 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1337 * The handle need to be closed by calling CloseHandle () when it is no
1341 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1343 return mono_threads_core_open_thread_handle (handle, tid);
1346 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1348 struct _MonoThreadInfoInterruptToken {
1349 void (*callback) (gpointer data);
1354 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1356 * - @callback: must be able to be called from another thread and always cancel the wait
1357 * - @data: passed to the callback
1358 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1359 * if set to TRUE, it must mean that the thread is in interrupted state
1362 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1364 MonoThreadInfo *info;
1365 MonoThreadInfoInterruptToken *previous_token, *token;
1367 g_assert (callback);
1369 g_assert (interrupted);
1370 *interrupted = FALSE;
1372 info = mono_thread_info_current ();
1375 /* The memory of this token can be freed at 2 places:
1376 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1377 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1378 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1379 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1380 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1381 token->callback = callback;
1384 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1386 if (previous_token) {
1387 if (previous_token != INTERRUPT_STATE)
1388 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1392 *interrupted = TRUE;
1395 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1396 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1400 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1402 MonoThreadInfo *info;
1403 MonoThreadInfoInterruptToken *previous_token;
1405 g_assert (interrupted);
1406 *interrupted = FALSE;
1408 info = mono_thread_info_current ();
1411 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1413 /* only the installer can uninstall the token */
1414 g_assert (previous_token);
1416 if (previous_token == INTERRUPT_STATE) {
1417 /* if it is interrupted, then it is going to be freed in finish interrupt */
1418 *interrupted = TRUE;
1420 g_free (previous_token);
1423 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1424 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1427 static MonoThreadInfoInterruptToken*
1428 set_interrupt_state (MonoThreadInfo *info)
1430 MonoThreadInfoInterruptToken *token, *previous_token;
1434 /* Atomically obtain the token the thread is
1435 * waiting on, and change it to a flag value. */
1438 previous_token = info->interrupt_token;
1440 /* Already interrupted */
1441 if (previous_token == INTERRUPT_STATE) {
1446 token = previous_token;
1447 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1453 * mono_thread_info_prepare_interrupt:
1455 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1456 * - if the thread calls one of the WaitFor functions, the function will return with
1457 * WAIT_IO_COMPLETION instead of waiting
1458 * - if the thread was waiting when this function was called, the wait will be broken
1460 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1461 * didn't receive the interrupt signal yet, in this case it should call the wait function
1462 * again. This essentially means that the target thread will busy wait until it is ready to
1463 * process the interruption.
1465 MonoThreadInfoInterruptToken*
1466 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1468 MonoThreadInfoInterruptToken *token;
1470 token = set_interrupt_state (info);
1472 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1473 mono_thread_info_get_tid (info), token);
1479 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1481 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1486 g_assert (token->callback);
1488 token->callback (token->data);
1494 mono_thread_info_self_interrupt (void)
1496 MonoThreadInfo *info;
1497 MonoThreadInfoInterruptToken *token;
1499 info = mono_thread_info_current ();
1502 token = set_interrupt_state (info);
1505 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1506 mono_thread_info_get_tid (info));
1509 /* Clear the interrupted flag of the current thread, set with
1510 * mono_thread_info_self_interrupt, so it can wait again */
1512 mono_thread_info_clear_self_interrupt ()
1514 MonoThreadInfo *info;
1515 MonoThreadInfoInterruptToken *previous_token;
1517 info = mono_thread_info_current ();
1520 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1521 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1523 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1527 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1530 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1534 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1538 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1539 g_string_append_printf (text, "not waiting");
1540 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1541 g_string_append_printf (text, "interrupted state");
1543 g_string_append_printf (text, "waiting");
1547 mono_thread_info_is_current (MonoThreadInfo *info)
1549 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();