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 long sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
84 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
86 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
89 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
91 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
92 InterlockedIncrement (&abort_posts);
93 mono_os_sem_post (&suspend_semaphore);
97 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
99 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
100 InterlockedIncrement (&suspend_posts);
101 mono_os_sem_post (&suspend_semaphore);
105 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
107 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
108 InterlockedIncrement (&resume_posts);
109 mono_os_sem_post (&suspend_semaphore);
113 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
115 if (mono_threads_is_coop_enabled ()) {
116 /* There's nothing else to do after we async request the thread to suspend */
117 mono_threads_add_to_pending_operation_set (info);
121 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
125 check_async_suspend (MonoThreadInfo *info)
127 if (mono_threads_is_coop_enabled ()) {
128 /* Async suspend can't async fail on coop */
132 return mono_threads_core_check_suspend_result (info);
136 resume_async_suspended (MonoThreadInfo *info)
138 if (mono_threads_is_coop_enabled ())
139 g_assert_not_reached ();
141 g_assert (mono_threads_core_begin_async_resume (info));
145 resume_self_suspended (MonoThreadInfo* info)
147 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
148 mono_os_sem_post (&info->resume_semaphore);
152 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
155 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
156 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
157 g_assert (res != -1);
161 resume_blocking_suspended (MonoThreadInfo* info)
163 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
164 mono_os_sem_post (&info->resume_semaphore);
168 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
170 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
172 InterlockedIncrement (&pending_ops);
176 mono_threads_begin_global_suspend (void)
178 size_t ps = pending_suspends;
179 if (G_UNLIKELY (ps != 0))
180 g_error ("pending_suspends = %d, but must be 0", ps);
181 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
182 abort_posts, waits_done, pending_ops);
183 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
184 mono_threads_coop_begin_global_suspend ();
188 mono_threads_end_global_suspend (void)
190 size_t ps = pending_suspends;
191 if (G_UNLIKELY (ps != 0))
192 g_error ("pending_suspends = %d, but must be 0", ps);
193 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
194 abort_posts, waits_done, pending_ops);
195 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
196 mono_threads_coop_end_global_suspend ();
202 MonoThreadInfo *cur = mono_thread_info_current ();
204 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
205 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
206 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
207 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
215 FOREACH_THREAD_SAFE (info) {
217 char thread_name [256] = { 0 };
218 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
220 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" : "" );
222 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" : "" );
224 } FOREACH_THREAD_SAFE_END
228 mono_threads_wait_pending_operations (void)
231 int c = pending_suspends;
233 /* Wait threads to park */
234 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
235 if (pending_suspends) {
236 MonoStopwatch suspension_time;
237 mono_stopwatch_start (&suspension_time);
238 for (i = 0; i < pending_suspends; ++i) {
239 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
240 InterlockedIncrement (&waits_done);
241 if (!mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE))
243 mono_stopwatch_stop (&suspension_time);
247 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
248 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
250 mono_stopwatch_stop (&suspension_time);
251 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
255 pending_suspends = 0;
261 //Thread initialization code
263 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
266 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
269 mono_hazard_pointer_clear (hp, 0);
271 mono_hazard_pointer_clear (hp, 1);
273 mono_hazard_pointer_clear (hp, 2);
277 If return non null Hazard Pointer 1 holds the return value.
280 mono_thread_info_lookup (MonoNativeThreadId id)
282 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
284 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
285 mono_hazard_pointer_clear_all (hp, -1);
289 mono_hazard_pointer_clear_all (hp, 1);
290 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
294 mono_thread_info_insert (MonoThreadInfo *info)
296 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
298 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
299 mono_hazard_pointer_clear_all (hp, -1);
303 mono_hazard_pointer_clear_all (hp, -1);
308 mono_thread_info_remove (MonoThreadInfo *info)
310 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
313 THREADS_DEBUG ("removing info %p\n", info);
314 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
315 mono_hazard_pointer_clear_all (hp, -1);
320 free_thread_info (gpointer mem)
322 MonoThreadInfo *info = (MonoThreadInfo *) mem;
324 mono_os_sem_destroy (&info->resume_semaphore);
325 mono_threads_platform_free (info);
331 mono_thread_info_register_small_id (void)
333 int small_id = mono_thread_small_id_alloc ();
334 #ifdef HAVE_KW_THREAD
335 tls_small_id = small_id;
337 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
343 register_thread (MonoThreadInfo *info, gpointer baseptr)
346 guint8 *staddr = NULL;
347 int small_id = mono_thread_info_register_small_id ();
349 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
350 info->small_id = small_id;
352 mono_os_sem_init (&info->resume_semaphore, 0);
354 /*set TLS early so SMR works */
355 mono_native_tls_set_value (thread_info_key, info);
357 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
359 if (threads_callbacks.thread_register) {
360 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
361 // g_warning ("thread registation failed\n");
367 mono_thread_info_get_stack_bounds (&staddr, &stsize);
370 info->stack_start_limit = staddr;
371 info->stack_end = staddr + stsize;
373 info->stackdata = g_byte_array_new ();
375 mono_threads_platform_register (info);
378 Transition it before taking any locks or publishing itself to reduce the chance
379 of others witnessing a detached thread.
380 We can reasonably expect that until this thread gets published, no other thread will
381 try to manipulate it.
383 mono_threads_transition_attach (info);
384 mono_thread_info_suspend_lock ();
385 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
386 result = mono_thread_info_insert (info);
388 mono_thread_info_suspend_unlock ();
393 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
396 unregister_thread (void *arg)
398 gpointer gc_unsafe_stackdata;
399 MonoThreadInfo *info;
402 info = (MonoThreadInfo *) arg;
405 small_id = info->small_id;
407 /* We only enter the GC unsafe region, as when exiting this function, the thread
408 * will be detached, and the current MonoThreadInfo* will be destroyed. */
409 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
411 THREADS_DEBUG ("unregistering info %p\n", info);
413 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
415 mono_threads_core_unregister (info);
418 * TLS destruction order is not reliable so small_id might be cleaned up
421 #ifndef HAVE_KW_THREAD
422 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
426 First perform the callback that requires no locks.
427 This callback has the potential of taking other locks, so we do it before.
428 After it completes, the thread remains functional.
430 if (threads_callbacks.thread_detach)
431 threads_callbacks.thread_detach (info);
433 mono_thread_info_suspend_lock_with_info (info);
436 Now perform the callback that must be done under locks.
437 This will render the thread useless and non-suspendable, so it must
438 be done while holding the suspend lock to give no other thread chance
441 if (threads_callbacks.thread_unregister)
442 threads_callbacks.thread_unregister (info);
443 mono_threads_unregister_current_thread (info);
444 mono_threads_transition_detach (info);
446 mono_thread_info_suspend_unlock ();
448 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
450 /*now it's safe to free the thread info.*/
451 mono_thread_hazardous_try_free (info, free_thread_info);
452 /* Pump the HP queue */
453 mono_thread_hazardous_try_free_some ();
455 mono_thread_small_id_free (small_id);
459 thread_exited_dtor (void *arg)
461 #if defined(__MACH__)
463 * Since we use pthread dtors to clean up thread data, if a thread
464 * is attached to the runtime by another pthread dtor after our dtor
465 * has ran, it will never be detached, leading to various problems
466 * since the thread ids etc. will be reused while they are still in
467 * the threads hashtables etc.
468 * Dtors are called in a loop until all user tls entries are 0,
469 * but the loop has a maximum count (4), so if we set the tls
470 * variable every time, it will remain set when system tls dtors
471 * are ran. This allows mono_thread_info_is_exiting () to detect
472 * whenever the thread is exiting, even if it is executed from a
473 * system tls dtor (i.e. obj-c dealloc methods).
475 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
480 * Removes the current thread from the thread list.
481 * This must be called from the thread unregister callback and nowhere else.
482 * The current thread must be passed as TLS might have already been cleaned up.
485 mono_threads_unregister_current_thread (MonoThreadInfo *info)
488 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
489 result = mono_thread_info_remove (info);
494 mono_thread_info_current_unchecked (void)
496 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
501 mono_thread_info_current (void)
503 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
507 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
510 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
511 The way to distinguish between before, during and after cleanup is the following:
513 -If the TLS key is set, cleanup has not begun;
514 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
515 -If the thread is nowhere to be found, cleanup has finished.
517 We cannot function after cleanup since there's no way to ensure what will happen.
521 /*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 */
522 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
528 mono_thread_info_get_small_id (void)
530 #ifdef HAVE_KW_THREAD
533 gpointer val = mono_native_tls_get_value (small_id_key);
536 return GPOINTER_TO_INT (val) - 1;
541 mono_thread_info_list_head (void)
547 * mono_threads_attach_tools_thread
549 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
551 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
552 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
555 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
556 * doing things like resolving backtraces in their background processing thread.
559 mono_threads_attach_tools_thread (void)
562 MonoThreadInfo *info;
564 /* Must only be called once */
565 g_assert (!mono_native_tls_get_value (thread_info_key));
567 while (!mono_threads_inited) {
568 mono_thread_info_usleep (10);
571 info = mono_thread_info_attach (&dummy);
574 info->tools_thread = TRUE;
578 mono_thread_info_attach (void *baseptr)
580 MonoThreadInfo *info;
581 if (!mono_threads_inited)
584 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
585 * thread is created before an embedding API user initialized Mono. */
586 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
589 g_assert (mono_threads_inited);
592 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
594 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
595 THREADS_DEBUG ("attaching %p\n", info);
596 if (!register_thread (info, baseptr))
598 } else if (threads_callbacks.thread_attach) {
599 threads_callbacks.thread_attach (info);
605 mono_thread_info_detach (void)
607 MonoThreadInfo *info;
608 if (!mono_threads_inited)
610 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
611 * is created before an embedding API user initialized Mono. */
612 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
615 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
617 THREADS_DEBUG ("detaching %p\n", info);
618 unregister_thread (info);
619 mono_native_tls_set_value (thread_info_key, NULL);
624 * mono_thread_info_is_exiting:
626 * Return whenever the current thread is exiting, i.e. it is running pthread
630 mono_thread_info_is_exiting (void)
632 #if defined(__MACH__)
633 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
640 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
643 threads_callbacks = *callbacks;
644 thread_info_size = info_size;
647 res = mono_native_tls_alloc (&thread_info_key, NULL);
648 res = mono_native_tls_alloc (&thread_exited_key, NULL);
650 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
651 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
656 #ifndef HAVE_KW_THREAD
657 res = mono_native_tls_alloc (&small_id_key, NULL);
661 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
663 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
664 long threshold = strtol(sleepLimit, NULL, 10);
665 if ((errno == 0) && (threshold >= 40)) {
666 sleepAbortDuration = threshold;
667 sleepWarnDuration = threshold / 20;
669 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
672 mono_os_sem_init (&global_suspend_semaphore, 1);
673 mono_os_sem_init (&suspend_semaphore, 0);
675 mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
676 mono_thread_smr_init ();
677 mono_threads_init_platform ();
678 mono_threads_init_coop ();
679 mono_threads_init_abort_syscall ();
681 #if defined(__MACH__)
682 mono_mach_init (thread_info_key);
685 mono_threads_inited = TRUE;
687 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
691 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
693 runtime_callbacks = *callbacks;
696 MonoThreadInfoRuntimeCallbacks *
697 mono_threads_get_runtime_callbacks (void)
699 return &runtime_callbacks;
703 Signal that the current thread wants to be suspended.
704 This function can be called without holding the suspend lock held.
705 To finish suspending, call mono_suspend_check.
708 mono_thread_info_begin_self_suspend (void)
710 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
714 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
715 mono_threads_transition_request_self_suspension (info);
719 mono_thread_info_end_self_suspend (void)
721 MonoThreadInfo *info;
723 info = mono_thread_info_current ();
726 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
728 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
730 /* commit the saved state and notify others if needed */
731 switch (mono_threads_transition_state_poll (info)) {
732 case SelfSuspendResumed:
734 case SelfSuspendWait:
735 mono_thread_info_wait_for_resume (info);
737 case SelfSuspendNotifyAndWait:
738 mono_threads_notify_initiator_of_suspend (info);
739 mono_thread_info_wait_for_resume (info);
740 mono_threads_notify_initiator_of_resume (info);
746 mono_thread_info_core_resume (MonoThreadInfo *info)
748 gboolean res = FALSE;
749 if (info->create_suspended) {
750 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
751 /* 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 */
752 info->create_suspended = FALSE;
753 mono_threads_core_resume_created (info, tid);
757 switch (mono_threads_transition_request_resume (info)) {
764 case ResumeInitSelfResume:
765 resume_self_suspended (info);
768 case ResumeInitAsyncResume:
769 resume_async_suspended (info);
772 case ResumeInitBlockingResume:
773 resume_blocking_suspended (info);
782 mono_thread_info_resume (MonoNativeThreadId tid)
784 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
785 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
786 MonoThreadInfo *info;
788 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
790 mono_thread_info_suspend_lock ();
792 info = mono_thread_info_lookup (tid); /*info on HP1*/
798 result = mono_thread_info_core_resume (info);
800 //Wait for the pending resume to finish
801 mono_threads_wait_pending_operations ();
804 mono_thread_info_suspend_unlock ();
805 mono_hazard_pointer_clear (hp, 1);
810 mono_thread_info_begin_suspend (MonoThreadInfo *info)
812 switch (mono_threads_transition_request_async_suspension (info)) {
813 case AsyncSuspendAlreadySuspended:
814 case AsyncSuspendBlocking:
816 case AsyncSuspendWait:
817 mono_threads_add_to_pending_operation_set (info);
819 case AsyncSuspendInitSuspend:
820 return begin_async_suspend (info, FALSE);
822 g_assert_not_reached ();
827 mono_thread_info_begin_resume (MonoThreadInfo *info)
829 return mono_thread_info_core_resume (info);
833 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
834 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
837 is_thread_in_critical_region (MonoThreadInfo *info)
841 gpointer stack_start;
842 MonoThreadUnwindState *state;
844 /* Are we inside a system critical region? */
845 if (info->inside_critical_region)
848 /* Are we inside a GC critical region? */
849 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
853 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
854 state = mono_thread_info_get_suspend_state (info);
855 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
858 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
859 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
860 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
863 ji = mono_jit_info_table_find (
864 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
865 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
870 method = mono_jit_info_get_method (ji);
872 return threads_callbacks.mono_method_is_critical (method);
876 mono_thread_info_in_critical_location (MonoThreadInfo *info)
878 return is_thread_in_critical_region (info);
882 The return value is only valid until a matching mono_thread_info_resume is called
884 static MonoThreadInfo*
885 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
887 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
888 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
892 switch (mono_threads_transition_request_async_suspension (info)) {
893 case AsyncSuspendAlreadySuspended:
894 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
896 case AsyncSuspendWait:
897 mono_threads_add_to_pending_operation_set (info);
899 case AsyncSuspendInitSuspend:
900 if (!begin_async_suspend (info, interrupt_kernel)) {
901 mono_hazard_pointer_clear (hp, 1);
905 case AsyncSuspendBlocking:
906 if (interrupt_kernel)
907 mono_threads_core_abort_syscall (info);
911 g_assert_not_reached ();
914 //Wait for the pending suspend to finish
915 mono_threads_wait_pending_operations ();
917 if (!check_async_suspend (info)) {
918 mono_thread_info_core_resume (info);
919 mono_threads_wait_pending_operations ();
920 mono_hazard_pointer_clear (hp, 1);
926 static MonoThreadInfo*
927 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
929 MonoThreadInfo *info = NULL;
930 int sleep_duration = 0;
932 if (!(info = suspend_sync (id, interrupt_kernel))) {
933 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
937 /*WARNING: We now are in interrupt context until we resume the thread. */
938 if (!is_thread_in_critical_region (info))
941 if (!mono_thread_info_core_resume (info)) {
942 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
945 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
947 /* Wait for the pending resume to finish */
948 mono_threads_wait_pending_operations ();
950 if (sleep_duration == 0)
951 mono_thread_info_yield ();
953 g_usleep (sleep_duration);
955 sleep_duration += 10;
961 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
964 MonoThreadInfo *info = NULL;
965 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
967 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
968 /*FIXME: unify this with self-suspend*/
969 g_assert (id != mono_native_thread_id_get ());
971 /* This can block during stw */
972 mono_thread_info_suspend_lock ();
973 mono_threads_begin_global_suspend ();
975 info = suspend_sync_nolock (id, interrupt_kernel);
979 switch (result = callback (info, user_data)) {
980 case MonoResumeThread:
981 mono_hazard_pointer_set (hp, 1, info);
982 mono_thread_info_core_resume (info);
983 mono_threads_wait_pending_operations ();
988 g_error ("Invalid suspend_and_run callback return value %d", result);
992 mono_hazard_pointer_clear (hp, 1);
993 mono_threads_end_global_suspend ();
994 mono_thread_info_suspend_unlock ();
998 Inject an assynchronous call into the target thread. The target thread must be suspended and
999 only a single async call can be setup for a given suspend cycle.
1000 This async call must cause stack unwinding as the current implementation doesn't save enough state
1001 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1002 currently used only to deliver exceptions.
1005 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1007 /* An async call can only be setup on an async suspended thread */
1008 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1009 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1010 g_assert (!info->async_target);
1011 info->async_target = target_func;
1012 /* This is not GC tracked */
1013 info->user_data = user_data;
1017 The suspend lock is held during any suspend in progress.
1018 A GC that has safepoints must take this lock as part of its
1019 STW to make sure no unsafe pending suspend is in progress.
1023 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1027 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1029 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1030 g_assert (res != -1);
1032 MONO_EXIT_GC_SAFE_WITH_INFO;
1036 mono_thread_info_suspend_lock (void)
1038 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1042 mono_thread_info_suspend_unlock (void)
1044 mono_os_sem_post (&global_suspend_semaphore);
1048 * This is a very specific function whose only purpose is to
1049 * break a given thread from socket syscalls.
1051 * This only exists because linux won't fail a call to connect
1052 * if the underlying is closed.
1054 * TODO We should cleanup and unify this with the other syscall abort
1058 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1060 MonoThreadHazardPointers *hp;
1061 MonoThreadInfo *info;
1063 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1066 hp = mono_hazard_pointer_get ();
1067 info = mono_thread_info_lookup (tid);
1071 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1072 mono_hazard_pointer_clear (hp, 1);
1076 mono_thread_info_suspend_lock ();
1077 mono_threads_begin_global_suspend ();
1079 mono_threads_core_abort_syscall (info);
1080 mono_threads_wait_pending_operations ();
1082 mono_hazard_pointer_clear (hp, 1);
1084 mono_threads_end_global_suspend ();
1085 mono_thread_info_suspend_unlock ();
1089 mono_thread_info_unified_management_enabled (void)
1091 return unified_suspend_enabled;
1095 * mono_thread_info_set_is_async_context:
1097 * Set whenever the current thread is in an async context. Some runtime functions might behave
1098 * differently while in an async context in order to be async safe.
1101 mono_thread_info_set_is_async_context (gboolean async_context)
1103 MonoThreadInfo *info = mono_thread_info_current ();
1106 info->is_async_context = async_context;
1110 mono_thread_info_is_async_context (void)
1112 MonoThreadInfo *info = mono_thread_info_current ();
1115 return info->is_async_context;
1121 * mono_threads_create_thread:
1123 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1124 * Returns: a windows or io-layer handle for the thread.
1127 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1129 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1133 * mono_thread_info_get_stack_bounds:
1135 * Return the address and size of the current threads stack. Return NULL as the
1136 * stack address if the stack address cannot be determined.
1139 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1141 guint8 *current = (guint8 *)&stsize;
1142 mono_threads_core_get_stack_bounds (staddr, stsize);
1146 /* Sanity check the result */
1147 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1149 /* When running under emacs, sometimes staddr is not aligned to a page size */
1150 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1154 mono_thread_info_yield (void)
1156 return mono_threads_core_yield ();
1158 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1159 static MonoCoopMutex sleep_mutex;
1160 static MonoCoopCond sleep_cond;
1163 sleep_initialize (void)
1165 mono_coop_mutex_init (&sleep_mutex);
1166 mono_coop_cond_init (&sleep_cond);
1170 sleep_interrupt (gpointer data)
1172 mono_coop_mutex_lock (&sleep_mutex);
1173 mono_coop_cond_broadcast (&sleep_cond);
1174 mono_coop_mutex_unlock (&sleep_mutex);
1177 static inline guint32
1178 sleep_interruptable (guint32 ms, gboolean *alerted)
1182 g_assert (INFINITE == G_MAXUINT32);
1188 end = mono_100ns_ticks () + (ms * 1000 * 10);
1190 mono_lazy_initialize (&sleep_init, sleep_initialize);
1192 mono_coop_mutex_lock (&sleep_mutex);
1195 if (ms != INFINITE) {
1196 now = mono_100ns_ticks ();
1201 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1203 mono_coop_mutex_unlock (&sleep_mutex);
1204 return WAIT_IO_COMPLETION;
1208 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1210 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1212 mono_thread_info_uninstall_interrupt (alerted);
1214 mono_coop_mutex_unlock (&sleep_mutex);
1215 return WAIT_IO_COMPLETION;
1219 mono_coop_mutex_unlock (&sleep_mutex);
1225 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1228 MonoThreadInfo *info;
1230 mono_thread_info_yield ();
1232 info = mono_thread_info_current ();
1233 if (info && mono_thread_info_is_interrupt_state (info))
1234 return WAIT_IO_COMPLETION;
1240 return sleep_interruptable (ms, alerted);
1244 if (ms == INFINITE) {
1247 Sleep (G_MAXUINT32);
1249 sleep (G_MAXUINT32);
1254 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1255 struct timespec start, target;
1257 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1258 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1259 g_assert (ret == 0);
1262 target.tv_sec += ms / 1000;
1263 target.tv_nsec += (ms % 1000) * 1000000;
1264 if (target.tv_nsec > 999999999) {
1265 target.tv_nsec -= 999999999;
1270 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1275 struct timespec req, rem;
1277 req.tv_sec = ms / 1000;
1278 req.tv_nsec = (ms % 1000) * 1000000;
1281 memset (&rem, 0, sizeof (rem));
1282 ret = nanosleep (&req, &rem);
1284 #endif /* __linux__ */
1293 mono_thread_info_usleep (guint64 us)
1302 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1304 return ((MonoThreadInfo*)info)->tls [key];
1308 * mono_threads_info_tls_set:
1310 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1311 * values of TLS variables for threads other than the current thread.
1312 * This should only be used for infrequently changing TLS variables, and it should
1313 * be paired with setting the real TLS variable since this provides no GC tracking.
1316 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1318 ((MonoThreadInfo*)info)->tls [key] = value;
1322 * mono_thread_info_exit:
1324 * Exit the current thread.
1325 * This function doesn't return.
1328 mono_thread_info_exit (void)
1330 mono_threads_core_exit (0);
1334 * mono_thread_info_open_handle:
1336 * Return a io-layer/win32 handle for the current thread.
1337 * The handle need to be closed by calling CloseHandle () when it is no
1341 mono_thread_info_open_handle (void)
1343 return mono_threads_core_open_handle ();
1347 * mono_threads_open_thread_handle:
1349 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1350 * The handle need to be closed by calling CloseHandle () when it is no
1354 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1356 return mono_threads_core_open_thread_handle (handle, tid);
1359 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1361 struct _MonoThreadInfoInterruptToken {
1362 void (*callback) (gpointer data);
1367 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1369 * - @callback: must be able to be called from another thread and always cancel the wait
1370 * - @data: passed to the callback
1371 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1372 * if set to TRUE, it must mean that the thread is in interrupted state
1375 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1377 MonoThreadInfo *info;
1378 MonoThreadInfoInterruptToken *previous_token, *token;
1380 g_assert (callback);
1382 g_assert (interrupted);
1383 *interrupted = FALSE;
1385 info = mono_thread_info_current ();
1388 /* The memory of this token can be freed at 2 places:
1389 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1390 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1391 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1392 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1393 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1394 token->callback = callback;
1397 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1399 if (previous_token) {
1400 if (previous_token != INTERRUPT_STATE)
1401 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1405 *interrupted = TRUE;
1408 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1409 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1413 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1415 MonoThreadInfo *info;
1416 MonoThreadInfoInterruptToken *previous_token;
1418 g_assert (interrupted);
1419 *interrupted = FALSE;
1421 info = mono_thread_info_current ();
1424 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1426 /* only the installer can uninstall the token */
1427 g_assert (previous_token);
1429 if (previous_token == INTERRUPT_STATE) {
1430 /* if it is interrupted, then it is going to be freed in finish interrupt */
1431 *interrupted = TRUE;
1433 g_free (previous_token);
1436 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1437 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1440 static MonoThreadInfoInterruptToken*
1441 set_interrupt_state (MonoThreadInfo *info)
1443 MonoThreadInfoInterruptToken *token, *previous_token;
1447 /* Atomically obtain the token the thread is
1448 * waiting on, and change it to a flag value. */
1451 previous_token = info->interrupt_token;
1453 /* Already interrupted */
1454 if (previous_token == INTERRUPT_STATE) {
1459 token = previous_token;
1460 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1466 * mono_thread_info_prepare_interrupt:
1468 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1469 * - if the thread calls one of the WaitFor functions, the function will return with
1470 * WAIT_IO_COMPLETION instead of waiting
1471 * - if the thread was waiting when this function was called, the wait will be broken
1473 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1474 * didn't receive the interrupt signal yet, in this case it should call the wait function
1475 * again. This essentially means that the target thread will busy wait until it is ready to
1476 * process the interruption.
1478 MonoThreadInfoInterruptToken*
1479 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1481 MonoThreadInfoInterruptToken *token;
1483 token = set_interrupt_state (info);
1485 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1486 mono_thread_info_get_tid (info), token);
1492 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1494 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1499 g_assert (token->callback);
1501 token->callback (token->data);
1507 mono_thread_info_self_interrupt (void)
1509 MonoThreadInfo *info;
1510 MonoThreadInfoInterruptToken *token;
1512 info = mono_thread_info_current ();
1515 token = set_interrupt_state (info);
1518 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1519 mono_thread_info_get_tid (info));
1522 /* Clear the interrupted flag of the current thread, set with
1523 * mono_thread_info_self_interrupt, so it can wait again */
1525 mono_thread_info_clear_self_interrupt ()
1527 MonoThreadInfo *info;
1528 MonoThreadInfoInterruptToken *previous_token;
1530 info = mono_thread_info_current ();
1533 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1534 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1536 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1540 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1543 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1547 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1551 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1552 g_string_append_printf (text, "not waiting");
1553 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1554 g_string_append_printf (text, "interrupted state");
1556 g_string_append_printf (text, "waiting");
1560 mono_thread_info_is_current (MonoThreadInfo *info)
1562 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();