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/metadata/mempool.h>
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-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>
33 #include <mono/utils/mach-support.h>
37 Mutex that makes sure only a single thread can be suspending others.
38 Suspend is a very racy operation since it requires restarting until
39 the target thread is not on an unsafe region.
41 We could implement this using critical regions, but would be much much
42 harder for an operation that is hardly performance critical.
44 The GC has to acquire this lock before starting a STW to make sure
45 a runtime suspend won't make it wronly see a thread in a safepoint
46 when it is in fact not.
48 static MonoSemType global_suspend_semaphore;
50 static size_t thread_info_size;
51 static MonoThreadInfoCallbacks threads_callbacks;
52 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
53 static MonoNativeTlsKey thread_info_key, thread_exited_key;
55 static __thread guint32 tls_small_id MONO_TLS_FAST;
57 static MonoNativeTlsKey small_id_key;
59 static MonoLinkedListSet thread_list;
60 static gboolean mono_threads_inited = FALSE;
62 static MonoSemType suspend_semaphore;
63 static size_t pending_suspends;
64 static gboolean unified_suspend_enabled;
66 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
69 #define SLEEP_DURATION_BEFORE_WARNING (10)
71 #define SLEEP_DURATION_BEFORE_ABORT 200
73 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
76 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
78 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
79 InterlockedIncrement (&abort_posts);
80 MONO_SEM_POST (&suspend_semaphore);
84 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
86 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
87 InterlockedIncrement (&suspend_posts);
88 MONO_SEM_POST (&suspend_semaphore);
92 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
94 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
95 InterlockedIncrement (&resume_posts);
96 MONO_SEM_POST (&suspend_semaphore);
100 resume_async_suspended (MonoThreadInfo *info)
102 g_assert (mono_threads_core_begin_async_resume (info));
106 resume_self_suspended (MonoThreadInfo* info)
108 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
109 MONO_SEM_POST (&info->resume_semaphore);
113 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
115 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
116 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
120 resume_blocking_suspended (MonoThreadInfo* info)
122 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
123 MONO_SEM_POST (&info->resume_semaphore);
127 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
129 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
131 InterlockedIncrement (&pending_ops);
135 mono_threads_begin_global_suspend (void)
137 g_assert (pending_suspends == 0);
138 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,
139 abort_posts, waits_done, pending_ops);
140 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
141 mono_threads_core_begin_global_suspend ();
145 mono_threads_end_global_suspend (void)
147 g_assert (pending_suspends == 0);
148 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
149 abort_posts, waits_done, pending_ops);
150 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
151 mono_threads_core_end_global_suspend ();
157 MonoThreadInfo *info;
158 MonoThreadInfo *cur = mono_thread_info_current ();
160 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
161 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
162 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
163 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
164 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
165 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
166 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
167 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
168 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
169 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
171 FOREACH_THREAD_SAFE (info) {
173 char thread_name [256] = { 0 };
174 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
176 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" : "" );
178 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" : "" );
181 } END_FOREACH_THREAD_SAFE
185 mono_threads_wait_pending_operations (void)
188 int c = pending_suspends;
190 /* Wait threads to park */
191 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
192 if (pending_suspends) {
193 MonoStopwatch suspension_time;
194 mono_stopwatch_start (&suspension_time);
195 for (i = 0; i < pending_suspends; ++i) {
196 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
197 InterlockedIncrement (&waits_done);
198 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
200 mono_stopwatch_stop (&suspension_time);
204 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
205 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);
207 mono_stopwatch_stop (&suspension_time);
208 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
212 pending_suspends = 0;
218 //Thread initialization code
220 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
223 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
226 mono_hazard_pointer_clear (hp, 0);
228 mono_hazard_pointer_clear (hp, 1);
230 mono_hazard_pointer_clear (hp, 2);
234 If return non null Hazard Pointer 1 holds the return value.
237 mono_thread_info_lookup (MonoNativeThreadId id)
239 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
241 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
242 mono_hazard_pointer_clear_all (hp, -1);
246 mono_hazard_pointer_clear_all (hp, 1);
247 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
251 mono_thread_info_insert (MonoThreadInfo *info)
253 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
255 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
256 mono_hazard_pointer_clear_all (hp, -1);
260 mono_hazard_pointer_clear_all (hp, -1);
265 mono_thread_info_remove (MonoThreadInfo *info)
267 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
270 THREADS_DEBUG ("removing info %p\n", info);
271 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
272 mono_hazard_pointer_clear_all (hp, -1);
277 free_thread_info (gpointer mem)
279 MonoThreadInfo *info = (MonoThreadInfo *) mem;
281 MONO_SEM_DESTROY (&info->resume_semaphore);
282 mono_threads_platform_free (info);
288 mono_thread_info_register_small_id (void)
290 int small_id = mono_thread_small_id_alloc ();
291 #ifdef HAVE_KW_THREAD
292 tls_small_id = small_id;
294 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
300 register_thread (MonoThreadInfo *info, gpointer baseptr)
303 guint8 *staddr = NULL;
304 int small_id = mono_thread_info_register_small_id ();
306 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
307 info->small_id = small_id;
309 MONO_SEM_INIT (&info->resume_semaphore, 0);
311 /*set TLS early so SMR works */
312 mono_native_tls_set_value (thread_info_key, info);
314 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
316 if (threads_callbacks.thread_register) {
317 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
318 // g_warning ("thread registation failed\n");
324 mono_thread_info_get_stack_bounds (&staddr, &stsize);
327 info->stack_start_limit = staddr;
328 info->stack_end = staddr + stsize;
330 info->stackdata = g_byte_array_new ();
332 mono_threads_platform_register (info);
335 Transition it before taking any locks or publishing itself to reduce the chance
336 of others witnessing a detached thread.
337 We can reasonably expect that until this thread gets published, no other thread will
338 try to manipulate it.
340 mono_threads_transition_attach (info);
341 mono_thread_info_suspend_lock ();
342 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
343 result = mono_thread_info_insert (info);
345 mono_thread_info_suspend_unlock ();
350 unregister_thread (void *arg)
352 MonoThreadInfo *info = (MonoThreadInfo *) arg;
353 int small_id = info->small_id;
356 THREADS_DEBUG ("unregistering info %p\n", info);
358 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
360 mono_threads_core_unregister (info);
363 * TLS destruction order is not reliable so small_id might be cleaned up
366 #ifndef HAVE_KW_THREAD
367 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
371 First perform the callback that requires no locks.
372 This callback has the potential of taking other locks, so we do it before.
373 After it completes, the thread remains functional.
375 if (threads_callbacks.thread_detach)
376 threads_callbacks.thread_detach (info);
378 mono_thread_info_suspend_lock ();
381 Now perform the callback that must be done under locks.
382 This will render the thread useless and non-suspendable, so it must
383 be done while holding the suspend lock to give no other thread chance
386 if (threads_callbacks.thread_unregister)
387 threads_callbacks.thread_unregister (info);
388 mono_threads_unregister_current_thread (info);
389 mono_threads_transition_detach (info);
391 mono_thread_info_suspend_unlock ();
393 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
395 /*now it's safe to free the thread info.*/
396 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
397 mono_thread_small_id_free (small_id);
401 thread_exited_dtor (void *arg)
403 #if defined(__MACH__)
405 * Since we use pthread dtors to clean up thread data, if a thread
406 * is attached to the runtime by another pthread dtor after our dtor
407 * has ran, it will never be detached, leading to various problems
408 * since the thread ids etc. will be reused while they are still in
409 * the threads hashtables etc.
410 * Dtors are called in a loop until all user tls entries are 0,
411 * but the loop has a maximum count (4), so if we set the tls
412 * variable every time, it will remain set when system tls dtors
413 * are ran. This allows mono_thread_info_is_exiting () to detect
414 * whenever the thread is exiting, even if it is executed from a
415 * system tls dtor (i.e. obj-c dealloc methods).
417 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
422 * Removes the current thread from the thread list.
423 * This must be called from the thread unregister callback and nowhere else.
424 * The current thread must be passed as TLS might have already been cleaned up.
427 mono_threads_unregister_current_thread (MonoThreadInfo *info)
430 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
431 result = mono_thread_info_remove (info);
436 mono_thread_info_current_unchecked (void)
438 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
443 mono_thread_info_current (void)
445 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
449 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
452 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
453 The way to distinguish between before, during and after cleanup is the following:
455 -If the TLS key is set, cleanup has not begun;
456 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
457 -If the thread is nowhere to be found, cleanup has finished.
459 We cannot function after cleanup since there's no way to ensure what will happen.
463 /*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 */
464 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
470 mono_thread_info_get_small_id (void)
472 #ifdef HAVE_KW_THREAD
475 gpointer val = mono_native_tls_get_value (small_id_key);
478 return GPOINTER_TO_INT (val) - 1;
483 mono_thread_info_list_head (void)
489 * mono_threads_attach_tools_thread
491 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
493 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
494 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
497 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
498 * doing things like resolving backtraces in their background processing thread.
501 mono_threads_attach_tools_thread (void)
504 MonoThreadInfo *info;
506 /* Must only be called once */
507 g_assert (!mono_native_tls_get_value (thread_info_key));
509 while (!mono_threads_inited) {
513 info = mono_thread_info_attach (&dummy);
516 info->tools_thread = TRUE;
520 mono_thread_info_attach (void *baseptr)
522 MonoThreadInfo *info;
523 if (!mono_threads_inited)
526 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
527 * thread is created before an embedding API user initialized Mono. */
528 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
531 g_assert (mono_threads_inited);
534 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
536 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
537 THREADS_DEBUG ("attaching %p\n", info);
538 if (!register_thread (info, baseptr))
540 } else if (threads_callbacks.thread_attach) {
541 threads_callbacks.thread_attach (info);
547 mono_thread_info_detach (void)
549 MonoThreadInfo *info;
550 if (!mono_threads_inited)
552 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
553 * is created before an embedding API user initialized Mono. */
554 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
557 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
559 THREADS_DEBUG ("detaching %p\n", info);
560 unregister_thread (info);
561 mono_native_tls_set_value (thread_info_key, NULL);
566 * mono_thread_info_is_exiting:
568 * Return whenever the current thread is exiting, i.e. it is running pthread
572 mono_thread_info_is_exiting (void)
574 #if defined(__MACH__)
575 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
582 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
585 threads_callbacks = *callbacks;
586 thread_info_size = info_size;
588 res = mono_native_tls_alloc (&thread_info_key, NULL);
589 res = mono_native_tls_alloc (&thread_exited_key, NULL);
591 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
592 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
597 #ifndef HAVE_KW_THREAD
598 res = mono_native_tls_alloc (&small_id_key, NULL);
602 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || MONO_THREADS_PLATFORM_REQUIRES_UNIFIED_SUSPEND;
604 MONO_SEM_INIT (&global_suspend_semaphore, 1);
605 MONO_SEM_INIT (&suspend_semaphore, 0);
607 mono_lls_init (&thread_list, NULL);
608 mono_thread_smr_init ();
609 mono_threads_init_platform ();
610 mono_threads_init_abort_syscall ();
612 #if defined(__MACH__)
613 mono_mach_init (thread_info_key);
616 mono_threads_inited = TRUE;
618 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
622 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
624 runtime_callbacks = *callbacks;
627 MonoThreadInfoRuntimeCallbacks *
628 mono_threads_get_runtime_callbacks (void)
630 return &runtime_callbacks;
634 The return value is only valid until a matching mono_thread_info_resume is called
636 static MonoThreadInfo*
637 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
639 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
640 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
642 *error_condition = "Thread not found";
646 switch (mono_threads_transition_request_async_suspension (info)) {
647 case AsyncSuspendAlreadySuspended:
648 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
650 case AsyncSuspendWait:
651 mono_threads_add_to_pending_operation_set (info);
653 case AsyncSuspendInitSuspend:
654 if (!mono_threads_core_begin_async_suspend (info, interrupt_kernel)) {
655 mono_hazard_pointer_clear (hp, 1);
656 *error_condition = "Could not suspend thread";
661 //Wait for the pending suspend to finish
662 mono_threads_wait_pending_operations ();
664 if (!mono_threads_core_check_suspend_result (info)) {
666 mono_hazard_pointer_clear (hp, 1);
667 *error_condition = "Post suspend failed";
674 Signal that the current thread wants to be suspended.
675 This function can be called without holding the suspend lock held.
676 To finish suspending, call mono_suspend_check.
679 mono_thread_info_begin_self_suspend (void)
681 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
685 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
686 mono_threads_transition_request_self_suspension (info);
690 mono_thread_info_end_self_suspend (void)
692 MonoThreadInfo *info;
694 info = mono_thread_info_current ();
697 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
699 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
701 /* commit the saved state and notify others if needed */
702 switch (mono_threads_transition_state_poll (info)) {
703 case SelfSuspendResumed:
705 case SelfSuspendWait:
706 mono_thread_info_wait_for_resume (info);
708 case SelfSuspendNotifyAndWait:
709 mono_threads_notify_initiator_of_suspend (info);
710 mono_thread_info_wait_for_resume (info);
711 mono_threads_notify_initiator_of_resume (info);
717 mono_thread_info_core_resume (MonoThreadInfo *info)
719 gboolean res = FALSE;
720 if (info->create_suspended) {
721 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
722 /* 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 */
723 info->create_suspended = FALSE;
724 mono_threads_core_resume_created (info, tid);
728 switch (mono_threads_transition_request_resume (info)) {
735 case ResumeInitSelfResume:
736 resume_self_suspended (info);
739 case ResumeInitAsyncResume:
740 resume_async_suspended (info);
743 case ResumeInitBlockingResume:
744 resume_blocking_suspended (info);
753 mono_thread_info_resume (MonoNativeThreadId tid)
755 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
756 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
757 MonoThreadInfo *info;
759 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
761 mono_thread_info_suspend_lock ();
763 info = mono_thread_info_lookup (tid); /*info on HP1*/
769 result = mono_thread_info_core_resume (info);
771 //Wait for the pending resume to finish
772 mono_threads_wait_pending_operations ();
775 mono_thread_info_suspend_unlock ();
776 mono_hazard_pointer_clear (hp, 1);
781 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
783 switch (mono_threads_transition_request_async_suspension (info)) {
784 case AsyncSuspendAlreadySuspended:
786 case AsyncSuspendWait:
787 mono_threads_add_to_pending_operation_set (info);
789 case AsyncSuspendInitSuspend:
790 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
792 g_assert_not_reached ();
797 mono_thread_info_begin_resume (MonoThreadInfo *info)
799 return mono_thread_info_core_resume (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);
851 static MonoThreadInfo*
852 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
854 MonoThreadInfo *info = NULL;
855 int sleep_duration = 0;
857 const char *suspend_error = "Unknown error";
858 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
859 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
863 /*WARNING: We now are in interrupt context until we resume the thread. */
864 if (!is_thread_in_critical_region (info))
867 if (!mono_thread_info_core_resume (info)) {
868 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
871 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
873 /* Wait for the pending resume to finish */
874 mono_threads_wait_pending_operations ();
876 if (!sleep_duration) {
884 g_usleep (sleep_duration);
886 sleep_duration += 10;
892 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
895 MonoThreadInfo *info = NULL;
896 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
898 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
899 /*FIXME: unify this with self-suspend*/
900 g_assert (id != mono_native_thread_id_get ());
902 /* This can block during stw */
903 mono_thread_info_suspend_lock ();
904 mono_threads_begin_global_suspend ();
906 info = suspend_sync_nolock (id, interrupt_kernel);
910 switch (result = callback (info, user_data)) {
911 case MonoResumeThread:
912 mono_hazard_pointer_set (hp, 1, info);
913 mono_thread_info_core_resume (info);
914 mono_threads_wait_pending_operations ();
919 g_error ("Invalid suspend_and_run callback return value %d", result);
923 mono_hazard_pointer_clear (hp, 1);
924 mono_threads_end_global_suspend ();
925 mono_thread_info_suspend_unlock ();
930 If we are trying to suspend a target that is on a critical region
931 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
932 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
934 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
936 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
939 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
941 MonoThreadInfo *info = NULL;
943 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
944 /*FIXME: unify this with self-suspend*/
945 g_assert (id != mono_native_thread_id_get ());
947 mono_thread_info_suspend_lock ();
948 mono_threads_begin_global_suspend ();
950 info = suspend_sync_nolock (id, interrupt_kernel);
952 /* XXX this clears HP 1, so we restated it again */
953 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
954 mono_threads_end_global_suspend ();
955 mono_thread_info_suspend_unlock ();
961 Inject an assynchronous call into the target thread. The target thread must be suspended and
962 only a single async call can be setup for a given suspend cycle.
963 This async call must cause stack unwinding as the current implementation doesn't save enough state
964 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
965 currently used only to deliver exceptions.
968 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
970 /* An async call can only be setup on an async suspended thread */
971 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
972 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
973 g_assert (!info->async_target);
974 info->async_target = target_func;
975 /* This is not GC tracked */
976 info->user_data = user_data;
980 The suspend lock is held during any suspend in progress.
981 A GC that has safepoints must take this lock as part of its
982 STW to make sure no unsafe pending suspend is in progress.
985 mono_thread_info_suspend_lock (void)
988 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
989 MONO_FINISH_TRY_BLOCKING;
993 mono_thread_info_suspend_unlock (void)
995 MONO_SEM_POST (&global_suspend_semaphore);
999 * This is a very specific function whose only purpose is to
1000 * break a given thread from socket syscalls.
1002 * This only exists because linux won't fail a call to connect
1003 * if the underlying is closed.
1005 * TODO We should cleanup and unify this with the other syscall abort
1009 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1011 MonoThreadHazardPointers *hp;
1012 MonoThreadInfo *info;
1014 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1017 hp = mono_hazard_pointer_get ();
1018 info = mono_thread_info_lookup (tid); /*info on HP1*/
1022 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
1023 mono_hazard_pointer_clear (hp, 1);
1027 mono_thread_info_suspend_lock ();
1028 mono_threads_begin_global_suspend ();
1030 mono_threads_core_abort_syscall (info);
1031 mono_threads_wait_pending_operations ();
1033 mono_hazard_pointer_clear (hp, 1);
1035 mono_threads_end_global_suspend ();
1036 mono_thread_info_suspend_unlock ();
1040 mono_thread_info_unified_management_enabled (void)
1042 return unified_suspend_enabled;
1046 * mono_thread_info_set_is_async_context:
1048 * Set whenever the current thread is in an async context. Some runtime functions might behave
1049 * differently while in an async context in order to be async safe.
1052 mono_thread_info_set_is_async_context (gboolean async_context)
1054 MonoThreadInfo *info = mono_thread_info_current ();
1057 info->is_async_context = async_context;
1061 mono_thread_info_is_async_context (void)
1063 MonoThreadInfo *info = mono_thread_info_current ();
1066 return info->is_async_context;
1072 * mono_threads_create_thread:
1074 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1075 * Returns: a windows or io-layer handle for the thread.
1078 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1080 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1084 * mono_thread_info_get_stack_bounds:
1086 * Return the address and size of the current threads stack. Return NULL as the
1087 * stack address if the stack address cannot be determined.
1090 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1092 guint8 *current = (guint8 *)&stsize;
1093 mono_threads_core_get_stack_bounds (staddr, stsize);
1097 /* Sanity check the result */
1098 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1100 /* When running under emacs, sometimes staddr is not aligned to a page size */
1101 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1105 mono_thread_info_yield (void)
1107 return mono_threads_core_yield ();
1111 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1113 return ((MonoThreadInfo*)info)->tls [key];
1117 * mono_threads_info_tls_set:
1119 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1120 * values of TLS variables for threads other than the current thread.
1121 * This should only be used for infrequently changing TLS variables, and it should
1122 * be paired with setting the real TLS variable since this provides no GC tracking.
1125 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1127 ((MonoThreadInfo*)info)->tls [key] = value;
1131 * mono_thread_info_exit:
1133 * Exit the current thread.
1134 * This function doesn't return.
1137 mono_thread_info_exit (void)
1139 mono_threads_core_exit (0);
1143 * mono_thread_info_open_handle:
1145 * Return a io-layer/win32 handle for the current thread.
1146 * The handle need to be closed by calling CloseHandle () when it is no
1150 mono_thread_info_open_handle (void)
1152 return mono_threads_core_open_handle ();
1156 * mono_threads_open_thread_handle:
1158 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1159 * The handle need to be closed by calling CloseHandle () when it is no
1163 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1165 return mono_threads_core_open_thread_handle (handle, tid);
1169 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1171 mono_threads_core_set_name (tid, name);
1174 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1176 struct _MonoThreadInfoInterruptToken {
1177 void (*callback) (gpointer data);
1182 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1184 * - @callback: must be able to be called from another thread and always cancel the wait
1185 * - @data: passed to the callback
1186 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1187 * if set to TRUE, it must mean that the thread is in interrupted state
1190 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1192 MonoThreadInfo *info;
1193 MonoThreadInfoInterruptToken *previous_token, *token;
1195 g_assert (callback);
1197 g_assert (interrupted);
1198 *interrupted = FALSE;
1200 info = mono_thread_info_current ();
1203 /* The memory of this token can be freed at 2 places:
1204 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1205 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1206 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1207 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1208 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1209 token->callback = callback;
1212 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1214 if (previous_token) {
1215 if (previous_token != INTERRUPT_STATE)
1216 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1220 *interrupted = TRUE;
1223 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1224 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1228 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1230 MonoThreadInfo *info;
1231 MonoThreadInfoInterruptToken *previous_token;
1233 g_assert (interrupted);
1234 *interrupted = FALSE;
1236 info = mono_thread_info_current ();
1239 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1241 /* only the installer can uninstall the token */
1242 g_assert (previous_token);
1244 if (previous_token == INTERRUPT_STATE) {
1245 /* if it is interrupted, then it is going to be freed in finish interrupt */
1246 *interrupted = TRUE;
1248 g_free (previous_token);
1251 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1252 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1255 static MonoThreadInfoInterruptToken*
1256 set_interrupt_state (MonoThreadInfo *info)
1258 MonoThreadInfoInterruptToken *token, *previous_token;
1262 /* Atomically obtain the token the thread is
1263 * waiting on, and change it to a flag value. */
1266 previous_token = info->interrupt_token;
1268 /* Already interrupted */
1269 if (previous_token == INTERRUPT_STATE) {
1274 token = previous_token;
1275 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1281 * mono_thread_info_prepare_interrupt:
1283 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1284 * - if the thread calls one of the WaitFor functions, the function will return with
1285 * WAIT_IO_COMPLETION instead of waiting
1286 * - if the thread was waiting when this function was called, the wait will be broken
1288 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1289 * didn't receive the interrupt signal yet, in this case it should call the wait function
1290 * again. This essentially means that the target thread will busy wait until it is ready to
1291 * process the interruption.
1293 MonoThreadInfoInterruptToken*
1294 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1296 MonoThreadInfoInterruptToken *token;
1298 token = set_interrupt_state (info);
1300 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1301 mono_thread_info_get_tid (info), token);
1307 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1309 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1314 g_assert (token->callback);
1316 token->callback (token->data);
1322 mono_thread_info_self_interrupt (void)
1324 MonoThreadInfo *info;
1325 MonoThreadInfoInterruptToken *token;
1327 info = mono_thread_info_current ();
1330 token = set_interrupt_state (info);
1333 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1334 mono_thread_info_get_tid (info));
1337 /* Clear the interrupted flag of the current thread, set with
1338 * mono_thread_info_self_interrupt, so it can wait again */
1340 mono_thread_info_clear_self_interrupt ()
1342 MonoThreadInfo *info;
1343 MonoThreadInfoInterruptToken *previous_token;
1345 info = mono_thread_info_current ();
1348 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1349 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1351 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1355 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1358 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1362 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1366 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1367 g_string_append_printf (text, "not waiting");
1368 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1369 g_string_append_printf (text, "interrupted state");
1371 g_string_append_printf (text, "waiting");
1374 /* info must be self or be held in a hazard pointer. */
1376 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1378 MonoAsyncJob old_job;
1380 old_job = (MonoAsyncJob) info->service_requests;
1383 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1388 mono_threads_consume_async_jobs (void)
1390 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1393 return (MonoAsyncJob) 0;
1395 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);