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 #include <mono/utils/mono-compiler.h>
14 #include <mono/utils/mono-semaphore.h>
15 #include <mono/utils/mono-threads.h>
16 #include <mono/utils/mono-tls.h>
17 #include <mono/utils/hazard-pointer.h>
18 #include <mono/utils/mono-memory-model.h>
19 #include <mono/utils/mono-mmap.h>
20 #include <mono/utils/atomic.h>
21 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mach-support.h>
31 Mutex that makes sure only a single thread can be suspending others.
32 Suspend is a very racy operation since it requires restarting until
33 the target thread is not on an unsafe region.
35 We could implement this using critical regions, but would be much much
36 harder for an operation that is hardly performance critical.
38 The GC has to acquire this lock before starting a STW to make sure
39 a runtime suspend won't make it wronly see a thread in a safepoint
40 when it is in fact not.
42 static MonoSemType global_suspend_semaphore;
44 static size_t thread_info_size;
45 static MonoThreadInfoCallbacks threads_callbacks;
46 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
47 static MonoNativeTlsKey thread_info_key, thread_exited_key;
49 static __thread guint32 tls_small_id MONO_TLS_FAST;
51 static MonoNativeTlsKey small_id_key;
53 static MonoLinkedListSet thread_list;
54 static gboolean disable_new_interrupt = FALSE;
55 static gboolean mono_threads_inited = FALSE;
57 static MonoSemType suspend_semaphore;
58 static size_t pending_suspends;
59 static gboolean unified_suspend_enabled;
61 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
64 #define SLEEP_DURATION_BEFORE_WARNING (10)
66 #define SLEEP_DURATION_BEFORE_ABORT 200
68 static int suspend_posts, resume_posts, waits_done, pending_ops;
71 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
73 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
74 MONO_SEM_POST (&suspend_semaphore);
75 InterlockedIncrement (&suspend_posts);
79 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
81 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
82 MONO_SEM_POST (&suspend_semaphore);
83 InterlockedIncrement (&resume_posts);
87 resume_async_suspended (MonoThreadInfo *info)
89 g_assert (mono_threads_core_begin_async_resume (info));
93 resume_self_suspended (MonoThreadInfo* info)
95 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
96 MONO_SEM_POST (&info->resume_semaphore);
100 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
103 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
107 resume_blocking_suspended (MonoThreadInfo* info)
109 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
110 MONO_SEM_POST (&info->resume_semaphore);
114 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
116 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
118 InterlockedIncrement (&pending_ops);
122 mono_threads_begin_global_suspend (void)
124 g_assert (pending_suspends == 0);
125 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d wd %d po %d\n", suspend_posts, resume_posts, waits_done, pending_ops);
129 mono_threads_end_global_suspend (void)
131 g_assert (pending_suspends == 0);
132 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d wd %d po %d\n", suspend_posts, resume_posts, waits_done, pending_ops);
138 MonoThreadInfo *info;
139 MonoThreadInfo *cur = mono_thread_info_current ();
141 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2)\n");
142 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
143 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
144 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
145 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
146 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
147 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
148 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
149 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x07\t- blocking (GOOD)\n");
150 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
152 FOREACH_THREAD_SAFE (info) {
153 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
154 } END_FOREACH_THREAD_SAFE
158 mono_threads_wait_pending_operations (void)
161 int c = pending_suspends;
163 /* Wait threads to park */
164 if (pending_suspends) {
165 MonoStopwatch suspension_time;
166 mono_stopwatch_start (&suspension_time);
167 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
168 for (i = 0; i < pending_suspends; ++i) {
169 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
170 InterlockedIncrement (&waits_done);
171 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
173 mono_stopwatch_stop (&suspension_time);
177 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
178 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);
180 mono_stopwatch_stop (&suspension_time);
181 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
185 pending_suspends = 0;
191 //Thread initialization code
193 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
196 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
199 mono_hazard_pointer_clear (hp, 0);
201 mono_hazard_pointer_clear (hp, 1);
203 mono_hazard_pointer_clear (hp, 2);
207 If return non null Hazard Pointer 1 holds the return value.
210 mono_thread_info_lookup (MonoNativeThreadId id)
212 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
214 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
215 mono_hazard_pointer_clear_all (hp, -1);
219 mono_hazard_pointer_clear_all (hp, 1);
220 return mono_hazard_pointer_get_val (hp, 1);
224 mono_thread_info_insert (MonoThreadInfo *info)
226 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
228 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
229 mono_hazard_pointer_clear_all (hp, -1);
233 mono_hazard_pointer_clear_all (hp, -1);
238 mono_thread_info_remove (MonoThreadInfo *info)
240 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
243 THREADS_DEBUG ("removing info %p\n", info);
244 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
245 mono_hazard_pointer_clear_all (hp, -1);
250 free_thread_info (gpointer mem)
252 MonoThreadInfo *info = mem;
254 MONO_SEM_DESTROY (&info->resume_semaphore);
255 mono_threads_platform_free (info);
261 mono_thread_info_register_small_id (void)
263 int small_id = mono_thread_small_id_alloc ();
264 #ifdef HAVE_KW_THREAD
265 tls_small_id = small_id;
267 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
273 register_thread (MonoThreadInfo *info, gpointer baseptr)
276 guint8 *staddr = NULL;
277 int small_id = mono_thread_info_register_small_id ();
279 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
280 info->small_id = small_id;
282 MONO_SEM_INIT (&info->resume_semaphore, 0);
284 /*set TLS early so SMR works */
285 mono_native_tls_set_value (thread_info_key, info);
287 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
289 if (threads_callbacks.thread_register) {
290 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
291 // g_warning ("thread registation failed\n");
297 mono_thread_info_get_stack_bounds (&staddr, &stsize);
300 info->stack_start_limit = staddr;
301 info->stack_end = staddr + stsize;
303 mono_threads_platform_register (info);
306 Transition it before taking any locks or publishing itself to reduce the chance
307 of others witnessing a detached thread.
308 We can reasonably expect that until this thread gets published, no other thread will
309 try to manipulate it.
311 mono_threads_transition_attach (info);
312 mono_thread_info_suspend_lock ();
313 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
314 result = mono_thread_info_insert (info);
316 mono_thread_info_suspend_unlock ();
321 unregister_thread (void *arg)
323 MonoThreadInfo *info = arg;
324 int small_id = info->small_id;
327 THREADS_DEBUG ("unregistering info %p\n", info);
329 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
331 mono_threads_core_unregister (info);
334 * TLS destruction order is not reliable so small_id might be cleaned up
337 #ifndef HAVE_KW_THREAD
338 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
342 First perform the callback that requires no locks.
343 This callback has the potential of taking other locks, so we do it before.
344 After it completes, the thread remains functional.
346 if (threads_callbacks.thread_detach)
347 threads_callbacks.thread_detach (info);
349 mono_thread_info_suspend_lock ();
352 Now perform the callback that must be done under locks.
353 This will render the thread useless and non-suspendable, so it must
354 be done while holding the suspend lock to give no other thread chance
357 if (threads_callbacks.thread_unregister)
358 threads_callbacks.thread_unregister (info);
359 mono_threads_unregister_current_thread (info);
360 mono_threads_transition_detach (info);
362 mono_thread_info_suspend_unlock ();
364 /*now it's safe to free the thread info.*/
365 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
366 mono_thread_small_id_free (small_id);
370 thread_exited_dtor (void *arg)
372 #if defined(__MACH__)
374 * Since we use pthread dtors to clean up thread data, if a thread
375 * is attached to the runtime by another pthread dtor after our dtor
376 * has ran, it will never be detached, leading to various problems
377 * since the thread ids etc. will be reused while they are still in
378 * the threads hashtables etc.
379 * Dtors are called in a loop until all user tls entries are 0,
380 * but the loop has a maximum count (4), so if we set the tls
381 * variable every time, it will remain set when system tls dtors
382 * are ran. This allows mono_thread_info_is_exiting () to detect
383 * whenever the thread is exiting, even if it is executed from a
384 * system tls dtor (i.e. obj-c dealloc methods).
386 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
391 * Removes the current thread from the thread list.
392 * This must be called from the thread unregister callback and nowhere else.
393 * The current thread must be passed as TLS might have already been cleaned up.
396 mono_threads_unregister_current_thread (MonoThreadInfo *info)
399 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
400 result = mono_thread_info_remove (info);
405 mono_thread_info_current_unchecked (void)
407 return (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
412 mono_thread_info_current (void)
414 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
418 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
421 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
422 The way to distinguish between before, during and after cleanup is the following:
424 -If the TLS key is set, cleanup has not begun;
425 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
426 -If the thread is nowhere to be found, cleanup has finished.
428 We cannot function after cleanup since there's no way to ensure what will happen.
432 /*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 */
433 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
439 mono_thread_info_get_small_id (void)
441 #ifdef HAVE_KW_THREAD
444 gpointer val = mono_native_tls_get_value (small_id_key);
447 return GPOINTER_TO_INT (val) - 1;
452 mono_thread_info_list_head (void)
458 * mono_threads_attach_tools_thread
460 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
462 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
463 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
466 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
467 * doing things like resolving backtraces in their background processing thread.
470 mono_threads_attach_tools_thread (void)
473 MonoThreadInfo *info;
475 /* Must only be called once */
476 g_assert (!mono_native_tls_get_value (thread_info_key));
478 info = mono_thread_info_attach (&dummy);
479 info->tools_thread = TRUE;
483 mono_thread_info_attach (void *baseptr)
485 MonoThreadInfo *info;
486 if (!mono_threads_inited)
488 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
489 * thread is created before an embedding API user initialized Mono. */
490 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
493 info = mono_native_tls_get_value (thread_info_key);
495 info = g_malloc0 (thread_info_size);
496 THREADS_DEBUG ("attaching %p\n", info);
497 if (!register_thread (info, baseptr))
499 } else if (threads_callbacks.thread_attach) {
500 threads_callbacks.thread_attach (info);
506 mono_thread_info_detach (void)
508 MonoThreadInfo *info;
509 if (!mono_threads_inited)
511 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
512 * is created before an embedding API user initialized Mono. */
513 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
516 info = mono_native_tls_get_value (thread_info_key);
518 THREADS_DEBUG ("detaching %p\n", info);
519 unregister_thread (info);
520 mono_native_tls_set_value (thread_info_key, NULL);
525 * mono_thread_info_is_exiting:
527 * Return whenever the current thread is exiting, i.e. it is running pthread
531 mono_thread_info_is_exiting (void)
533 #if defined(__MACH__)
534 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
541 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
544 threads_callbacks = *callbacks;
545 thread_info_size = info_size;
547 res = mono_native_tls_alloc (&thread_info_key, NULL);
548 res = mono_native_tls_alloc (&thread_exited_key, NULL);
550 res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
551 res = mono_native_tls_alloc (&thread_exited_key, thread_exited_dtor);
555 #ifndef HAVE_KW_THREAD
556 res = mono_native_tls_alloc (&small_id_key, NULL);
560 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || MONO_THREADS_PLATFORM_REQUIRES_UNIFIED_SUSPEND;
562 MONO_SEM_INIT (&global_suspend_semaphore, 1);
563 MONO_SEM_INIT (&suspend_semaphore, 0);
565 mono_lls_init (&thread_list, NULL);
566 mono_thread_smr_init ();
567 mono_threads_init_platform ();
569 #if defined(__MACH__)
570 mono_mach_init (thread_info_key);
573 mono_threads_inited = TRUE;
575 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
579 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
581 runtime_callbacks = *callbacks;
584 MonoThreadInfoCallbacks *
585 mono_threads_get_callbacks (void)
587 return &threads_callbacks;
590 MonoThreadInfoRuntimeCallbacks *
591 mono_threads_get_runtime_callbacks (void)
593 return &runtime_callbacks;
597 The return value is only valid until a matching mono_thread_info_resume is called
599 static MonoThreadInfo*
600 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
602 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
603 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
605 *error_condition = "Thread not found";
609 switch (mono_threads_transition_request_async_suspension (info)) {
610 case AsyncSuspendAlreadySuspended:
611 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
613 case AsyncSuspendWait:
614 mono_threads_add_to_pending_operation_set (info);
616 case AsyncSuspendInitSuspend:
617 if (!mono_threads_core_begin_async_suspend (info, interrupt_kernel)) {
618 mono_hazard_pointer_clear (hp, 1);
619 *error_condition = "Could not suspend thread";
624 //Wait for the pending suspend to finish
625 mono_threads_wait_pending_operations ();
627 if (!mono_threads_core_check_suspend_result (info)) {
629 mono_hazard_pointer_clear (hp, 1);
630 *error_condition = "Post suspend failed";
637 Signal that the current thread wants to be suspended.
638 This function can be called without holding the suspend lock held.
639 To finish suspending, call mono_suspend_check.
642 mono_thread_info_begin_self_suspend (void)
644 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
648 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
649 mono_threads_transition_request_self_suspension (info);
653 mono_thread_info_end_self_suspend (void)
655 MonoThreadInfo *info;
657 info = mono_thread_info_current ();
660 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
662 g_assert (mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL));
664 /* commit the saved state and notify others if needed */
665 switch (mono_threads_transition_state_poll (info)) {
666 case SelfSuspendResumed:
668 case SelfSuspendWait:
669 mono_thread_info_wait_for_resume (info);
671 case SelfSuspendNotifyAndWait:
672 mono_threads_notify_initiator_of_suspend (info);
673 mono_thread_info_wait_for_resume (info);
674 mono_threads_notify_initiator_of_resume (info);
680 mono_thread_info_core_resume (MonoThreadInfo *info)
682 gboolean res = FALSE;
683 if (info->create_suspended) {
684 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
685 /* 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 */
686 info->create_suspended = FALSE;
687 mono_threads_core_resume_created (info, tid);
691 switch (mono_threads_transition_request_resume (info)) {
698 case ResumeInitSelfResume:
699 resume_self_suspended (info);
702 case ResumeInitAsyncResume:
703 resume_async_suspended (info);
706 case ResumeInitBlockingResume:
707 resume_blocking_suspended (info);
716 mono_thread_info_resume (MonoNativeThreadId tid)
718 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
719 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
720 MonoThreadInfo *info;
722 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
724 mono_thread_info_suspend_lock ();
726 info = mono_thread_info_lookup (tid); /*info on HP1*/
732 result = mono_thread_info_core_resume (info);
734 //Wait for the pending resume to finish
735 mono_threads_wait_pending_operations ();
738 mono_thread_info_suspend_unlock ();
739 mono_hazard_pointer_clear (hp, 1);
744 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
746 switch (mono_threads_transition_request_async_suspension (info)) {
747 case AsyncSuspendAlreadySuspended:
749 case AsyncSuspendWait:
750 mono_threads_add_to_pending_operation_set (info);
752 case AsyncSuspendInitSuspend:
753 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
755 g_assert_not_reached ();
760 mono_thread_info_begin_resume (MonoThreadInfo *info)
762 return mono_thread_info_core_resume (info);
766 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
767 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
770 is_thread_in_critical_region (MonoThreadInfo *info)
774 gpointer stack_start;
775 MonoThreadUnwindState *state;
777 /* Are we inside a system critical region? */
778 if (info->inside_critical_region)
781 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
785 /* Are we inside a GC critical region? */
786 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
790 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
791 state = mono_thread_info_get_suspend_state (info);
792 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
795 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
796 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
797 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
800 ji = mono_jit_info_table_find (
801 state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
802 MONO_CONTEXT_GET_IP (&state->ctx));
807 method = mono_jit_info_get_method (ji);
809 return threads_callbacks.mono_method_is_critical (method);
813 mono_thread_info_in_critical_location (MonoThreadInfo *info)
815 return is_thread_in_critical_region (info);
818 static MonoThreadInfo*
819 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
821 MonoThreadInfo *info = NULL;
822 int sleep_duration = 0;
824 const char *suspend_error = "Unknown error";
825 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
826 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
830 /*WARNING: We now are in interrupt context until we resume the thread. */
831 if (!is_thread_in_critical_region (info))
834 if (!mono_thread_info_core_resume (info)) {
835 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
838 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
840 /* Wait for the pending resume to finish */
841 mono_threads_wait_pending_operations ();
843 if (!sleep_duration) {
851 g_usleep (sleep_duration);
853 sleep_duration += 10;
859 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
862 MonoThreadInfo *info = NULL;
863 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
865 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
866 /*FIXME: unify this with self-suspend*/
867 g_assert (id != mono_native_thread_id_get ());
869 mono_thread_info_suspend_lock ();
870 mono_threads_begin_global_suspend ();
872 info = suspend_sync_nolock (id, interrupt_kernel);
876 switch (result = callback (info, user_data)) {
877 case MonoResumeThread:
878 mono_hazard_pointer_set (hp, 1, info);
879 mono_thread_info_core_resume (info);
880 mono_threads_wait_pending_operations ();
885 g_error ("Invalid suspend_and_run callback return value %d", result);
889 mono_hazard_pointer_clear (hp, 1);
890 mono_threads_end_global_suspend ();
891 mono_thread_info_suspend_unlock ();
896 If we are trying to suspend a target that is on a critical region
897 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
898 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
900 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
902 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
905 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
907 MonoThreadInfo *info = NULL;
909 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
910 /*FIXME: unify this with self-suspend*/
911 g_assert (id != mono_native_thread_id_get ());
913 mono_thread_info_suspend_lock ();
914 mono_threads_begin_global_suspend ();
916 info = suspend_sync_nolock (id, interrupt_kernel);
918 /* XXX this clears HP 1, so we restated it again */
919 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
920 mono_threads_end_global_suspend ();
921 mono_thread_info_suspend_unlock ();
927 Inject an assynchronous call into the target thread. The target thread must be suspended and
928 only a single async call can be setup for a given suspend cycle.
929 This async call must cause stack unwinding as the current implementation doesn't save enough state
930 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
931 currently used only to deliver exceptions.
934 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
936 /* An async call can only be setup on an async suspended thread */
937 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
938 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
939 g_assert (!info->async_target);
940 info->async_target = target_func;
941 /* This is not GC tracked */
942 info->user_data = user_data;
946 The suspend lock is held during any suspend in progress.
947 A GC that has safepoints must take this lock as part of its
948 STW to make sure no unsafe pending suspend is in progress.
951 mono_thread_info_suspend_lock (void)
953 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
957 mono_thread_info_suspend_unlock (void)
959 MONO_SEM_POST (&global_suspend_semaphore);
963 mono_thread_info_disable_new_interrupt (gboolean disable)
965 disable_new_interrupt = disable;
969 * This is a very specific function whose only purpose is to
970 * break a given thread from socket syscalls.
972 * This only exists because linux won't fail a call to connect
973 * if the underlying is closed.
975 * TODO We should cleanup and unify this with the other syscall abort
979 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
981 MonoThreadHazardPointers *hp;
982 MonoThreadInfo *info;
984 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
987 hp = mono_hazard_pointer_get ();
988 info = mono_thread_info_lookup (tid); /*info on HP1*/
992 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
993 mono_hazard_pointer_clear (hp, 1);
997 mono_thread_info_suspend_lock ();
999 mono_threads_core_abort_syscall (info);
1001 mono_hazard_pointer_clear (hp, 1);
1002 mono_thread_info_suspend_unlock ();
1006 mono_thread_info_unified_management_enabled (void)
1008 return unified_suspend_enabled;
1012 Disabled by default for now.
1013 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
1014 which means mono-context and setup_async_callback, and we need a mono-threads backend.
1017 mono_thread_info_new_interrupt_enabled (void)
1019 /*We need STW gc events to work correctly*/
1020 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
1023 #if defined(HOST_WIN32)
1024 return !disable_new_interrupt;
1026 #if defined (__i386__) || defined(__x86_64__)
1027 return !disable_new_interrupt;
1029 #if defined(__arm__) || defined(__aarch64__)
1030 return !disable_new_interrupt;
1036 * mono_thread_info_set_is_async_context:
1038 * Set whenever the current thread is in an async context. Some runtime functions might behave
1039 * differently while in an async context in order to be async safe.
1042 mono_thread_info_set_is_async_context (gboolean async_context)
1044 MonoThreadInfo *info = mono_thread_info_current ();
1047 info->is_async_context = async_context;
1051 mono_thread_info_is_async_context (void)
1053 MonoThreadInfo *info = mono_thread_info_current ();
1056 return info->is_async_context;
1062 * mono_threads_create_thread:
1064 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1065 * Returns: a windows or io-layer handle for the thread.
1068 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1070 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1074 * mono_thread_info_get_stack_bounds:
1076 * Return the address and size of the current threads stack. Return NULL as the
1077 * stack address if the stack address cannot be determined.
1080 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1082 guint8 *current = (guint8 *)&stsize;
1083 mono_threads_core_get_stack_bounds (staddr, stsize);
1087 /* Sanity check the result */
1088 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1090 /* When running under emacs, sometimes staddr is not aligned to a page size */
1091 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1095 mono_thread_info_yield (void)
1097 return mono_threads_core_yield ();
1101 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1103 return ((MonoThreadInfo*)info)->tls [key];
1107 * mono_threads_info_tls_set:
1109 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1110 * values of TLS variables for threads other than the current thread.
1111 * This should only be used for infrequently changing TLS variables, and it should
1112 * be paired with setting the real TLS variable since this provides no GC tracking.
1115 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1117 ((MonoThreadInfo*)info)->tls [key] = value;
1121 * mono_thread_info_exit:
1123 * Exit the current thread.
1124 * This function doesn't return.
1127 mono_thread_info_exit (void)
1129 mono_threads_core_exit (0);
1133 * mono_thread_info_open_handle:
1135 * Return a io-layer/win32 handle for the current thread.
1136 * The handle need to be closed by calling CloseHandle () when it is no
1140 mono_thread_info_open_handle (void)
1142 return mono_threads_core_open_handle ();
1146 * mono_threads_open_thread_handle:
1148 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1149 * The handle need to be closed by calling CloseHandle () when it is no
1153 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1155 return mono_threads_core_open_thread_handle (handle, tid);
1159 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1161 mono_threads_core_set_name (tid, name);
1165 * mono_thread_info_prepare_interrupt:
1167 * See wapi_prepare_interrupt ().
1170 mono_thread_info_prepare_interrupt (HANDLE thread_handle)
1172 return mono_threads_core_prepare_interrupt (thread_handle);
1176 mono_thread_info_finish_interrupt (gpointer wait_handle)
1178 mono_threads_core_finish_interrupt (wait_handle);
1182 mono_thread_info_interrupt (HANDLE thread_handle)
1184 gpointer wait_handle;
1186 wait_handle = mono_thread_info_prepare_interrupt (thread_handle);
1187 mono_thread_info_finish_interrupt (wait_handle);
1191 mono_thread_info_self_interrupt (void)
1193 mono_threads_core_self_interrupt ();
1197 mono_thread_info_clear_interruption (void)
1199 mono_threads_core_clear_interruption ();
1202 /* info must be self or be held in a hazard pointer. */
1204 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1206 MonoAsyncJob old_job;
1208 old_job = info->service_requests;
1211 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1216 mono_threads_consume_async_jobs (void)
1218 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1223 return InterlockedExchange (&info->service_requests, 0);