2 * mono-threads.c: Low-level threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 /* enable pthread extensions */
15 #define _DARWIN_C_SOURCE
18 #include <mono/utils/mono-compiler.h>
19 #include <mono/utils/mono-os-semaphore.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-lazy-init.h>
28 #include <mono/utils/mono-coop-mutex.h>
29 #include <mono/utils/mono-coop-semaphore.h>
34 #include <mono/utils/mach-support.h>
38 Mutex that makes sure only a single thread can be suspending others.
39 Suspend is a very racy operation since it requires restarting until
40 the target thread is not on an unsafe region.
42 We could implement this using critical regions, but would be much much
43 harder for an operation that is hardly performance critical.
45 The GC has to acquire this lock before starting a STW to make sure
46 a runtime suspend won't make it wronly see a thread in a safepoint
47 when it is in fact not.
49 static MonoCoopSem global_suspend_semaphore;
51 static size_t thread_info_size;
52 static MonoThreadInfoCallbacks threads_callbacks;
53 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
54 static MonoNativeTlsKey thread_info_key, thread_exited_key;
56 static __thread guint32 tls_small_id MONO_TLS_FAST;
58 static MonoNativeTlsKey small_id_key;
60 static MonoLinkedListSet thread_list;
61 static gboolean mono_threads_inited = FALSE;
63 static MonoSemType suspend_semaphore;
64 static size_t pending_suspends;
65 static gboolean unified_suspend_enabled;
67 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
70 #define SLEEP_DURATION_BEFORE_WARNING (10)
72 #define SLEEP_DURATION_BEFORE_ABORT 200
74 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
77 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
79 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
80 InterlockedIncrement (&abort_posts);
81 mono_os_sem_post (&suspend_semaphore);
85 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
87 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
88 InterlockedIncrement (&suspend_posts);
89 mono_os_sem_post (&suspend_semaphore);
93 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
95 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
96 InterlockedIncrement (&resume_posts);
97 mono_os_sem_post (&suspend_semaphore);
101 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
103 if (mono_threads_is_coop_enabled ()) {
104 /* There's nothing else to do after we async request the thread to suspend */
105 mono_threads_add_to_pending_operation_set (info);
109 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
113 check_async_suspend (MonoThreadInfo *info)
115 if (mono_threads_is_coop_enabled ()) {
116 /* Async suspend can't async fail on coop */
120 return mono_threads_core_check_suspend_result (info);
124 resume_async_suspended (MonoThreadInfo *info)
126 if (mono_threads_is_coop_enabled ())
127 g_assert_not_reached ();
129 g_assert (mono_threads_core_begin_async_resume (info));
133 resume_self_suspended (MonoThreadInfo* info)
135 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
136 mono_os_sem_post (&info->resume_semaphore);
140 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
143 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
144 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
145 g_assert (res != -1);
149 resume_blocking_suspended (MonoThreadInfo* info)
151 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
152 mono_os_sem_post (&info->resume_semaphore);
156 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
158 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
160 InterlockedIncrement (&pending_ops);
164 mono_threads_begin_global_suspend (void)
166 size_t ps = pending_suspends;
167 if (G_UNLIKELY (ps != 0))
168 g_error ("pending_suspends = %d, but must be 0", ps);
169 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,
170 abort_posts, waits_done, pending_ops);
171 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
172 mono_threads_coop_begin_global_suspend ();
176 mono_threads_end_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 ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\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_end_global_suspend ();
190 MonoThreadInfo *cur = mono_thread_info_current ();
192 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
193 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
194 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
195 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
196 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
197 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
198 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
199 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
200 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
201 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
203 FOREACH_THREAD_SAFE (info) {
205 char thread_name [256] = { 0 };
206 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
208 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" : "" );
210 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" : "" );
212 } FOREACH_THREAD_SAFE_END
216 mono_threads_wait_pending_operations (void)
219 int c = pending_suspends;
221 /* Wait threads to park */
222 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
223 if (pending_suspends) {
224 MonoStopwatch suspension_time;
225 mono_stopwatch_start (&suspension_time);
226 for (i = 0; i < pending_suspends; ++i) {
227 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
228 InterlockedIncrement (&waits_done);
229 if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE))
231 mono_stopwatch_stop (&suspension_time);
235 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
236 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);
238 mono_stopwatch_stop (&suspension_time);
239 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
243 pending_suspends = 0;
249 //Thread initialization code
251 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
254 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
257 mono_hazard_pointer_clear (hp, 0);
259 mono_hazard_pointer_clear (hp, 1);
261 mono_hazard_pointer_clear (hp, 2);
265 If return non null Hazard Pointer 1 holds the return value.
268 mono_thread_info_lookup (MonoNativeThreadId id)
270 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
272 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
273 mono_hazard_pointer_clear_all (hp, -1);
277 mono_hazard_pointer_clear_all (hp, 1);
278 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
282 mono_thread_info_insert (MonoThreadInfo *info)
284 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
286 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
287 mono_hazard_pointer_clear_all (hp, -1);
291 mono_hazard_pointer_clear_all (hp, -1);
296 mono_thread_info_remove (MonoThreadInfo *info)
298 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
301 THREADS_DEBUG ("removing info %p\n", info);
302 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
303 mono_hazard_pointer_clear_all (hp, -1);
308 free_thread_info (gpointer mem)
310 MonoThreadInfo *info = (MonoThreadInfo *) mem;
312 mono_os_sem_destroy (&info->resume_semaphore);
313 mono_threads_platform_free (info);
319 mono_thread_info_register_small_id (void)
321 int small_id = mono_thread_small_id_alloc ();
322 #ifdef HAVE_KW_THREAD
323 tls_small_id = small_id;
325 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
331 register_thread (MonoThreadInfo *info, gpointer baseptr)
334 guint8 *staddr = NULL;
335 int small_id = mono_thread_info_register_small_id ();
337 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
338 info->small_id = small_id;
340 mono_os_sem_init (&info->resume_semaphore, 0);
342 /*set TLS early so SMR works */
343 mono_native_tls_set_value (thread_info_key, info);
345 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
347 if (threads_callbacks.thread_register) {
348 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
349 // g_warning ("thread registation failed\n");
355 mono_thread_info_get_stack_bounds (&staddr, &stsize);
358 info->stack_start_limit = staddr;
359 info->stack_end = staddr + stsize;
361 info->stackdata = g_byte_array_new ();
363 mono_threads_platform_register (info);
366 Transition it before taking any locks or publishing itself to reduce the chance
367 of others witnessing a detached thread.
368 We can reasonably expect that until this thread gets published, no other thread will
369 try to manipulate it.
371 mono_threads_transition_attach (info);
372 mono_thread_info_suspend_lock ();
373 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
374 result = mono_thread_info_insert (info);
376 mono_thread_info_suspend_unlock ();
381 unregister_thread (void *arg)
383 MonoThreadInfo *info = (MonoThreadInfo *) arg;
384 int small_id = info->small_id;
387 THREADS_DEBUG ("unregistering info %p\n", info);
389 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
391 mono_threads_core_unregister (info);
394 * TLS destruction order is not reliable so small_id might be cleaned up
397 #ifndef HAVE_KW_THREAD
398 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
402 First perform the callback that requires no locks.
403 This callback has the potential of taking other locks, so we do it before.
404 After it completes, the thread remains functional.
406 if (threads_callbacks.thread_detach)
407 threads_callbacks.thread_detach (info);
409 mono_thread_info_suspend_lock ();
412 Now perform the callback that must be done under locks.
413 This will render the thread useless and non-suspendable, so it must
414 be done while holding the suspend lock to give no other thread chance
417 if (threads_callbacks.thread_unregister)
418 threads_callbacks.thread_unregister (info);
419 mono_threads_unregister_current_thread (info);
420 mono_threads_transition_detach (info);
422 mono_thread_info_suspend_unlock ();
424 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
426 /*now it's safe to free the thread info.*/
427 mono_thread_hazardous_free_or_queue (info, free_thread_info, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
428 mono_thread_small_id_free (small_id);
432 thread_exited_dtor (void *arg)
434 #if defined(__MACH__)
436 * Since we use pthread dtors to clean up thread data, if a thread
437 * is attached to the runtime by another pthread dtor after our dtor
438 * has ran, it will never be detached, leading to various problems
439 * since the thread ids etc. will be reused while they are still in
440 * the threads hashtables etc.
441 * Dtors are called in a loop until all user tls entries are 0,
442 * but the loop has a maximum count (4), so if we set the tls
443 * variable every time, it will remain set when system tls dtors
444 * are ran. This allows mono_thread_info_is_exiting () to detect
445 * whenever the thread is exiting, even if it is executed from a
446 * system tls dtor (i.e. obj-c dealloc methods).
448 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
453 * Removes the current thread from the thread list.
454 * This must be called from the thread unregister callback and nowhere else.
455 * The current thread must be passed as TLS might have already been cleaned up.
458 mono_threads_unregister_current_thread (MonoThreadInfo *info)
461 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
462 result = mono_thread_info_remove (info);
467 mono_thread_info_current_unchecked (void)
469 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
474 mono_thread_info_current (void)
476 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
480 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
483 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
484 The way to distinguish between before, during and after cleanup is the following:
486 -If the TLS key is set, cleanup has not begun;
487 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
488 -If the thread is nowhere to be found, cleanup has finished.
490 We cannot function after cleanup since there's no way to ensure what will happen.
494 /*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 */
495 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
501 mono_thread_info_get_small_id (void)
503 #ifdef HAVE_KW_THREAD
506 gpointer val = mono_native_tls_get_value (small_id_key);
509 return GPOINTER_TO_INT (val) - 1;
514 mono_thread_info_list_head (void)
520 * mono_threads_attach_tools_thread
522 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
524 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
525 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
528 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
529 * doing things like resolving backtraces in their background processing thread.
532 mono_threads_attach_tools_thread (void)
535 MonoThreadInfo *info;
537 /* Must only be called once */
538 g_assert (!mono_native_tls_get_value (thread_info_key));
540 while (!mono_threads_inited) {
541 mono_thread_info_usleep (10);
544 info = mono_thread_info_attach (&dummy);
547 info->tools_thread = TRUE;
551 mono_thread_info_attach (void *baseptr)
553 MonoThreadInfo *info;
554 if (!mono_threads_inited)
557 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
558 * thread is created before an embedding API user initialized Mono. */
559 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
562 g_assert (mono_threads_inited);
565 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
567 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
568 THREADS_DEBUG ("attaching %p\n", info);
569 if (!register_thread (info, baseptr))
571 } else if (threads_callbacks.thread_attach) {
572 threads_callbacks.thread_attach (info);
578 mono_thread_info_detach (void)
580 MonoThreadInfo *info;
581 if (!mono_threads_inited)
583 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
584 * is created before an embedding API user initialized Mono. */
585 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
588 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
590 THREADS_DEBUG ("detaching %p\n", info);
591 unregister_thread (info);
592 mono_native_tls_set_value (thread_info_key, NULL);
597 * mono_thread_info_is_exiting:
599 * Return whenever the current thread is exiting, i.e. it is running pthread
603 mono_thread_info_is_exiting (void)
605 #if defined(__MACH__)
606 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
613 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
616 threads_callbacks = *callbacks;
617 thread_info_size = info_size;
619 res = mono_native_tls_alloc (&thread_info_key, NULL);
620 res = mono_native_tls_alloc (&thread_exited_key, NULL);
622 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
623 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
628 #ifndef HAVE_KW_THREAD
629 res = mono_native_tls_alloc (&small_id_key, NULL);
633 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
635 mono_coop_sem_init (&global_suspend_semaphore, 1);
636 mono_os_sem_init (&suspend_semaphore, 0);
638 mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
639 mono_thread_smr_init ();
640 mono_threads_init_platform ();
641 mono_threads_init_coop ();
642 mono_threads_init_abort_syscall ();
644 #if defined(__MACH__)
645 mono_mach_init (thread_info_key);
648 mono_threads_inited = TRUE;
650 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
654 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
656 runtime_callbacks = *callbacks;
659 MonoThreadInfoRuntimeCallbacks *
660 mono_threads_get_runtime_callbacks (void)
662 return &runtime_callbacks;
666 Signal that the current thread wants to be suspended.
667 This function can be called without holding the suspend lock held.
668 To finish suspending, call mono_suspend_check.
671 mono_thread_info_begin_self_suspend (void)
673 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
677 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
678 mono_threads_transition_request_self_suspension (info);
682 mono_thread_info_end_self_suspend (void)
684 MonoThreadInfo *info;
686 info = mono_thread_info_current ();
689 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
691 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
693 /* commit the saved state and notify others if needed */
694 switch (mono_threads_transition_state_poll (info)) {
695 case SelfSuspendResumed:
697 case SelfSuspendWait:
698 mono_thread_info_wait_for_resume (info);
700 case SelfSuspendNotifyAndWait:
701 mono_threads_notify_initiator_of_suspend (info);
702 mono_thread_info_wait_for_resume (info);
703 mono_threads_notify_initiator_of_resume (info);
709 mono_thread_info_core_resume (MonoThreadInfo *info)
711 gboolean res = FALSE;
712 if (info->create_suspended) {
713 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
714 /* 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 */
715 info->create_suspended = FALSE;
716 mono_threads_core_resume_created (info, tid);
720 switch (mono_threads_transition_request_resume (info)) {
727 case ResumeInitSelfResume:
728 resume_self_suspended (info);
731 case ResumeInitAsyncResume:
732 resume_async_suspended (info);
735 case ResumeInitBlockingResume:
736 resume_blocking_suspended (info);
745 mono_thread_info_resume (MonoNativeThreadId tid)
747 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
748 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
749 MonoThreadInfo *info;
751 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
753 mono_thread_info_suspend_lock ();
755 info = mono_thread_info_lookup (tid); /*info on HP1*/
761 result = mono_thread_info_core_resume (info);
763 //Wait for the pending resume to finish
764 mono_threads_wait_pending_operations ();
767 mono_thread_info_suspend_unlock ();
768 mono_hazard_pointer_clear (hp, 1);
773 mono_thread_info_begin_suspend (MonoThreadInfo *info)
775 switch (mono_threads_transition_request_async_suspension (info)) {
776 case AsyncSuspendAlreadySuspended:
777 case AsyncSuspendBlocking:
779 case AsyncSuspendWait:
780 mono_threads_add_to_pending_operation_set (info);
782 case AsyncSuspendInitSuspend:
783 return begin_async_suspend (info, FALSE);
785 g_assert_not_reached ();
790 mono_thread_info_begin_resume (MonoThreadInfo *info)
792 return mono_thread_info_core_resume (info);
796 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
798 return check_async_suspend (info);
802 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
803 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
806 is_thread_in_critical_region (MonoThreadInfo *info)
810 gpointer stack_start;
811 MonoThreadUnwindState *state;
813 /* Are we inside a system critical region? */
814 if (info->inside_critical_region)
817 /* Are we inside a GC critical region? */
818 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
822 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
823 state = mono_thread_info_get_suspend_state (info);
824 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
827 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
828 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
829 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
832 ji = mono_jit_info_table_find (
833 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
834 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
839 method = mono_jit_info_get_method (ji);
841 return threads_callbacks.mono_method_is_critical (method);
845 mono_thread_info_in_critical_location (MonoThreadInfo *info)
847 return is_thread_in_critical_region (info);
851 The return value is only valid until a matching mono_thread_info_resume is called
853 static MonoThreadInfo*
854 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
856 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
857 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
861 switch (mono_threads_transition_request_async_suspension (info)) {
862 case AsyncSuspendAlreadySuspended:
863 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
865 case AsyncSuspendWait:
866 mono_threads_add_to_pending_operation_set (info);
868 case AsyncSuspendInitSuspend:
869 if (!begin_async_suspend (info, interrupt_kernel)) {
870 mono_hazard_pointer_clear (hp, 1);
874 case AsyncSuspendBlocking:
875 if (interrupt_kernel)
876 mono_threads_core_abort_syscall (info);
880 g_assert_not_reached ();
883 //Wait for the pending suspend to finish
884 mono_threads_wait_pending_operations ();
886 if (!check_async_suspend (info)) {
887 mono_hazard_pointer_clear (hp, 1);
893 static MonoThreadInfo*
894 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
896 MonoThreadInfo *info = NULL;
897 int sleep_duration = 0;
899 if (!(info = suspend_sync (id, interrupt_kernel))) {
900 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
904 /*WARNING: We now are in interrupt context until we resume the thread. */
905 if (!is_thread_in_critical_region (info))
908 if (!mono_thread_info_core_resume (info)) {
909 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
912 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
914 /* Wait for the pending resume to finish */
915 mono_threads_wait_pending_operations ();
917 if (sleep_duration == 0)
918 mono_thread_info_yield ();
920 g_usleep (sleep_duration);
922 sleep_duration += 10;
928 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
931 MonoThreadInfo *info = NULL;
932 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
934 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
935 /*FIXME: unify this with self-suspend*/
936 g_assert (id != mono_native_thread_id_get ());
938 /* This can block during stw */
939 mono_thread_info_suspend_lock ();
940 mono_threads_begin_global_suspend ();
942 info = suspend_sync_nolock (id, interrupt_kernel);
946 switch (result = callback (info, user_data)) {
947 case MonoResumeThread:
948 mono_hazard_pointer_set (hp, 1, info);
949 mono_thread_info_core_resume (info);
950 mono_threads_wait_pending_operations ();
955 g_error ("Invalid suspend_and_run callback return value %d", result);
959 mono_hazard_pointer_clear (hp, 1);
960 mono_threads_end_global_suspend ();
961 mono_thread_info_suspend_unlock ();
965 Inject an assynchronous call into the target thread. The target thread must be suspended and
966 only a single async call can be setup for a given suspend cycle.
967 This async call must cause stack unwinding as the current implementation doesn't save enough state
968 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
969 currently used only to deliver exceptions.
972 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
974 /* An async call can only be setup on an async suspended thread */
975 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
976 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
977 g_assert (!info->async_target);
978 info->async_target = target_func;
979 /* This is not GC tracked */
980 info->user_data = user_data;
984 The suspend lock is held during any suspend in progress.
985 A GC that has safepoints must take this lock as part of its
986 STW to make sure no unsafe pending suspend is in progress.
989 mono_thread_info_suspend_lock (void)
991 int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
992 g_assert (res != -1);
996 mono_thread_info_suspend_unlock (void)
998 mono_coop_sem_post (&global_suspend_semaphore);
1002 * This is a very specific function whose only purpose is to
1003 * break a given thread from socket syscalls.
1005 * This only exists because linux won't fail a call to connect
1006 * if the underlying is closed.
1008 * TODO We should cleanup and unify this with the other syscall abort
1012 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1014 MonoThreadHazardPointers *hp;
1015 MonoThreadInfo *info;
1017 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1020 hp = mono_hazard_pointer_get ();
1021 info = mono_thread_info_lookup (tid);
1025 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1026 mono_hazard_pointer_clear (hp, 1);
1030 mono_thread_info_suspend_lock ();
1031 mono_threads_begin_global_suspend ();
1033 mono_threads_core_abort_syscall (info);
1034 mono_threads_wait_pending_operations ();
1036 mono_hazard_pointer_clear (hp, 1);
1038 mono_threads_end_global_suspend ();
1039 mono_thread_info_suspend_unlock ();
1043 mono_thread_info_unified_management_enabled (void)
1045 return unified_suspend_enabled;
1049 * mono_thread_info_set_is_async_context:
1051 * Set whenever the current thread is in an async context. Some runtime functions might behave
1052 * differently while in an async context in order to be async safe.
1055 mono_thread_info_set_is_async_context (gboolean async_context)
1057 MonoThreadInfo *info = mono_thread_info_current ();
1060 info->is_async_context = async_context;
1064 mono_thread_info_is_async_context (void)
1066 MonoThreadInfo *info = mono_thread_info_current ();
1069 return info->is_async_context;
1075 * mono_threads_create_thread:
1077 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1078 * Returns: a windows or io-layer handle for the thread.
1081 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1083 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1087 * mono_thread_info_get_stack_bounds:
1089 * Return the address and size of the current threads stack. Return NULL as the
1090 * stack address if the stack address cannot be determined.
1093 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1095 guint8 *current = (guint8 *)&stsize;
1096 mono_threads_core_get_stack_bounds (staddr, stsize);
1100 /* Sanity check the result */
1101 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1103 /* When running under emacs, sometimes staddr is not aligned to a page size */
1104 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1108 mono_thread_info_yield (void)
1110 return mono_threads_core_yield ();
1112 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1113 static MonoCoopMutex sleep_mutex;
1114 static MonoCoopCond sleep_cond;
1117 sleep_initialize (void)
1119 mono_coop_mutex_init (&sleep_mutex);
1120 mono_coop_cond_init (&sleep_cond);
1124 sleep_interrupt (gpointer data)
1126 mono_coop_mutex_lock (&sleep_mutex);
1127 mono_coop_cond_broadcast (&sleep_cond);
1128 mono_coop_mutex_unlock (&sleep_mutex);
1131 static inline guint32
1132 sleep_interruptable (guint32 ms, gboolean *alerted)
1136 g_assert (INFINITE == G_MAXUINT32);
1142 end = mono_100ns_ticks () + (ms * 1000 * 10);
1144 mono_lazy_initialize (&sleep_init, sleep_initialize);
1146 mono_coop_mutex_lock (&sleep_mutex);
1149 if (ms != INFINITE) {
1150 now = mono_100ns_ticks ();
1155 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1157 mono_coop_mutex_unlock (&sleep_mutex);
1158 return WAIT_IO_COMPLETION;
1162 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1164 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1166 mono_thread_info_uninstall_interrupt (alerted);
1168 mono_coop_mutex_unlock (&sleep_mutex);
1169 return WAIT_IO_COMPLETION;
1173 mono_coop_mutex_unlock (&sleep_mutex);
1179 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1182 MonoThreadInfo *info;
1184 mono_thread_info_yield ();
1186 info = mono_thread_info_current ();
1187 if (info && mono_thread_info_is_interrupt_state (info))
1188 return WAIT_IO_COMPLETION;
1194 return sleep_interruptable (ms, alerted);
1196 MONO_PREPARE_BLOCKING;
1198 if (ms == INFINITE) {
1201 Sleep (G_MAXUINT32);
1203 sleep (G_MAXUINT32);
1208 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1209 struct timespec start, target;
1211 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1212 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1213 g_assert (ret == 0);
1216 target.tv_sec += ms / 1000;
1217 target.tv_nsec += (ms % 1000) * 1000000;
1218 if (target.tv_nsec > 999999999) {
1219 target.tv_nsec -= 999999999;
1224 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1229 struct timespec req, rem;
1231 req.tv_sec = ms / 1000;
1232 req.tv_nsec = (ms % 1000) * 1000000;
1235 memset (&rem, 0, sizeof (rem));
1236 ret = nanosleep (&req, &rem);
1238 #endif /* __linux__ */
1241 MONO_FINISH_BLOCKING;
1247 mono_thread_info_usleep (guint64 us)
1249 MONO_PREPARE_BLOCKING;
1251 MONO_FINISH_BLOCKING;
1256 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1258 return ((MonoThreadInfo*)info)->tls [key];
1262 * mono_threads_info_tls_set:
1264 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1265 * values of TLS variables for threads other than the current thread.
1266 * This should only be used for infrequently changing TLS variables, and it should
1267 * be paired with setting the real TLS variable since this provides no GC tracking.
1270 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1272 ((MonoThreadInfo*)info)->tls [key] = value;
1276 * mono_thread_info_exit:
1278 * Exit the current thread.
1279 * This function doesn't return.
1282 mono_thread_info_exit (void)
1284 mono_threads_core_exit (0);
1288 * mono_thread_info_open_handle:
1290 * Return a io-layer/win32 handle for the current thread.
1291 * The handle need to be closed by calling CloseHandle () when it is no
1295 mono_thread_info_open_handle (void)
1297 return mono_threads_core_open_handle ();
1301 * mono_threads_open_thread_handle:
1303 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1304 * The handle need to be closed by calling CloseHandle () when it is no
1308 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1310 return mono_threads_core_open_thread_handle (handle, tid);
1314 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1316 mono_threads_core_set_name (tid, name);
1319 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1321 struct _MonoThreadInfoInterruptToken {
1322 void (*callback) (gpointer data);
1327 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1329 * - @callback: must be able to be called from another thread and always cancel the wait
1330 * - @data: passed to the callback
1331 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1332 * if set to TRUE, it must mean that the thread is in interrupted state
1335 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1337 MonoThreadInfo *info;
1338 MonoThreadInfoInterruptToken *previous_token, *token;
1340 g_assert (callback);
1342 g_assert (interrupted);
1343 *interrupted = FALSE;
1345 info = mono_thread_info_current ();
1348 /* The memory of this token can be freed at 2 places:
1349 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1350 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1351 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1352 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1353 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1354 token->callback = callback;
1357 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1359 if (previous_token) {
1360 if (previous_token != INTERRUPT_STATE)
1361 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1365 *interrupted = TRUE;
1368 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1369 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1373 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1375 MonoThreadInfo *info;
1376 MonoThreadInfoInterruptToken *previous_token;
1378 g_assert (interrupted);
1379 *interrupted = FALSE;
1381 info = mono_thread_info_current ();
1384 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1386 /* only the installer can uninstall the token */
1387 g_assert (previous_token);
1389 if (previous_token == INTERRUPT_STATE) {
1390 /* if it is interrupted, then it is going to be freed in finish interrupt */
1391 *interrupted = TRUE;
1393 g_free (previous_token);
1396 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1397 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1400 static MonoThreadInfoInterruptToken*
1401 set_interrupt_state (MonoThreadInfo *info)
1403 MonoThreadInfoInterruptToken *token, *previous_token;
1407 /* Atomically obtain the token the thread is
1408 * waiting on, and change it to a flag value. */
1411 previous_token = info->interrupt_token;
1413 /* Already interrupted */
1414 if (previous_token == INTERRUPT_STATE) {
1419 token = previous_token;
1420 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1426 * mono_thread_info_prepare_interrupt:
1428 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1429 * - if the thread calls one of the WaitFor functions, the function will return with
1430 * WAIT_IO_COMPLETION instead of waiting
1431 * - if the thread was waiting when this function was called, the wait will be broken
1433 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1434 * didn't receive the interrupt signal yet, in this case it should call the wait function
1435 * again. This essentially means that the target thread will busy wait until it is ready to
1436 * process the interruption.
1438 MonoThreadInfoInterruptToken*
1439 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1441 MonoThreadInfoInterruptToken *token;
1443 token = set_interrupt_state (info);
1445 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1446 mono_thread_info_get_tid (info), token);
1452 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1454 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1459 g_assert (token->callback);
1461 token->callback (token->data);
1467 mono_thread_info_self_interrupt (void)
1469 MonoThreadInfo *info;
1470 MonoThreadInfoInterruptToken *token;
1472 info = mono_thread_info_current ();
1475 token = set_interrupt_state (info);
1478 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1479 mono_thread_info_get_tid (info));
1482 /* Clear the interrupted flag of the current thread, set with
1483 * mono_thread_info_self_interrupt, so it can wait again */
1485 mono_thread_info_clear_self_interrupt ()
1487 MonoThreadInfo *info;
1488 MonoThreadInfoInterruptToken *previous_token;
1490 info = mono_thread_info_current ();
1493 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1494 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1496 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1500 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1503 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1507 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1511 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1512 g_string_append_printf (text, "not waiting");
1513 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1514 g_string_append_printf (text, "interrupted state");
1516 g_string_append_printf (text, "waiting");
1519 /* info must be self or be held in a hazard pointer. */
1521 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1523 MonoAsyncJob old_job;
1525 old_job = (MonoAsyncJob) info->service_requests;
1528 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1533 mono_threads_consume_async_jobs (void)
1535 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1538 return (MonoAsyncJob) 0;
1540 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);