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>
32 #include <mono/utils/mono-threads-debug.h>
37 #include <mono/utils/mach-support.h>
41 Mutex that makes sure only a single thread can be suspending others.
42 Suspend is a very racy operation since it requires restarting until
43 the target thread is not on an unsafe region.
45 We could implement this using critical regions, but would be much much
46 harder for an operation that is hardly performance critical.
48 The GC has to acquire this lock before starting a STW to make sure
49 a runtime suspend won't make it wronly see a thread in a safepoint
50 when it is in fact not.
52 This has to be a naked locking primitive, and not a coop aware one, as
53 it needs to be usable when destroying thread_info_key, the TLS key for
54 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
55 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
56 to an assertion error. We then simply switch state manually in
57 mono_thread_info_suspend_lock_with_info.
59 static MonoSemType global_suspend_semaphore;
61 static size_t thread_info_size;
62 static MonoThreadInfoCallbacks threads_callbacks;
63 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
64 static MonoNativeTlsKey thread_info_key, thread_exited_key;
66 static __thread guint32 tls_small_id MONO_TLS_FAST;
68 static MonoNativeTlsKey small_id_key;
70 static MonoLinkedListSet thread_list;
71 static gboolean mono_threads_inited = FALSE;
73 static MonoSemType suspend_semaphore;
74 static size_t pending_suspends;
76 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
79 #define SLEEP_DURATION_BEFORE_WARNING (50)
81 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
83 static guint32 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_suspend_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_suspend_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_suspend_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) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
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
264 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
267 mono_hazard_pointer_clear (hp, 0);
269 mono_hazard_pointer_clear (hp, 1);
271 mono_hazard_pointer_clear (hp, 2);
275 If return non null Hazard Pointer 1 holds the return value.
278 mono_thread_info_lookup (MonoNativeThreadId id)
280 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
282 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
283 mono_hazard_pointer_clear_all (hp, -1);
287 mono_hazard_pointer_clear_all (hp, 1);
288 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
292 mono_thread_info_insert (MonoThreadInfo *info)
294 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
296 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
297 mono_hazard_pointer_clear_all (hp, -1);
301 mono_hazard_pointer_clear_all (hp, -1);
306 mono_thread_info_remove (MonoThreadInfo *info)
308 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
311 THREADS_DEBUG ("removing info %p\n", info);
312 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
313 mono_hazard_pointer_clear_all (hp, -1);
318 free_thread_info (gpointer mem)
320 MonoThreadInfo *info = (MonoThreadInfo *) mem;
322 mono_os_sem_destroy (&info->resume_semaphore);
323 mono_threads_suspend_free (info);
329 mono_thread_info_register_small_id (void)
331 int small_id = mono_thread_small_id_alloc ();
332 #ifdef HAVE_KW_THREAD
333 tls_small_id = small_id;
335 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
341 register_thread (MonoThreadInfo *info, gpointer baseptr)
344 guint8 *staddr = NULL;
345 int small_id = mono_thread_info_register_small_id ();
347 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
348 info->small_id = small_id;
350 mono_os_sem_init (&info->resume_semaphore, 0);
352 /*set TLS early so SMR works */
353 mono_native_tls_set_value (thread_info_key, info);
355 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
357 if (threads_callbacks.thread_register) {
358 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
359 // g_warning ("thread registation failed\n");
360 mono_native_tls_set_value (thread_info_key, NULL);
366 mono_thread_info_get_stack_bounds (&staddr, &stsize);
369 info->stack_start_limit = staddr;
370 info->stack_end = staddr + stsize;
372 info->stackdata = g_byte_array_new ();
374 mono_threads_platform_register (info);
375 mono_threads_suspend_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;
404 info = (MonoThreadInfo *) arg;
406 g_assert (mono_thread_info_is_current (info));
407 g_assert (mono_thread_info_is_live (info));
409 small_id = info->small_id;
411 /* We only enter the GC unsafe region, as when exiting this function, the thread
412 * will be detached, and the current MonoThreadInfo* will be destroyed. */
413 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
415 THREADS_DEBUG ("unregistering info %p\n", info);
417 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
420 * TLS destruction order is not reliable so small_id might be cleaned up
423 #ifndef HAVE_KW_THREAD
424 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
427 /* we need to duplicate it, as the info->handle is going
428 * to be closed when unregistering from the platform */
429 handle = mono_threads_platform_duplicate_handle (info);
432 First perform the callback that requires no locks.
433 This callback has the potential of taking other locks, so we do it before.
434 After it completes, the thread remains functional.
436 if (threads_callbacks.thread_detach)
437 threads_callbacks.thread_detach (info);
439 mono_thread_info_suspend_lock_with_info (info);
442 Now perform the callback that must be done under locks.
443 This will render the thread useless and non-suspendable, so it must
444 be done while holding the suspend lock to give no other thread chance
447 if (threads_callbacks.thread_unregister)
448 threads_callbacks.thread_unregister (info);
450 mono_threads_platform_unregister (info);
451 result = mono_thread_info_remove (info);
453 mono_threads_transition_detach (info);
455 mono_thread_info_suspend_unlock ();
457 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
459 /*now it's safe to free the thread info.*/
460 mono_thread_hazardous_try_free (info, free_thread_info);
461 /* Pump the HP queue */
462 mono_thread_hazardous_try_free_some ();
464 mono_thread_small_id_free (small_id);
466 /* Signal the w32handle. It can be done as late as here
467 * because w32handle does not access the current MonoThreadInfo,
468 * neither does it switch state to BLOCKING. */
469 mono_threads_platform_set_exited (handle);
471 mono_threads_platform_close_thread_handle (handle);
475 thread_exited_dtor (void *arg)
477 #if defined(__MACH__)
479 * Since we use pthread dtors to clean up thread data, if a thread
480 * is attached to the runtime by another pthread dtor after our dtor
481 * has ran, it will never be detached, leading to various problems
482 * since the thread ids etc. will be reused while they are still in
483 * the threads hashtables etc.
484 * Dtors are called in a loop until all user tls entries are 0,
485 * but the loop has a maximum count (4), so if we set the tls
486 * variable every time, it will remain set when system tls dtors
487 * are ran. This allows mono_thread_info_is_exiting () to detect
488 * whenever the thread is exiting, even if it is executed from a
489 * system tls dtor (i.e. obj-c dealloc methods).
491 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
496 mono_thread_info_current_unchecked (void)
498 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
503 mono_thread_info_current (void)
505 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
509 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
512 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
513 The way to distinguish between before, during and after cleanup is the following:
515 -If the TLS key is set, cleanup has not begun;
516 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
517 -If the thread is nowhere to be found, cleanup has finished.
519 We cannot function after cleanup since there's no way to ensure what will happen.
523 /*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 */
524 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
530 mono_thread_info_get_small_id (void)
532 #ifdef HAVE_KW_THREAD
535 gpointer val = mono_native_tls_get_value (small_id_key);
538 return GPOINTER_TO_INT (val) - 1;
543 mono_thread_info_list_head (void)
549 * mono_threads_attach_tools_thread
551 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
553 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
554 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
557 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
558 * doing things like resolving backtraces in their background processing thread.
561 mono_threads_attach_tools_thread (void)
564 MonoThreadInfo *info;
566 /* Must only be called once */
567 g_assert (!mono_native_tls_get_value (thread_info_key));
569 while (!mono_threads_inited) {
570 mono_thread_info_usleep (10);
573 info = mono_thread_info_attach (&dummy);
576 info->tools_thread = TRUE;
580 mono_thread_info_attach (void *baseptr)
582 MonoThreadInfo *info;
583 if (!mono_threads_inited)
586 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
587 * thread is created before an embedding API user initialized Mono. */
588 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
591 g_assert (mono_threads_inited);
594 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
596 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
597 THREADS_DEBUG ("attaching %p\n", info);
598 if (!register_thread (info, baseptr))
600 } else if (threads_callbacks.thread_attach) {
601 threads_callbacks.thread_attach (info);
607 mono_thread_info_detach (void)
609 MonoThreadInfo *info;
610 if (!mono_threads_inited)
612 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
613 * is created before an embedding API user initialized Mono. */
614 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
617 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
619 THREADS_DEBUG ("detaching %p\n", info);
620 unregister_thread (info);
621 mono_native_tls_set_value (thread_info_key, NULL);
626 * mono_thread_info_is_exiting:
628 * Return whenever the current thread is exiting, i.e. it is running pthread
632 mono_thread_info_is_exiting (void)
634 #if defined(__MACH__)
635 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
643 thread_info_key_dtor (void *arg)
645 /* Put the MonoThreadInfo back for the duration of the
646 * unregister code. In some circumstances the thread needs to
647 * take the GC lock which may block which requires a coop
648 * state transition. */
649 mono_native_tls_set_value (thread_info_key, arg);
650 unregister_thread (arg);
651 mono_native_tls_set_value (thread_info_key, NULL);
656 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
659 threads_callbacks = *callbacks;
660 thread_info_size = info_size;
661 const char *sleepLimit;
663 res = mono_native_tls_alloc (&thread_info_key, NULL);
664 res = mono_native_tls_alloc (&thread_exited_key, NULL);
666 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
667 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
672 #ifndef HAVE_KW_THREAD
673 res = mono_native_tls_alloc (&small_id_key, NULL);
677 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
679 long threshold = strtol(sleepLimit, NULL, 10);
680 if ((errno == 0) && (threshold >= 40)) {
681 sleepAbortDuration = threshold;
682 sleepWarnDuration = threshold / 20;
684 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
687 mono_os_sem_init (&global_suspend_semaphore, 1);
688 mono_os_sem_init (&suspend_semaphore, 0);
690 mono_lls_init (&thread_list, NULL);
691 mono_thread_smr_init ();
692 mono_threads_platform_init ();
693 mono_threads_suspend_init ();
694 mono_threads_suspend_init_signals ();
695 mono_threads_coop_init ();
697 #if defined(__MACH__)
698 mono_mach_init (thread_info_key);
701 mono_threads_inited = TRUE;
703 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
707 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
709 runtime_callbacks = *callbacks;
712 MonoThreadInfoRuntimeCallbacks *
713 mono_threads_get_runtime_callbacks (void)
715 return &runtime_callbacks;
719 Signal that the current thread wants to be suspended.
720 This function can be called without holding the suspend lock held.
721 To finish suspending, call mono_suspend_check.
724 mono_thread_info_begin_self_suspend (void)
726 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
730 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
731 mono_threads_transition_request_self_suspension (info);
735 mono_thread_info_end_self_suspend (void)
737 MonoThreadInfo *info;
739 info = mono_thread_info_current ();
742 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
744 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
746 /* commit the saved state and notify others if needed */
747 switch (mono_threads_transition_state_poll (info)) {
748 case SelfSuspendResumed:
750 case SelfSuspendWait:
751 mono_thread_info_wait_for_resume (info);
753 case SelfSuspendNotifyAndWait:
754 mono_threads_notify_initiator_of_suspend (info);
755 mono_thread_info_wait_for_resume (info);
756 mono_threads_notify_initiator_of_resume (info);
762 mono_thread_info_core_resume (MonoThreadInfo *info)
764 gboolean res = FALSE;
766 switch (mono_threads_transition_request_resume (info)) {
773 case ResumeInitSelfResume:
774 resume_self_suspended (info);
777 case ResumeInitAsyncResume:
778 resume_async_suspended (info);
781 case ResumeInitBlockingResume:
782 resume_blocking_suspended (info);
791 mono_thread_info_resume (MonoNativeThreadId tid)
793 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
794 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
795 MonoThreadInfo *info;
797 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
799 mono_thread_info_suspend_lock ();
801 info = mono_thread_info_lookup (tid); /*info on HP1*/
807 result = mono_thread_info_core_resume (info);
809 //Wait for the pending resume to finish
810 mono_threads_wait_pending_operations ();
813 mono_thread_info_suspend_unlock ();
814 mono_hazard_pointer_clear (hp, 1);
819 mono_thread_info_begin_suspend (MonoThreadInfo *info)
821 switch (mono_threads_transition_request_async_suspension (info)) {
822 case AsyncSuspendAlreadySuspended:
823 case AsyncSuspendBlocking:
825 case AsyncSuspendWait:
826 mono_threads_add_to_pending_operation_set (info);
828 case AsyncSuspendInitSuspend:
829 return begin_async_suspend (info, FALSE);
831 g_assert_not_reached ();
836 mono_thread_info_begin_resume (MonoThreadInfo *info)
838 return mono_thread_info_core_resume (info);
842 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
843 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
846 is_thread_in_critical_region (MonoThreadInfo *info)
850 gpointer stack_start;
851 MonoThreadUnwindState *state;
853 /* Are we inside a system critical region? */
854 if (info->inside_critical_region)
857 /* Are we inside a GC critical region? */
858 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
862 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
863 state = mono_thread_info_get_suspend_state (info);
864 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
867 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
868 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
869 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
872 ji = mono_jit_info_table_find (
873 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
874 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
879 method = mono_jit_info_get_method (ji);
881 return threads_callbacks.mono_method_is_critical (method);
885 mono_thread_info_in_critical_location (MonoThreadInfo *info)
887 return is_thread_in_critical_region (info);
891 The return value is only valid until a matching mono_thread_info_resume is called
893 static MonoThreadInfo*
894 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
896 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
897 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
901 switch (mono_threads_transition_request_async_suspension (info)) {
902 case AsyncSuspendAlreadySuspended:
903 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
905 case AsyncSuspendWait:
906 mono_threads_add_to_pending_operation_set (info);
908 case AsyncSuspendInitSuspend:
909 if (!begin_async_suspend (info, interrupt_kernel)) {
910 mono_hazard_pointer_clear (hp, 1);
914 case AsyncSuspendBlocking:
915 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
916 mono_threads_suspend_abort_syscall (info);
920 g_assert_not_reached ();
923 //Wait for the pending suspend to finish
924 mono_threads_wait_pending_operations ();
926 if (!check_async_suspend (info)) {
927 mono_thread_info_core_resume (info);
928 mono_threads_wait_pending_operations ();
929 mono_hazard_pointer_clear (hp, 1);
935 static MonoThreadInfo*
936 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
938 MonoThreadInfo *info = NULL;
939 int sleep_duration = 0;
941 if (!(info = suspend_sync (id, interrupt_kernel))) {
942 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
946 /*WARNING: We now are in interrupt context until we resume the thread. */
947 if (!is_thread_in_critical_region (info))
950 if (!mono_thread_info_core_resume (info)) {
951 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
954 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
956 /* Wait for the pending resume to finish */
957 mono_threads_wait_pending_operations ();
959 if (sleep_duration == 0)
960 mono_thread_info_yield ();
962 g_usleep (sleep_duration);
964 sleep_duration += 10;
970 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
973 MonoThreadInfo *info = NULL;
974 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
976 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
977 /*FIXME: unify this with self-suspend*/
978 g_assert (id != mono_native_thread_id_get ());
980 /* This can block during stw */
981 mono_thread_info_suspend_lock ();
982 mono_threads_begin_global_suspend ();
984 info = suspend_sync_nolock (id, interrupt_kernel);
988 switch (result = callback (info, user_data)) {
989 case MonoResumeThread:
990 mono_hazard_pointer_set (hp, 1, info);
991 mono_thread_info_core_resume (info);
992 mono_threads_wait_pending_operations ();
997 g_error ("Invalid suspend_and_run callback return value %d", result);
1001 mono_hazard_pointer_clear (hp, 1);
1002 mono_threads_end_global_suspend ();
1003 mono_thread_info_suspend_unlock ();
1007 Inject an assynchronous call into the target thread. The target thread must be suspended and
1008 only a single async call can be setup for a given suspend cycle.
1009 This async call must cause stack unwinding as the current implementation doesn't save enough state
1010 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1011 currently used only to deliver exceptions.
1014 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1016 /* An async call can only be setup on an async suspended thread */
1017 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1018 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1019 g_assert (!info->async_target);
1020 info->async_target = target_func;
1021 /* This is not GC tracked */
1022 info->user_data = user_data;
1026 The suspend lock is held during any suspend in progress.
1027 A GC that has safepoints must take this lock as part of its
1028 STW to make sure no unsafe pending suspend is in progress.
1032 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1035 g_assert (mono_thread_info_is_current (info));
1036 g_assert (mono_thread_info_is_live (info));
1038 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1040 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1041 g_assert (res != -1);
1043 MONO_EXIT_GC_SAFE_WITH_INFO;
1047 mono_thread_info_suspend_lock (void)
1049 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1053 mono_thread_info_suspend_unlock (void)
1055 mono_os_sem_post (&global_suspend_semaphore);
1059 * This is a very specific function whose only purpose is to
1060 * break a given thread from socket syscalls.
1062 * This only exists because linux won't fail a call to connect
1063 * if the underlying is closed.
1065 * TODO We should cleanup and unify this with the other syscall abort
1069 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1071 MonoThreadHazardPointers *hp;
1072 MonoThreadInfo *info;
1074 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1077 hp = mono_hazard_pointer_get ();
1078 info = mono_thread_info_lookup (tid);
1082 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1083 mono_hazard_pointer_clear (hp, 1);
1087 mono_thread_info_suspend_lock ();
1088 mono_threads_begin_global_suspend ();
1090 mono_threads_suspend_abort_syscall (info);
1091 mono_threads_wait_pending_operations ();
1093 mono_hazard_pointer_clear (hp, 1);
1095 mono_threads_end_global_suspend ();
1096 mono_thread_info_suspend_unlock ();
1100 * mono_thread_info_set_is_async_context:
1102 * Set whenever the current thread is in an async context. Some runtime functions might behave
1103 * differently while in an async context in order to be async safe.
1106 mono_thread_info_set_is_async_context (gboolean async_context)
1108 MonoThreadInfo *info = mono_thread_info_current ();
1111 info->is_async_context = async_context;
1115 mono_thread_info_is_async_context (void)
1117 MonoThreadInfo *info = mono_thread_info_current ();
1120 return info->is_async_context;
1127 MonoThreadStart start_routine;
1128 gpointer start_routine_arg;
1130 MonoCoopSem registered;
1135 inner_start_thread (gpointer data)
1137 CreateThreadData *thread_data;
1138 MonoThreadInfo *info;
1139 MonoThreadStart start_routine;
1140 gpointer start_routine_arg;
1141 guint32 start_routine_res;
1144 thread_data = (CreateThreadData*) data;
1145 g_assert (thread_data);
1147 start_routine = thread_data->start_routine;
1148 start_routine_arg = thread_data->start_routine_arg;
1150 info = mono_thread_info_attach (&dummy);
1151 info->runtime_thread = TRUE;
1153 thread_data->handle = mono_thread_info_duplicate_handle (info);
1155 mono_coop_sem_post (&thread_data->registered);
1157 if (InterlockedDecrement (&thread_data->ref) == 0) {
1158 mono_coop_sem_destroy (&thread_data->registered);
1159 g_free (thread_data);
1162 /* thread_data is not valid anymore */
1165 /* Run the actual main function of the thread */
1166 start_routine_res = start_routine (start_routine_arg);
1168 mono_threads_platform_exit (start_routine_res);
1170 g_assert_not_reached ();
1174 * mono_threads_create_thread:
1176 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1177 * Returns: a windows or io-layer handle for the thread.
1180 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1182 CreateThreadData *thread_data;
1186 thread_data = g_new0 (CreateThreadData, 1);
1187 thread_data->ref = 2;
1188 thread_data->start_routine = start;
1189 thread_data->start_routine_arg = arg;
1190 mono_coop_sem_init (&thread_data->registered, 0);
1192 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, stack_size, out_tid);
1194 /* ref is not going to be decremented in inner_start_thread */
1195 InterlockedDecrement (&thread_data->ref);
1200 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1201 g_assert (res == 0);
1203 ret = thread_data->handle;
1207 if (InterlockedDecrement (&thread_data->ref) == 0) {
1208 mono_coop_sem_destroy (&thread_data->registered);
1209 g_free (thread_data);
1216 * mono_thread_info_get_stack_bounds:
1218 * Return the address and size of the current threads stack. Return NULL as the
1219 * stack address if the stack address cannot be determined.
1222 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1224 guint8 *current = (guint8 *)&stsize;
1225 mono_threads_platform_get_stack_bounds (staddr, stsize);
1229 /* Sanity check the result */
1230 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1232 /* When running under emacs, sometimes staddr is not aligned to a page size */
1233 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1237 mono_thread_info_yield (void)
1239 return mono_threads_platform_yield ();
1241 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1242 static MonoCoopMutex sleep_mutex;
1243 static MonoCoopCond sleep_cond;
1246 sleep_initialize (void)
1248 mono_coop_mutex_init (&sleep_mutex);
1249 mono_coop_cond_init (&sleep_cond);
1253 sleep_interrupt (gpointer data)
1255 mono_coop_mutex_lock (&sleep_mutex);
1256 mono_coop_cond_broadcast (&sleep_cond);
1257 mono_coop_mutex_unlock (&sleep_mutex);
1260 static inline guint32
1261 sleep_interruptable (guint32 ms, gboolean *alerted)
1265 g_assert (INFINITE == G_MAXUINT32);
1271 end = mono_msec_ticks() + ms;
1273 mono_lazy_initialize (&sleep_init, sleep_initialize);
1275 mono_coop_mutex_lock (&sleep_mutex);
1278 if (ms != INFINITE) {
1279 now = mono_msec_ticks();
1284 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1286 mono_coop_mutex_unlock (&sleep_mutex);
1287 return WAIT_IO_COMPLETION;
1291 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1293 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1295 mono_thread_info_uninstall_interrupt (alerted);
1297 mono_coop_mutex_unlock (&sleep_mutex);
1298 return WAIT_IO_COMPLETION;
1302 mono_coop_mutex_unlock (&sleep_mutex);
1308 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1311 MonoThreadInfo *info;
1313 mono_thread_info_yield ();
1315 info = mono_thread_info_current ();
1316 if (info && mono_thread_info_is_interrupt_state (info))
1317 return WAIT_IO_COMPLETION;
1323 return sleep_interruptable (ms, alerted);
1327 if (ms == INFINITE) {
1330 Sleep (G_MAXUINT32);
1332 sleep (G_MAXUINT32);
1337 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1338 struct timespec start, target;
1340 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1341 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1342 g_assert (ret == 0);
1345 target.tv_sec += ms / 1000;
1346 target.tv_nsec += (ms % 1000) * 1000000;
1347 if (target.tv_nsec > 999999999) {
1348 target.tv_nsec -= 999999999;
1353 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1358 struct timespec req, rem;
1360 req.tv_sec = ms / 1000;
1361 req.tv_nsec = (ms % 1000) * 1000000;
1364 memset (&rem, 0, sizeof (rem));
1365 ret = nanosleep (&req, &rem);
1367 #endif /* __linux__ */
1376 mono_thread_info_usleep (guint64 us)
1385 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1387 return ((MonoThreadInfo*)info)->tls [key];
1391 * mono_threads_info_tls_set:
1393 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1394 * values of TLS variables for threads other than the current thread.
1395 * This should only be used for infrequently changing TLS variables, and it should
1396 * be paired with setting the real TLS variable since this provides no GC tracking.
1399 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1401 ((MonoThreadInfo*)info)->tls [key] = value;
1405 * mono_thread_info_exit:
1407 * Exit the current thread.
1408 * This function doesn't return.
1411 mono_thread_info_exit (void)
1413 mono_threads_platform_exit (0);
1417 * mono_threads_open_thread_handle:
1419 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1420 * The handle need to be closed by calling CloseHandle () when it is no
1424 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1426 return mono_threads_platform_open_thread_handle (handle, tid);
1430 mono_threads_close_thread_handle (HANDLE handle)
1432 return mono_threads_platform_close_thread_handle (handle);
1435 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1437 struct _MonoThreadInfoInterruptToken {
1438 void (*callback) (gpointer data);
1443 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1445 * - @callback: must be able to be called from another thread and always cancel the wait
1446 * - @data: passed to the callback
1447 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1448 * if set to TRUE, it must mean that the thread is in interrupted state
1451 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1453 MonoThreadInfo *info;
1454 MonoThreadInfoInterruptToken *previous_token, *token;
1456 g_assert (callback);
1458 g_assert (interrupted);
1459 *interrupted = FALSE;
1461 info = mono_thread_info_current ();
1464 /* The memory of this token can be freed at 2 places:
1465 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1466 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1467 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1468 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1469 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1470 token->callback = callback;
1473 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1475 if (previous_token) {
1476 if (previous_token != INTERRUPT_STATE)
1477 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1481 *interrupted = TRUE;
1484 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1485 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1489 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1491 MonoThreadInfo *info;
1492 MonoThreadInfoInterruptToken *previous_token;
1494 g_assert (interrupted);
1495 *interrupted = FALSE;
1497 info = mono_thread_info_current ();
1500 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1502 /* only the installer can uninstall the token */
1503 g_assert (previous_token);
1505 if (previous_token == INTERRUPT_STATE) {
1506 /* if it is interrupted, then it is going to be freed in finish interrupt */
1507 *interrupted = TRUE;
1509 g_free (previous_token);
1512 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1513 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1516 static MonoThreadInfoInterruptToken*
1517 set_interrupt_state (MonoThreadInfo *info)
1519 MonoThreadInfoInterruptToken *token, *previous_token;
1523 /* Atomically obtain the token the thread is
1524 * waiting on, and change it to a flag value. */
1527 previous_token = info->interrupt_token;
1529 /* Already interrupted */
1530 if (previous_token == INTERRUPT_STATE) {
1535 token = previous_token;
1536 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1542 * mono_thread_info_prepare_interrupt:
1544 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1545 * - if the thread calls one of the WaitFor functions, the function will return with
1546 * WAIT_IO_COMPLETION instead of waiting
1547 * - if the thread was waiting when this function was called, the wait will be broken
1549 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1550 * didn't receive the interrupt signal yet, in this case it should call the wait function
1551 * again. This essentially means that the target thread will busy wait until it is ready to
1552 * process the interruption.
1554 MonoThreadInfoInterruptToken*
1555 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1557 MonoThreadInfoInterruptToken *token;
1559 token = set_interrupt_state (info);
1561 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1562 mono_thread_info_get_tid (info), token);
1568 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1570 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1575 g_assert (token->callback);
1577 token->callback (token->data);
1583 mono_thread_info_self_interrupt (void)
1585 MonoThreadInfo *info;
1586 MonoThreadInfoInterruptToken *token;
1588 info = mono_thread_info_current ();
1591 token = set_interrupt_state (info);
1594 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1595 mono_thread_info_get_tid (info));
1598 /* Clear the interrupted flag of the current thread, set with
1599 * mono_thread_info_self_interrupt, so it can wait again */
1601 mono_thread_info_clear_self_interrupt ()
1603 MonoThreadInfo *info;
1604 MonoThreadInfoInterruptToken *previous_token;
1606 info = mono_thread_info_current ();
1609 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1610 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1612 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1616 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1619 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1623 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1627 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1628 g_string_append_printf (text, "not waiting");
1629 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1630 g_string_append_printf (text, "interrupted state");
1632 g_string_append_printf (text, "waiting");
1636 mono_thread_info_is_current (MonoThreadInfo *info)
1638 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1642 mono_thread_info_set_exited (THREAD_INFO_TYPE *info)
1644 g_assert (mono_thread_info_is_current (info));
1646 g_assert (info->handle);
1647 mono_threads_platform_set_exited (info->handle);
1651 mono_thread_info_duplicate_handle (MonoThreadInfo *info)
1653 g_assert (mono_thread_info_is_current (info));
1654 return mono_threads_platform_duplicate_handle (info);