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-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>
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_DETACHED) {
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 ();
1109 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1110 static mono_mutex_t sleep_mutex;
1111 static mono_cond_t sleep_cond;
1114 sleep_initialize (void)
1116 mono_mutex_init (&sleep_mutex);
1117 mono_cond_init (&sleep_cond, NULL);
1121 sleep_interrupt (gpointer data)
1123 mono_mutex_lock (&sleep_mutex);
1124 mono_cond_broadcast (&sleep_cond);
1125 mono_mutex_unlock (&sleep_mutex);
1128 static inline guint32
1129 sleep_interruptable (guint32 ms, gboolean *alerted)
1131 guint32 start, now, end;
1133 g_assert (INFINITE == G_MAXUINT32);
1138 start = mono_msec_ticks ();
1140 if (start < G_MAXUINT32 - ms) {
1143 /* start + ms would overflow guint32 */
1147 mono_lazy_initialize (&sleep_init, sleep_initialize);
1149 mono_mutex_lock (&sleep_mutex);
1151 for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
1152 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1154 mono_mutex_unlock (&sleep_mutex);
1155 return WAIT_IO_COMPLETION;
1159 mono_cond_timedwait_ms (&sleep_cond, &sleep_mutex, end - now);
1161 mono_cond_wait (&sleep_cond, &sleep_mutex);
1163 mono_thread_info_uninstall_interrupt (alerted);
1165 mono_mutex_unlock (&sleep_mutex);
1166 return WAIT_IO_COMPLETION;
1170 mono_mutex_unlock (&sleep_mutex);
1176 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1179 MonoThreadInfo *info;
1181 mono_thread_info_yield ();
1183 info = mono_thread_info_current ();
1184 if (info && mono_thread_info_is_interrupt_state (info))
1185 return WAIT_IO_COMPLETION;
1191 return sleep_interruptable (ms, alerted);
1193 if (ms == INFINITE) {
1196 Sleep (G_MAXUINT32);
1198 sleep (G_MAXUINT32);
1203 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1204 struct timespec start, target;
1206 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1207 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1208 g_assert (ret == 0);
1211 target.tv_sec += ms / 1000;
1212 target.tv_nsec += (ms % 1000) * 1000000;
1213 if (target.tv_nsec > 999999999) {
1214 target.tv_nsec -= 999999999;
1219 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1224 struct timespec req, rem;
1226 req.tv_sec = ms / 1000;
1227 req.tv_nsec = (ms % 1000) * 1000000;
1230 memset (&rem, 0, sizeof (rem));
1231 ret = nanosleep (&req, &rem);
1233 #endif /* __linux__ */
1240 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1242 return ((MonoThreadInfo*)info)->tls [key];
1246 * mono_threads_info_tls_set:
1248 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1249 * values of TLS variables for threads other than the current thread.
1250 * This should only be used for infrequently changing TLS variables, and it should
1251 * be paired with setting the real TLS variable since this provides no GC tracking.
1254 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1256 ((MonoThreadInfo*)info)->tls [key] = value;
1260 * mono_thread_info_exit:
1262 * Exit the current thread.
1263 * This function doesn't return.
1266 mono_thread_info_exit (void)
1268 mono_threads_core_exit (0);
1272 * mono_thread_info_open_handle:
1274 * Return a io-layer/win32 handle for the current thread.
1275 * The handle need to be closed by calling CloseHandle () when it is no
1279 mono_thread_info_open_handle (void)
1281 return mono_threads_core_open_handle ();
1285 * mono_threads_open_thread_handle:
1287 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1288 * The handle need to be closed by calling CloseHandle () when it is no
1292 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1294 return mono_threads_core_open_thread_handle (handle, tid);
1298 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1300 mono_threads_core_set_name (tid, name);
1303 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1305 struct _MonoThreadInfoInterruptToken {
1306 void (*callback) (gpointer data);
1311 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1313 * - @callback: must be able to be called from another thread and always cancel the wait
1314 * - @data: passed to the callback
1315 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1316 * if set to TRUE, it must mean that the thread is in interrupted state
1319 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1321 MonoThreadInfo *info;
1322 MonoThreadInfoInterruptToken *previous_token, *token;
1324 g_assert (callback);
1326 g_assert (interrupted);
1327 *interrupted = FALSE;
1329 info = mono_thread_info_current ();
1332 /* The memory of this token can be freed at 2 places:
1333 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1334 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1335 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1336 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1337 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1338 token->callback = callback;
1341 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1343 if (previous_token) {
1344 if (previous_token != INTERRUPT_STATE)
1345 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1349 *interrupted = TRUE;
1352 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1353 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1357 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1359 MonoThreadInfo *info;
1360 MonoThreadInfoInterruptToken *previous_token;
1362 g_assert (interrupted);
1363 *interrupted = FALSE;
1365 info = mono_thread_info_current ();
1368 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1370 /* only the installer can uninstall the token */
1371 g_assert (previous_token);
1373 if (previous_token == INTERRUPT_STATE) {
1374 /* if it is interrupted, then it is going to be freed in finish interrupt */
1375 *interrupted = TRUE;
1377 g_free (previous_token);
1380 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1381 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1384 static MonoThreadInfoInterruptToken*
1385 set_interrupt_state (MonoThreadInfo *info)
1387 MonoThreadInfoInterruptToken *token, *previous_token;
1391 /* Atomically obtain the token the thread is
1392 * waiting on, and change it to a flag value. */
1395 previous_token = info->interrupt_token;
1397 /* Already interrupted */
1398 if (previous_token == INTERRUPT_STATE) {
1403 token = previous_token;
1404 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1410 * mono_thread_info_prepare_interrupt:
1412 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1413 * - if the thread calls one of the WaitFor functions, the function will return with
1414 * WAIT_IO_COMPLETION instead of waiting
1415 * - if the thread was waiting when this function was called, the wait will be broken
1417 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1418 * didn't receive the interrupt signal yet, in this case it should call the wait function
1419 * again. This essentially means that the target thread will busy wait until it is ready to
1420 * process the interruption.
1422 MonoThreadInfoInterruptToken*
1423 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1425 MonoThreadInfoInterruptToken *token;
1427 token = set_interrupt_state (info);
1429 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1430 mono_thread_info_get_tid (info), token);
1436 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1438 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1443 g_assert (token->callback);
1445 token->callback (token->data);
1451 mono_thread_info_self_interrupt (void)
1453 MonoThreadInfo *info;
1454 MonoThreadInfoInterruptToken *token;
1456 info = mono_thread_info_current ();
1459 token = set_interrupt_state (info);
1462 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1463 mono_thread_info_get_tid (info));
1466 /* Clear the interrupted flag of the current thread, set with
1467 * mono_thread_info_self_interrupt, so it can wait again */
1469 mono_thread_info_clear_self_interrupt ()
1471 MonoThreadInfo *info;
1472 MonoThreadInfoInterruptToken *previous_token;
1474 info = mono_thread_info_current ();
1477 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1478 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1480 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1484 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1487 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1491 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1495 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1496 g_string_append_printf (text, "not waiting");
1497 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1498 g_string_append_printf (text, "interrupted state");
1500 g_string_append_printf (text, "waiting");
1503 /* info must be self or be held in a hazard pointer. */
1505 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1507 MonoAsyncJob old_job;
1509 old_job = (MonoAsyncJob) info->service_requests;
1512 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1517 mono_threads_consume_async_jobs (void)
1519 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1522 return (MonoAsyncJob) 0;
1524 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);