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>
35 #include <mono/utils/mach-support.h>
39 Mutex that makes sure only a single thread can be suspending others.
40 Suspend is a very racy operation since it requires restarting until
41 the target thread is not on an unsafe region.
43 We could implement this using critical regions, but would be much much
44 harder for an operation that is hardly performance critical.
46 The GC has to acquire this lock before starting a STW to make sure
47 a runtime suspend won't make it wronly see a thread in a safepoint
48 when it is in fact not.
50 static MonoCoopSem global_suspend_semaphore;
52 static size_t thread_info_size;
53 static MonoThreadInfoCallbacks threads_callbacks;
54 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
55 static MonoNativeTlsKey thread_info_key, thread_exited_key;
57 static __thread guint32 tls_small_id MONO_TLS_FAST;
59 static MonoNativeTlsKey small_id_key;
61 static MonoLinkedListSet thread_list;
62 static gboolean mono_threads_inited = FALSE;
64 static MonoSemType suspend_semaphore;
65 static size_t pending_suspends;
66 static gboolean unified_suspend_enabled;
68 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
71 #define SLEEP_DURATION_BEFORE_WARNING (10)
73 #define SLEEP_DURATION_BEFORE_ABORT 200
75 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
78 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
80 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
81 InterlockedIncrement (&abort_posts);
82 mono_os_sem_post (&suspend_semaphore);
86 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
88 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
89 InterlockedIncrement (&suspend_posts);
90 mono_os_sem_post (&suspend_semaphore);
94 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
96 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
97 InterlockedIncrement (&resume_posts);
98 mono_os_sem_post (&suspend_semaphore);
102 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
104 if (mono_threads_is_coop_enabled ()) {
105 /* There's nothing else to do after we async request the thread to suspend */
106 mono_threads_add_to_pending_operation_set (info);
110 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
114 check_async_suspend (MonoThreadInfo *info)
116 if (mono_threads_is_coop_enabled ()) {
117 /* Async suspend can't async fail on coop */
121 return mono_threads_core_check_suspend_result (info);
125 resume_async_suspended (MonoThreadInfo *info)
127 if (mono_threads_is_coop_enabled ())
128 g_assert_not_reached ();
130 g_assert (mono_threads_core_begin_async_resume (info));
134 resume_self_suspended (MonoThreadInfo* info)
136 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
137 mono_os_sem_post (&info->resume_semaphore);
141 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
144 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
145 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
146 g_assert (res != -1);
150 resume_blocking_suspended (MonoThreadInfo* info)
152 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
153 mono_os_sem_post (&info->resume_semaphore);
157 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
159 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
161 InterlockedIncrement (&pending_ops);
165 mono_threads_begin_global_suspend (void)
167 size_t ps = pending_suspends;
168 if (G_UNLIKELY (ps != 0))
169 g_error ("pending_suspends = %d, but must be 0", ps);
170 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,
171 abort_posts, waits_done, pending_ops);
172 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
173 mono_threads_coop_begin_global_suspend ();
177 mono_threads_end_global_suspend (void)
179 size_t ps = pending_suspends;
180 if (G_UNLIKELY (ps != 0))
181 g_error ("pending_suspends = %d, but must be 0", ps);
182 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
183 abort_posts, waits_done, pending_ops);
184 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
185 mono_threads_coop_end_global_suspend ();
191 MonoThreadInfo *cur = mono_thread_info_current ();
193 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
194 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
195 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
196 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
197 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
198 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
199 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
200 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
201 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
202 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
204 FOREACH_THREAD_SAFE (info) {
206 char thread_name [256] = { 0 };
207 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
209 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" : "" );
211 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" : "" );
213 } FOREACH_THREAD_SAFE_END
217 mono_threads_wait_pending_operations (void)
220 int c = pending_suspends;
222 /* Wait threads to park */
223 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
224 if (pending_suspends) {
225 MonoStopwatch suspension_time;
226 mono_stopwatch_start (&suspension_time);
227 for (i = 0; i < pending_suspends; ++i) {
228 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
229 InterlockedIncrement (&waits_done);
230 if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE))
232 mono_stopwatch_stop (&suspension_time);
236 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
237 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);
239 mono_stopwatch_stop (&suspension_time);
240 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
244 pending_suspends = 0;
250 //Thread initialization code
252 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
255 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
258 mono_hazard_pointer_clear (hp, 0);
260 mono_hazard_pointer_clear (hp, 1);
262 mono_hazard_pointer_clear (hp, 2);
266 If return non null Hazard Pointer 1 holds the return value.
269 mono_thread_info_lookup (MonoNativeThreadId id)
271 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
273 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
274 mono_hazard_pointer_clear_all (hp, -1);
278 mono_hazard_pointer_clear_all (hp, 1);
279 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
283 mono_thread_info_insert (MonoThreadInfo *info)
285 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
287 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
288 mono_hazard_pointer_clear_all (hp, -1);
292 mono_hazard_pointer_clear_all (hp, -1);
297 mono_thread_info_remove (MonoThreadInfo *info)
299 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
302 THREADS_DEBUG ("removing info %p\n", info);
303 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
304 mono_hazard_pointer_clear_all (hp, -1);
309 free_thread_info (gpointer mem)
311 MonoThreadInfo *info = (MonoThreadInfo *) mem;
313 mono_os_sem_destroy (&info->resume_semaphore);
314 mono_threads_platform_free (info);
320 mono_thread_info_register_small_id (void)
322 int small_id = mono_thread_small_id_alloc ();
323 #ifdef HAVE_KW_THREAD
324 tls_small_id = small_id;
326 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
332 register_thread (MonoThreadInfo *info, gpointer baseptr)
335 guint8 *staddr = NULL;
336 int small_id = mono_thread_info_register_small_id ();
338 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
339 info->small_id = small_id;
341 mono_os_sem_init (&info->resume_semaphore, 0);
343 /*set TLS early so SMR works */
344 mono_native_tls_set_value (thread_info_key, info);
346 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
348 if (threads_callbacks.thread_register) {
349 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
350 // g_warning ("thread registation failed\n");
356 mono_thread_info_get_stack_bounds (&staddr, &stsize);
359 info->stack_start_limit = staddr;
360 info->stack_end = staddr + stsize;
362 info->stackdata = g_byte_array_new ();
364 mono_threads_platform_register (info);
367 Transition it before taking any locks or publishing itself to reduce the chance
368 of others witnessing a detached thread.
369 We can reasonably expect that until this thread gets published, no other thread will
370 try to manipulate it.
372 mono_threads_transition_attach (info);
373 mono_thread_info_suspend_lock ();
374 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
375 result = mono_thread_info_insert (info);
377 mono_thread_info_suspend_unlock ();
382 unregister_thread (void *arg)
384 MonoThreadInfo *info = (MonoThreadInfo *) arg;
385 int small_id = info->small_id;
388 THREADS_DEBUG ("unregistering info %p\n", info);
390 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
392 mono_threads_core_unregister (info);
395 * TLS destruction order is not reliable so small_id might be cleaned up
398 #ifndef HAVE_KW_THREAD
399 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
403 First perform the callback that requires no locks.
404 This callback has the potential of taking other locks, so we do it before.
405 After it completes, the thread remains functional.
407 if (threads_callbacks.thread_detach)
408 threads_callbacks.thread_detach (info);
410 mono_thread_info_suspend_lock ();
413 Now perform the callback that must be done under locks.
414 This will render the thread useless and non-suspendable, so it must
415 be done while holding the suspend lock to give no other thread chance
418 if (threads_callbacks.thread_unregister)
419 threads_callbacks.thread_unregister (info);
420 mono_threads_unregister_current_thread (info);
421 mono_threads_transition_detach (info);
423 mono_thread_info_suspend_unlock ();
425 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
427 /*now it's safe to free the thread info.*/
428 mono_thread_hazardous_free_or_queue (info, free_thread_info, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
429 mono_thread_small_id_free (small_id);
433 thread_exited_dtor (void *arg)
435 #if defined(__MACH__)
437 * Since we use pthread dtors to clean up thread data, if a thread
438 * is attached to the runtime by another pthread dtor after our dtor
439 * has ran, it will never be detached, leading to various problems
440 * since the thread ids etc. will be reused while they are still in
441 * the threads hashtables etc.
442 * Dtors are called in a loop until all user tls entries are 0,
443 * but the loop has a maximum count (4), so if we set the tls
444 * variable every time, it will remain set when system tls dtors
445 * are ran. This allows mono_thread_info_is_exiting () to detect
446 * whenever the thread is exiting, even if it is executed from a
447 * system tls dtor (i.e. obj-c dealloc methods).
449 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
454 * Removes the current thread from the thread list.
455 * This must be called from the thread unregister callback and nowhere else.
456 * The current thread must be passed as TLS might have already been cleaned up.
459 mono_threads_unregister_current_thread (MonoThreadInfo *info)
462 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
463 result = mono_thread_info_remove (info);
468 mono_thread_info_current_unchecked (void)
470 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
475 mono_thread_info_current (void)
477 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
481 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
484 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
485 The way to distinguish between before, during and after cleanup is the following:
487 -If the TLS key is set, cleanup has not begun;
488 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
489 -If the thread is nowhere to be found, cleanup has finished.
491 We cannot function after cleanup since there's no way to ensure what will happen.
495 /*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 */
496 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
502 mono_thread_info_get_small_id (void)
504 #ifdef HAVE_KW_THREAD
507 gpointer val = mono_native_tls_get_value (small_id_key);
510 return GPOINTER_TO_INT (val) - 1;
515 mono_thread_info_list_head (void)
521 * mono_threads_attach_tools_thread
523 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
525 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
526 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
529 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
530 * doing things like resolving backtraces in their background processing thread.
533 mono_threads_attach_tools_thread (void)
536 MonoThreadInfo *info;
538 /* Must only be called once */
539 g_assert (!mono_native_tls_get_value (thread_info_key));
541 while (!mono_threads_inited) {
542 mono_thread_info_usleep (10);
545 info = mono_thread_info_attach (&dummy);
548 info->tools_thread = TRUE;
552 mono_thread_info_attach (void *baseptr)
554 MonoThreadInfo *info;
555 if (!mono_threads_inited)
558 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
559 * thread is created before an embedding API user initialized Mono. */
560 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
563 g_assert (mono_threads_inited);
566 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
568 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
569 THREADS_DEBUG ("attaching %p\n", info);
570 if (!register_thread (info, baseptr))
572 } else if (threads_callbacks.thread_attach) {
573 threads_callbacks.thread_attach (info);
579 mono_thread_info_detach (void)
581 MonoThreadInfo *info;
582 if (!mono_threads_inited)
584 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
585 * is created before an embedding API user initialized Mono. */
586 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
589 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
591 THREADS_DEBUG ("detaching %p\n", info);
592 unregister_thread (info);
593 mono_native_tls_set_value (thread_info_key, NULL);
598 * mono_thread_info_is_exiting:
600 * Return whenever the current thread is exiting, i.e. it is running pthread
604 mono_thread_info_is_exiting (void)
606 #if defined(__MACH__)
607 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
614 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
617 threads_callbacks = *callbacks;
618 thread_info_size = info_size;
620 res = mono_native_tls_alloc (&thread_info_key, NULL);
621 res = mono_native_tls_alloc (&thread_exited_key, NULL);
623 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
624 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
629 #ifndef HAVE_KW_THREAD
630 res = mono_native_tls_alloc (&small_id_key, NULL);
634 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
636 mono_coop_sem_init (&global_suspend_semaphore, 1);
637 mono_os_sem_init (&suspend_semaphore, 0);
639 mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
640 mono_thread_smr_init ();
641 mono_threads_init_platform ();
642 mono_threads_init_coop ();
643 mono_threads_init_abort_syscall ();
645 #if defined(__MACH__)
646 mono_mach_init (thread_info_key);
649 mono_threads_inited = TRUE;
651 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
655 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
657 runtime_callbacks = *callbacks;
660 MonoThreadInfoRuntimeCallbacks *
661 mono_threads_get_runtime_callbacks (void)
663 return &runtime_callbacks;
667 Signal that the current thread wants to be suspended.
668 This function can be called without holding the suspend lock held.
669 To finish suspending, call mono_suspend_check.
672 mono_thread_info_begin_self_suspend (void)
674 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
678 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
679 mono_threads_transition_request_self_suspension (info);
683 mono_thread_info_end_self_suspend (void)
685 MonoThreadInfo *info;
687 info = mono_thread_info_current ();
690 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
692 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
694 /* commit the saved state and notify others if needed */
695 switch (mono_threads_transition_state_poll (info)) {
696 case SelfSuspendResumed:
698 case SelfSuspendWait:
699 mono_thread_info_wait_for_resume (info);
701 case SelfSuspendNotifyAndWait:
702 mono_threads_notify_initiator_of_suspend (info);
703 mono_thread_info_wait_for_resume (info);
704 mono_threads_notify_initiator_of_resume (info);
710 mono_thread_info_core_resume (MonoThreadInfo *info)
712 gboolean res = FALSE;
713 if (info->create_suspended) {
714 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
715 /* 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 */
716 info->create_suspended = FALSE;
717 mono_threads_core_resume_created (info, tid);
721 switch (mono_threads_transition_request_resume (info)) {
728 case ResumeInitSelfResume:
729 resume_self_suspended (info);
732 case ResumeInitAsyncResume:
733 resume_async_suspended (info);
736 case ResumeInitBlockingResume:
737 resume_blocking_suspended (info);
746 mono_thread_info_resume (MonoNativeThreadId tid)
748 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
749 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
750 MonoThreadInfo *info;
752 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
754 mono_thread_info_suspend_lock ();
756 info = mono_thread_info_lookup (tid); /*info on HP1*/
762 result = mono_thread_info_core_resume (info);
764 //Wait for the pending resume to finish
765 mono_threads_wait_pending_operations ();
768 mono_thread_info_suspend_unlock ();
769 mono_hazard_pointer_clear (hp, 1);
774 mono_thread_info_begin_suspend (MonoThreadInfo *info)
776 switch (mono_threads_transition_request_async_suspension (info)) {
777 case AsyncSuspendAlreadySuspended:
778 case AsyncSuspendBlocking:
780 case AsyncSuspendWait:
781 mono_threads_add_to_pending_operation_set (info);
783 case AsyncSuspendInitSuspend:
784 return begin_async_suspend (info, FALSE);
786 g_assert_not_reached ();
791 mono_thread_info_begin_resume (MonoThreadInfo *info)
793 return mono_thread_info_core_resume (info);
797 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
799 return check_async_suspend (info);
803 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
804 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
807 is_thread_in_critical_region (MonoThreadInfo *info)
811 gpointer stack_start;
812 MonoThreadUnwindState *state;
814 /* Are we inside a system critical region? */
815 if (info->inside_critical_region)
818 /* Are we inside a GC critical region? */
819 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
823 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
824 state = mono_thread_info_get_suspend_state (info);
825 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
828 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
829 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
830 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
833 ji = mono_jit_info_table_find (
834 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
835 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
840 method = mono_jit_info_get_method (ji);
842 return threads_callbacks.mono_method_is_critical (method);
846 mono_thread_info_in_critical_location (MonoThreadInfo *info)
848 return is_thread_in_critical_region (info);
852 The return value is only valid until a matching mono_thread_info_resume is called
854 static MonoThreadInfo*
855 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
857 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
858 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
862 switch (mono_threads_transition_request_async_suspension (info)) {
863 case AsyncSuspendAlreadySuspended:
864 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
866 case AsyncSuspendWait:
867 mono_threads_add_to_pending_operation_set (info);
869 case AsyncSuspendInitSuspend:
870 if (!begin_async_suspend (info, interrupt_kernel)) {
871 mono_hazard_pointer_clear (hp, 1);
875 case AsyncSuspendBlocking:
876 if (interrupt_kernel)
877 mono_threads_core_abort_syscall (info);
881 g_assert_not_reached ();
884 //Wait for the pending suspend to finish
885 mono_threads_wait_pending_operations ();
887 if (!check_async_suspend (info)) {
888 mono_hazard_pointer_clear (hp, 1);
894 static MonoThreadInfo*
895 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
897 MonoThreadInfo *info = NULL;
898 int sleep_duration = 0;
900 if (!(info = suspend_sync (id, interrupt_kernel))) {
901 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
905 /*WARNING: We now are in interrupt context until we resume the thread. */
906 if (!is_thread_in_critical_region (info))
909 if (!mono_thread_info_core_resume (info)) {
910 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
913 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
915 /* Wait for the pending resume to finish */
916 mono_threads_wait_pending_operations ();
918 if (sleep_duration == 0)
919 mono_thread_info_yield ();
921 g_usleep (sleep_duration);
923 sleep_duration += 10;
929 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
932 MonoThreadInfo *info = NULL;
933 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
935 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
936 /*FIXME: unify this with self-suspend*/
937 g_assert (id != mono_native_thread_id_get ());
939 /* This can block during stw */
940 mono_thread_info_suspend_lock ();
941 mono_threads_begin_global_suspend ();
943 info = suspend_sync_nolock (id, interrupt_kernel);
947 switch (result = callback (info, user_data)) {
948 case MonoResumeThread:
949 mono_hazard_pointer_set (hp, 1, info);
950 mono_thread_info_core_resume (info);
951 mono_threads_wait_pending_operations ();
956 g_error ("Invalid suspend_and_run callback return value %d", result);
960 mono_hazard_pointer_clear (hp, 1);
961 mono_threads_end_global_suspend ();
962 mono_thread_info_suspend_unlock ();
966 Inject an assynchronous call into the target thread. The target thread must be suspended and
967 only a single async call can be setup for a given suspend cycle.
968 This async call must cause stack unwinding as the current implementation doesn't save enough state
969 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
970 currently used only to deliver exceptions.
973 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
975 /* An async call can only be setup on an async suspended thread */
976 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
977 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
978 g_assert (!info->async_target);
979 info->async_target = target_func;
980 /* This is not GC tracked */
981 info->user_data = user_data;
985 The suspend lock is held during any suspend in progress.
986 A GC that has safepoints must take this lock as part of its
987 STW to make sure no unsafe pending suspend is in progress.
990 mono_thread_info_suspend_lock (void)
992 int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
993 g_assert (res != -1);
997 mono_thread_info_suspend_unlock (void)
999 mono_coop_sem_post (&global_suspend_semaphore);
1003 * This is a very specific function whose only purpose is to
1004 * break a given thread from socket syscalls.
1006 * This only exists because linux won't fail a call to connect
1007 * if the underlying is closed.
1009 * TODO We should cleanup and unify this with the other syscall abort
1013 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1015 MonoThreadHazardPointers *hp;
1016 MonoThreadInfo *info;
1018 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1021 hp = mono_hazard_pointer_get ();
1022 info = mono_thread_info_lookup (tid);
1026 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1027 mono_hazard_pointer_clear (hp, 1);
1031 mono_thread_info_suspend_lock ();
1032 mono_threads_begin_global_suspend ();
1034 mono_threads_core_abort_syscall (info);
1035 mono_threads_wait_pending_operations ();
1037 mono_hazard_pointer_clear (hp, 1);
1039 mono_threads_end_global_suspend ();
1040 mono_thread_info_suspend_unlock ();
1044 mono_thread_info_unified_management_enabled (void)
1046 return unified_suspend_enabled;
1050 * mono_thread_info_set_is_async_context:
1052 * Set whenever the current thread is in an async context. Some runtime functions might behave
1053 * differently while in an async context in order to be async safe.
1056 mono_thread_info_set_is_async_context (gboolean async_context)
1058 MonoThreadInfo *info = mono_thread_info_current ();
1061 info->is_async_context = async_context;
1065 mono_thread_info_is_async_context (void)
1067 MonoThreadInfo *info = mono_thread_info_current ();
1070 return info->is_async_context;
1076 * mono_threads_create_thread:
1078 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1079 * Returns: a windows or io-layer handle for the thread.
1082 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1084 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1088 * mono_thread_info_get_stack_bounds:
1090 * Return the address and size of the current threads stack. Return NULL as the
1091 * stack address if the stack address cannot be determined.
1094 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1096 guint8 *current = (guint8 *)&stsize;
1097 mono_threads_core_get_stack_bounds (staddr, stsize);
1101 /* Sanity check the result */
1102 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1104 /* When running under emacs, sometimes staddr is not aligned to a page size */
1105 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1109 mono_thread_info_yield (void)
1111 return mono_threads_core_yield ();
1113 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1114 static MonoCoopMutex sleep_mutex;
1115 static MonoCoopCond sleep_cond;
1118 sleep_initialize (void)
1120 mono_coop_mutex_init (&sleep_mutex);
1121 mono_coop_cond_init (&sleep_cond);
1125 sleep_interrupt (gpointer data)
1127 mono_coop_mutex_lock (&sleep_mutex);
1128 mono_coop_cond_broadcast (&sleep_cond);
1129 mono_coop_mutex_unlock (&sleep_mutex);
1132 static inline guint32
1133 sleep_interruptable (guint32 ms, gboolean *alerted)
1137 g_assert (INFINITE == G_MAXUINT32);
1143 end = mono_100ns_ticks () + (ms * 1000 * 10);
1145 mono_lazy_initialize (&sleep_init, sleep_initialize);
1147 mono_coop_mutex_lock (&sleep_mutex);
1150 if (ms != INFINITE) {
1151 now = mono_100ns_ticks ();
1156 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1158 mono_coop_mutex_unlock (&sleep_mutex);
1159 return WAIT_IO_COMPLETION;
1163 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1165 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1167 mono_thread_info_uninstall_interrupt (alerted);
1169 mono_coop_mutex_unlock (&sleep_mutex);
1170 return WAIT_IO_COMPLETION;
1174 mono_coop_mutex_unlock (&sleep_mutex);
1180 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1183 MonoThreadInfo *info;
1185 mono_thread_info_yield ();
1187 info = mono_thread_info_current ();
1188 if (info && mono_thread_info_is_interrupt_state (info))
1189 return WAIT_IO_COMPLETION;
1195 return sleep_interruptable (ms, alerted);
1197 MONO_PREPARE_BLOCKING;
1199 if (ms == INFINITE) {
1202 Sleep (G_MAXUINT32);
1204 sleep (G_MAXUINT32);
1209 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1210 struct timespec start, target;
1212 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1213 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1214 g_assert (ret == 0);
1217 target.tv_sec += ms / 1000;
1218 target.tv_nsec += (ms % 1000) * 1000000;
1219 if (target.tv_nsec > 999999999) {
1220 target.tv_nsec -= 999999999;
1225 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1230 struct timespec req, rem;
1232 req.tv_sec = ms / 1000;
1233 req.tv_nsec = (ms % 1000) * 1000000;
1236 memset (&rem, 0, sizeof (rem));
1237 ret = nanosleep (&req, &rem);
1239 #endif /* __linux__ */
1242 MONO_FINISH_BLOCKING;
1248 mono_thread_info_usleep (guint64 us)
1250 MONO_PREPARE_BLOCKING;
1252 MONO_FINISH_BLOCKING;
1257 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1259 return ((MonoThreadInfo*)info)->tls [key];
1263 * mono_threads_info_tls_set:
1265 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1266 * values of TLS variables for threads other than the current thread.
1267 * This should only be used for infrequently changing TLS variables, and it should
1268 * be paired with setting the real TLS variable since this provides no GC tracking.
1271 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1273 ((MonoThreadInfo*)info)->tls [key] = value;
1277 * mono_thread_info_exit:
1279 * Exit the current thread.
1280 * This function doesn't return.
1283 mono_thread_info_exit (void)
1285 mono_threads_core_exit (0);
1289 * mono_thread_info_open_handle:
1291 * Return a io-layer/win32 handle for the current thread.
1292 * The handle need to be closed by calling CloseHandle () when it is no
1296 mono_thread_info_open_handle (void)
1298 return mono_threads_core_open_handle ();
1302 * mono_threads_open_thread_handle:
1304 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1305 * The handle need to be closed by calling CloseHandle () when it is no
1309 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1311 return mono_threads_core_open_thread_handle (handle, tid);
1315 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1317 mono_threads_core_set_name (tid, name);
1320 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1322 struct _MonoThreadInfoInterruptToken {
1323 void (*callback) (gpointer data);
1328 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1330 * - @callback: must be able to be called from another thread and always cancel the wait
1331 * - @data: passed to the callback
1332 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1333 * if set to TRUE, it must mean that the thread is in interrupted state
1336 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1338 MonoThreadInfo *info;
1339 MonoThreadInfoInterruptToken *previous_token, *token;
1341 g_assert (callback);
1343 g_assert (interrupted);
1344 *interrupted = FALSE;
1346 info = mono_thread_info_current ();
1349 /* The memory of this token can be freed at 2 places:
1350 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1351 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1352 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1353 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1354 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1355 token->callback = callback;
1358 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1360 if (previous_token) {
1361 if (previous_token != INTERRUPT_STATE)
1362 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1366 *interrupted = TRUE;
1369 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1370 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1374 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1376 MonoThreadInfo *info;
1377 MonoThreadInfoInterruptToken *previous_token;
1379 g_assert (interrupted);
1380 *interrupted = FALSE;
1382 info = mono_thread_info_current ();
1385 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1387 /* only the installer can uninstall the token */
1388 g_assert (previous_token);
1390 if (previous_token == INTERRUPT_STATE) {
1391 /* if it is interrupted, then it is going to be freed in finish interrupt */
1392 *interrupted = TRUE;
1394 g_free (previous_token);
1397 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1398 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1401 static MonoThreadInfoInterruptToken*
1402 set_interrupt_state (MonoThreadInfo *info)
1404 MonoThreadInfoInterruptToken *token, *previous_token;
1408 /* Atomically obtain the token the thread is
1409 * waiting on, and change it to a flag value. */
1412 previous_token = info->interrupt_token;
1414 /* Already interrupted */
1415 if (previous_token == INTERRUPT_STATE) {
1420 token = previous_token;
1421 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1427 * mono_thread_info_prepare_interrupt:
1429 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1430 * - if the thread calls one of the WaitFor functions, the function will return with
1431 * WAIT_IO_COMPLETION instead of waiting
1432 * - if the thread was waiting when this function was called, the wait will be broken
1434 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1435 * didn't receive the interrupt signal yet, in this case it should call the wait function
1436 * again. This essentially means that the target thread will busy wait until it is ready to
1437 * process the interruption.
1439 MonoThreadInfoInterruptToken*
1440 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1442 MonoThreadInfoInterruptToken *token;
1444 token = set_interrupt_state (info);
1446 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1447 mono_thread_info_get_tid (info), token);
1453 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1455 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1460 g_assert (token->callback);
1462 token->callback (token->data);
1468 mono_thread_info_self_interrupt (void)
1470 MonoThreadInfo *info;
1471 MonoThreadInfoInterruptToken *token;
1473 info = mono_thread_info_current ();
1476 token = set_interrupt_state (info);
1479 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1480 mono_thread_info_get_tid (info));
1483 /* Clear the interrupted flag of the current thread, set with
1484 * mono_thread_info_self_interrupt, so it can wait again */
1486 mono_thread_info_clear_self_interrupt ()
1488 MonoThreadInfo *info;
1489 MonoThreadInfoInterruptToken *previous_token;
1491 info = mono_thread_info_current ();
1494 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1495 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1497 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1501 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1504 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1508 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1512 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1513 g_string_append_printf (text, "not waiting");
1514 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1515 g_string_append_printf (text, "interrupted state");
1517 g_string_append_printf (text, "waiting");
1520 /* info must be self or be held in a hazard pointer. */
1522 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1524 MonoAsyncJob old_job;
1526 old_job = (MonoAsyncJob) info->service_requests;
1529 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1534 mono_threads_consume_async_jobs (void)
1536 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1539 return (MonoAsyncJob) 0;
1541 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);