6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 /* enable pthread extensions */
17 #define _DARWIN_C_SOURCE
20 #include <mono/utils/mono-compiler.h>
21 #include <mono/utils/mono-os-semaphore.h>
22 #include <mono/utils/mono-threads.h>
23 #include <mono/utils/mono-tls.h>
24 #include <mono/utils/hazard-pointer.h>
25 #include <mono/utils/mono-memory-model.h>
26 #include <mono/utils/mono-mmap.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-lazy-init.h>
30 #include <mono/utils/mono-coop-mutex.h>
31 #include <mono/utils/mono-coop-semaphore.h>
32 #include <mono/utils/mono-threads-coop.h>
33 #include <mono/utils/mono-threads-debug.h>
34 #include <mono/utils/os-event.h>
35 #include <mono/utils/w32api.h>
38 #include <mono/utils/mono-errno.h>
41 #include <mono/utils/mach-support.h>
45 Mutex that makes sure only a single thread can be suspending others.
46 Suspend is a very racy operation since it requires restarting until
47 the target thread is not on an unsafe region.
49 We could implement this using critical regions, but would be much much
50 harder for an operation that is hardly performance critical.
52 The GC has to acquire this lock before starting a STW to make sure
53 a runtime suspend won't make it wronly see a thread in a safepoint
54 when it is in fact not.
56 This has to be a naked locking primitive, and not a coop aware one, as
57 it needs to be usable when destroying thread_info_key, the TLS key for
58 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
59 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
60 to an assertion error. We then simply switch state manually in
61 mono_thread_info_suspend_lock_with_info.
63 static MonoSemType global_suspend_semaphore
;
65 static size_t thread_info_size
;
66 static MonoThreadInfoCallbacks threads_callbacks
;
67 static MonoThreadInfoRuntimeCallbacks runtime_callbacks
;
68 static MonoNativeTlsKey thread_info_key
, thread_exited_key
;
69 #ifdef MONO_KEYWORD_THREAD
70 static MONO_KEYWORD_THREAD gint32 tls_small_id
= -1;
72 static MonoNativeTlsKey small_id_key
;
74 static MonoLinkedListSet thread_list
;
75 static gboolean mono_threads_inited
= FALSE
;
77 static MonoSemType suspend_semaphore
;
78 static size_t pending_suspends
;
80 static mono_mutex_t join_mutex
;
82 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
85 #define SLEEP_DURATION_BEFORE_WARNING (50)
87 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
89 static guint32 sleepWarnDuration
= SLEEP_DURATION_BEFORE_WARNING
,
90 sleepAbortDuration
= SLEEP_DURATION_BEFORE_ABORT
;
92 static int suspend_posts
, resume_posts
, abort_posts
, waits_done
, pending_ops
;
95 mono_threads_notify_initiator_of_abort (MonoThreadInfo
* info
)
97 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info
));
98 mono_atomic_inc_i32 (&abort_posts
);
99 mono_os_sem_post (&suspend_semaphore
);
103 mono_threads_notify_initiator_of_suspend (MonoThreadInfo
* info
)
105 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info
));
106 // check that the thread is really in a valid suspended state.
107 g_assert (mono_thread_info_get_suspend_state (info
) != NULL
);
108 mono_atomic_inc_i32 (&suspend_posts
);
109 mono_os_sem_post (&suspend_semaphore
);
113 mono_threads_notify_initiator_of_resume (MonoThreadInfo
* info
)
115 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info
));
116 mono_atomic_inc_i32 (&resume_posts
);
117 mono_os_sem_post (&suspend_semaphore
);
121 BeginSuspendFail
= 0,
122 BeginSuspendOkPreemptive
= 1,
123 BeginSuspendOkCooperative
= 2,
124 BeginSuspendOkNoWait
= 3,
125 } BeginSuspendResult
;
127 static BeginSuspendResult
128 begin_cooperative_suspend (MonoThreadInfo
*info
)
130 /* There's nothing else to do after we async request the thread to suspend */
131 mono_threads_add_to_pending_operation_set (info
);
132 return BeginSuspendOkCooperative
;
135 static BeginSuspendResult
136 begin_preemptive_suspend (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
138 if (mono_threads_suspend_begin_async_suspend (info
, interrupt_kernel
))
139 return BeginSuspendOkPreemptive
;
141 return BeginSuspendFail
;
144 static BeginSuspendResult
145 begin_suspend_for_running_thread (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
147 /* If we're using full cooperative suspend or hybrid suspend,
148 * cooperatively suspend RUNNING threads */
149 if (mono_threads_are_safepoints_enabled ())
150 return begin_cooperative_suspend (info
);
152 return begin_preemptive_suspend (info
, interrupt_kernel
);
155 static BeginSuspendResult
156 begin_suspend_for_blocking_thread (MonoThreadInfo
*info
, gboolean interrupt_kernel
, MonoThreadSuspendPhase phase
, gboolean
*did_interrupt
)
158 // if a thread can't transition to blocking, we certainly shouldn't be
159 // trying to suspend it like it's blocking.
160 g_assert (mono_threads_is_blocking_transition_enabled ());
161 // with hybrid suspend, preemptively suspend blocking threads,
162 // otherwise blocking already counts as suspended.
163 if (mono_threads_is_hybrid_suspension_enabled ()) {
165 *did_interrupt
= interrupt_kernel
;
168 case MONO_THREAD_SUSPEND_PHASE_INITIAL
:
169 /* In hybrid suspend, in the first phase, a thread in
170 * blocking can continue running (and possibly
171 * self-suspend). We'll preemptively suspend it in the
173 return BeginSuspendOkNoWait
;
174 case MONO_THREAD_SUSPEND_PHASE_MOPUP
:
175 return begin_preemptive_suspend (info
, interrupt_kernel
);
177 g_assert_not_reached ();
181 *did_interrupt
= FALSE
;
182 // In full cooperative suspend, treat a thread in BLOCKING as
183 // already suspended and don't wait for it.
184 return BeginSuspendOkNoWait
;
189 check_async_suspend (MonoThreadInfo
*info
, BeginSuspendResult result
)
192 case BeginSuspendOkCooperative
:
194 case BeginSuspendOkPreemptive
:
195 return mono_threads_suspend_check_suspend_result (info
);
196 case BeginSuspendFail
:
198 case BeginSuspendOkNoWait
:
201 g_assert_not_reached ();
206 resume_async_suspended (MonoThreadInfo
*info
)
208 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
209 g_assert_not_reached ();
211 g_assert (mono_threads_suspend_begin_async_resume (info
));
215 resume_self_suspended (MonoThreadInfo
* info
)
217 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info
));
218 mono_os_sem_post (&info
->resume_semaphore
);
222 mono_thread_info_wait_for_resume (MonoThreadInfo
* info
)
225 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info
));
226 res
= mono_os_sem_wait (&info
->resume_semaphore
, MONO_SEM_FLAGS_NONE
);
227 g_assert (res
!= -1);
231 resume_blocking_suspended (MonoThreadInfo
* info
)
233 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info
));
234 mono_os_sem_post (&info
->resume_semaphore
);
238 mono_threads_add_to_pending_operation_set (MonoThreadInfo
* info
)
240 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info
));
242 mono_atomic_inc_i32 (&pending_ops
);
246 mono_threads_begin_global_suspend (void)
248 size_t ps
= pending_suspends
;
249 if (G_UNLIKELY (ps
!= 0))
250 g_error ("pending_suspends = %d, but must be 0", ps
);
251 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
,
252 abort_posts
, waits_done
, pending_ops
);
253 g_assert ((suspend_posts
+ resume_posts
+ abort_posts
) == waits_done
);
254 mono_threads_coop_begin_global_suspend ();
258 mono_threads_end_global_suspend (void)
260 size_t ps
= pending_suspends
;
261 if (G_UNLIKELY (ps
!= 0))
262 g_error ("pending_suspends = %d, but must be 0", ps
);
263 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts
, resume_posts
,
264 abort_posts
, waits_done
, pending_ops
);
265 g_assert ((suspend_posts
+ resume_posts
+ abort_posts
) == waits_done
);
266 mono_threads_coop_end_global_suspend ();
272 MonoThreadInfo
*cur
= mono_thread_info_current ();
274 g_async_safe_printf ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
275 g_async_safe_printf ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
276 g_async_safe_printf ("\t0x1\t- detached (GOOD, unless the thread is running managed code)\n");
277 g_async_safe_printf ("\t0x2\t- running (BAD, unless it's the gc thread)\n");
278 g_async_safe_printf ("\t0x?03\t- async suspended (GOOD)\n");
279 g_async_safe_printf ("\t0x?04\t- self suspended (GOOD)\n");
280 g_async_safe_printf ("\t0x?05\t- async suspend requested (BAD)\n");
281 g_async_safe_printf ("\t0x6\t- blocking (BAD, unless there's no suspend initiator)\n");
282 g_async_safe_printf ("\t0x?07\t- blocking async suspended (GOOD)\n");
283 g_async_safe_printf ("\t0x?08\t- blocking self suspended (GOOD)\n");
284 g_async_safe_printf ("\t0x?09\t- blocking suspend requested (BAD in coop; GOOD in hybrid)\n");
286 FOREACH_THREAD_SAFE_ALL (info
) {
288 char thread_name
[256] = { 0 };
289 pthread_getname_np (mono_thread_info_get_tid (info
), thread_name
, 255);
291 g_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" : "" );
293 g_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" : "" );
295 } FOREACH_THREAD_SAFE_END
299 mono_threads_wait_pending_operations (void)
302 int c
= pending_suspends
;
304 /* Wait threads to park */
305 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c
);
306 if (pending_suspends
) {
307 MonoStopwatch suspension_time
;
308 mono_stopwatch_start (&suspension_time
);
309 for (i
= 0; i
< pending_suspends
; ++i
) {
310 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
311 mono_atomic_inc_i32 (&waits_done
);
312 if (mono_os_sem_timedwait (&suspend_semaphore
, sleepAbortDuration
, MONO_SEM_FLAGS_NONE
) == MONO_SEM_TIMEDWAIT_RET_SUCCESS
)
314 mono_stopwatch_stop (&suspension_time
);
318 g_async_safe_printf ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends
, i
);
319 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time
), sleepAbortDuration
);
321 mono_stopwatch_stop (&suspension_time
);
322 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends
, (int)mono_stopwatch_elapsed_ms (&suspension_time
));
326 pending_suspends
= 0;
332 //Thread initialization code
335 mono_hazard_pointer_clear_all (MonoThreadHazardPointers
*hp
, int retain
)
338 mono_hazard_pointer_clear (hp
, 0);
340 mono_hazard_pointer_clear (hp
, 1);
342 mono_hazard_pointer_clear (hp
, 2);
346 If return non null Hazard Pointer 1 holds the return value.
349 mono_thread_info_lookup (MonoNativeThreadId id
)
351 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
353 if (!mono_lls_find (&thread_list
, hp
, (uintptr_t)id
)) {
354 mono_hazard_pointer_clear_all (hp
, -1);
358 mono_hazard_pointer_clear_all (hp
, 1);
359 return (MonoThreadInfo
*) mono_hazard_pointer_get_val (hp
, 1);
363 mono_thread_info_insert (MonoThreadInfo
*info
)
365 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
367 if (!mono_lls_insert (&thread_list
, hp
, (MonoLinkedListSetNode
*)info
)) {
368 mono_hazard_pointer_clear_all (hp
, -1);
372 mono_hazard_pointer_clear_all (hp
, -1);
377 mono_thread_info_remove (MonoThreadInfo
*info
)
379 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
382 THREADS_DEBUG ("removing info %p\n", info
);
383 res
= mono_lls_remove (&thread_list
, hp
, (MonoLinkedListSetNode
*)info
);
384 mono_hazard_pointer_clear_all (hp
, -1);
389 free_thread_info (gpointer mem
)
391 MonoThreadInfo
*info
= (MonoThreadInfo
*) mem
;
393 mono_os_sem_destroy (&info
->resume_semaphore
);
394 mono_threads_suspend_free (info
);
400 * mono_thread_info_register_small_id
402 * Registers a small ID for the current thread. This is a 16-bit value uniquely
403 * identifying the current thread. If the current thread already has a small ID
404 * assigned, that small ID will be returned; otherwise, the newly assigned small
408 mono_thread_info_register_small_id (void)
410 int small_id
= mono_thread_info_get_small_id ();
415 small_id
= mono_thread_small_id_alloc ();
416 #ifdef MONO_KEYWORD_THREAD
417 tls_small_id
= small_id
;
419 mono_native_tls_set_value (small_id_key
, GUINT_TO_POINTER (small_id
+ 1));
425 thread_handle_destroy (gpointer data
)
427 MonoThreadHandle
*thread_handle
;
429 thread_handle
= (MonoThreadHandle
*) data
;
431 mono_os_event_destroy (&thread_handle
->event
);
432 g_free (thread_handle
);
436 register_thread (MonoThreadInfo
*info
)
439 guint8
*staddr
= NULL
;
442 info
->small_id
= mono_thread_info_register_small_id ();
443 mono_thread_info_set_tid (info
, mono_native_thread_id_get ());
445 info
->handle
= g_new0 (MonoThreadHandle
, 1);
446 mono_refcount_init (info
->handle
, thread_handle_destroy
);
447 mono_os_event_init (&info
->handle
->event
, FALSE
);
449 mono_os_sem_init (&info
->resume_semaphore
, 0);
451 /*set TLS early so SMR works */
452 mono_native_tls_set_value (thread_info_key
, info
);
454 mono_thread_info_get_stack_bounds (&staddr
, &stsize
);
457 info
->stack_start_limit
= staddr
;
458 info
->stack_end
= staddr
+ stsize
;
460 info
->stackdata
= g_byte_array_new ();
462 info
->internal_thread_gchandle
= G_MAXUINT32
;
464 info
->profiler_signal_ack
= 1;
466 #ifdef USE_WINDOWS_BACKEND
467 info
->win32_apc_info
= 0;
468 info
->win32_apc_info_io_handle
= INVALID_HANDLE_VALUE
;
471 mono_threads_suspend_register (info
);
473 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info
, mono_thread_info_get_tid (info
), info
->small_id
);
475 if (threads_callbacks
.thread_attach
) {
476 if (!threads_callbacks
.thread_attach (info
)) {
477 // g_warning ("thread registation failed\n");
478 mono_native_tls_set_value (thread_info_key
, NULL
);
484 Transition it before taking any locks or publishing itself to reduce the chance
485 of others witnessing a detached thread.
486 We can reasonably expect that until this thread gets published, no other thread will
487 try to manipulate it.
489 mono_threads_transition_attach (info
);
490 mono_thread_info_suspend_lock ();
491 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
492 result
= mono_thread_info_insert (info
);
494 mono_thread_info_suspend_unlock ();
500 mono_thread_info_suspend_lock_with_info (MonoThreadInfo
*info
);
503 mono_threads_signal_thread_handle (MonoThreadHandle
* thread_handle
);
506 unregister_thread (void *arg
)
508 MONO_STACKDATA (gc_unsafe_stackdata
);
509 MonoThreadInfo
*info
;
512 MonoThreadHandle
* handle
;
514 info
= (MonoThreadInfo
*) arg
;
516 g_assert (mono_thread_info_is_current (info
));
517 g_assert (mono_thread_info_is_live (info
));
519 /* Pump the HP queue while the thread is alive.*/
520 mono_thread_hazardous_try_free_some ();
522 small_id
= info
->small_id
;
524 /* We only enter the GC unsafe region, as when exiting this function, the thread
525 * will be detached, and the current MonoThreadInfo* will be destroyed. */
526 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info
, &gc_unsafe_stackdata
);
528 THREADS_DEBUG ("unregistering info %p\n", info
);
530 mono_native_tls_set_value (thread_exited_key
, GUINT_TO_POINTER (1));
533 * TLS destruction order is not reliable so small_id might be cleaned up
536 #ifndef MONO_KEYWORD_THREAD
537 mono_native_tls_set_value (small_id_key
, GUINT_TO_POINTER (info
->small_id
+ 1));
540 /* we need to duplicate it, as the info->handle is going
541 * to be closed when unregistering from the platform */
542 handle
= mono_threads_open_thread_handle (info
->handle
);
545 First perform the callback that requires no locks.
546 This callback has the potential of taking other locks, so we do it before.
547 After it completes, the thread remains functional.
549 if (threads_callbacks
.thread_detach
)
550 threads_callbacks
.thread_detach (info
);
552 mono_thread_info_suspend_lock_with_info (info
);
555 Now perform the callback that must be done under locks.
556 This will render the thread useless and non-suspendable, so it must
557 be done while holding the suspend lock to give no other thread chance
560 if (threads_callbacks
.thread_detach_with_lock
)
561 threads_callbacks
.thread_detach_with_lock (info
);
563 /* The thread is no longer active, so unref its handle */
564 mono_threads_close_thread_handle (info
->handle
);
567 result
= mono_thread_info_remove (info
);
569 mono_threads_transition_detach (info
);
571 mono_thread_info_suspend_unlock ();
573 g_byte_array_free (info
->stackdata
, /*free_segment=*/TRUE
);
575 /*now it's safe to free the thread info.*/
576 mono_thread_hazardous_try_free (info
, free_thread_info
);
578 mono_thread_small_id_free (small_id
);
580 mono_threads_signal_thread_handle (handle
);
582 mono_threads_close_thread_handle (handle
);
584 mono_native_tls_set_value (thread_info_key
, NULL
);
588 thread_exited_dtor (void *arg
)
590 #if defined(__MACH__)
592 * Since we use pthread dtors to clean up thread data, if a thread
593 * is attached to the runtime by another pthread dtor after our dtor
594 * has ran, it will never be detached, leading to various problems
595 * since the thread ids etc. will be reused while they are still in
596 * the threads hashtables etc.
597 * Dtors are called in a loop until all user tls entries are 0,
598 * but the loop has a maximum count (4), so if we set the tls
599 * variable every time, it will remain set when system tls dtors
600 * are ran. This allows mono_thread_info_is_exiting () to detect
601 * whenever the thread is exiting, even if it is executed from a
602 * system tls dtor (i.e. obj-c dealloc methods).
604 mono_native_tls_set_value (thread_exited_key
, GUINT_TO_POINTER (1));
609 mono_thread_info_current_unchecked (void)
611 return mono_threads_inited
? (MonoThreadInfo
*)mono_native_tls_get_value (thread_info_key
) : NULL
;
616 mono_thread_info_current (void)
618 MonoThreadInfo
*info
= (MonoThreadInfo
*)mono_native_tls_get_value (thread_info_key
);
622 info
= mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
625 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
626 The way to distinguish between before, during and after cleanup is the following:
628 -If the TLS key is set, cleanup has not begun;
629 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
630 -If the thread is nowhere to be found, cleanup has finished.
632 We cannot function after cleanup since there's no way to ensure what will happen.
636 /*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 */
637 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
643 * mono_thread_info_get_small_id
645 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
646 * identifying the current thread. Returns -1 if the current thread doesn't have
647 * a small ID assigned.
649 * To ensure that the calling thread has a small ID assigned, call either
650 * mono_thread_info_attach or mono_thread_info_register_small_id.
653 mono_thread_info_get_small_id (void)
655 #ifdef MONO_KEYWORD_THREAD
658 gpointer val
= mono_native_tls_get_value (small_id_key
);
661 return GPOINTER_TO_INT (val
) - 1;
666 mono_thread_info_list_head (void)
672 mono_thread_info_attach (void)
674 MonoThreadInfo
*info
;
677 if (!mono_threads_inited
)
679 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
680 * thread is created before an embedding API user initialized Mono. */
681 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
686 g_assert (mono_threads_inited
);
688 info
= (MonoThreadInfo
*) mono_native_tls_get_value (thread_info_key
);
690 info
= (MonoThreadInfo
*) g_malloc0 (thread_info_size
);
691 THREADS_DEBUG ("attaching %p\n", info
);
692 if (!register_thread (info
)) {
702 mono_thread_info_detach (void)
704 MonoThreadInfo
*info
;
707 if (!mono_threads_inited
)
709 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
710 * is created before an embedding API user initialized Mono. */
711 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
716 g_assert (mono_threads_inited
);
718 info
= (MonoThreadInfo
*) mono_native_tls_get_value (thread_info_key
);
720 THREADS_DEBUG ("detaching %p\n", info
);
721 unregister_thread (info
);
726 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo
*info
, guint32
*gchandle
)
729 g_assert (mono_thread_info_is_current (info
));
731 if (info
->internal_thread_gchandle
== G_MAXUINT32
)
734 *gchandle
= info
->internal_thread_gchandle
;
739 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo
*info
, guint32 gchandle
)
742 g_assert (mono_thread_info_is_current (info
));
743 g_assert (gchandle
!= G_MAXUINT32
);
744 info
->internal_thread_gchandle
= gchandle
;
748 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE
*info
)
751 g_assert (mono_thread_info_is_current (info
));
752 info
->internal_thread_gchandle
= G_MAXUINT32
;
756 * mono_thread_info_is_exiting:
758 * Return whenever the current thread is exiting, i.e. it is running pthread
762 mono_thread_info_is_exiting (void)
764 #if defined(__MACH__)
765 if (mono_native_tls_get_value (thread_exited_key
) == GUINT_TO_POINTER (1))
773 thread_info_key_dtor (void *arg
)
775 /* Put the MonoThreadInfo back for the duration of the
776 * unregister code. In some circumstances the thread needs to
777 * take the GC lock which may block which requires a coop
778 * state transition. */
779 mono_native_tls_set_value (thread_info_key
, arg
);
780 unregister_thread (arg
);
781 mono_native_tls_set_value (thread_info_key
, NULL
);
786 mono_thread_info_get_flags (MonoThreadInfo
*info
)
788 return (MonoThreadInfoFlags
)mono_atomic_load_i32 (&info
->flags
);
792 mono_thread_info_set_flags (MonoThreadInfoFlags flags
)
794 MonoThreadInfo
*info
= mono_thread_info_current ();
795 MonoThreadInfoFlags old
= (MonoThreadInfoFlags
)mono_atomic_load_i32 (&info
->flags
);
797 if (threads_callbacks
.thread_flags_changing
)
798 threads_callbacks
.thread_flags_changing (old
, flags
);
800 mono_atomic_store_i32 (&info
->flags
, flags
);
802 if (threads_callbacks
.thread_flags_changed
)
803 threads_callbacks
.thread_flags_changed (old
, flags
);
806 #define MONO_END_INIT_CB GINT_TO_POINTER(-1)
807 static GSList
*init_callbacks
;
810 mono_thread_info_wait_inited (void)
813 mono_os_sem_init (&cb
, 0);
814 GSList
*old
= init_callbacks
;
817 wait_request
.data
= &cb
;
818 wait_request
.next
= old
;
820 while (mono_threads_inited
!= TRUE
) {
821 GSList
*old_read
= (GSList
*)mono_atomic_cas_ptr ((gpointer
*) &init_callbacks
, &wait_request
, old
);
823 // Queued up waiter, need to be unstuck
824 if (old_read
== old
) {
826 } else if (old_read
== GINT_TO_POINTER (MONO_END_INIT_CB
)) {
830 // We raced with another writer
831 wait_request
.next
= (GSList
*) old_read
;
836 while (mono_threads_inited
!= TRUE
) {
837 gboolean timedout
= mono_os_sem_timedwait (&cb
, 1000, MONO_SEM_FLAGS_NONE
) == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
;
842 g_assert (mono_threads_inited
);
847 mono_thread_info_set_inited (void)
849 mono_threads_inited
= TRUE
;
850 mono_memory_barrier ();
852 GSList
*old
= init_callbacks
;
855 GSList
* old_read
= (GSList
*)mono_atomic_cas_ptr ((gpointer
*) &init_callbacks
, MONO_END_INIT_CB
, (gpointer
) old
);
861 if (old
== MONO_END_INIT_CB
) {
862 // Try not to use g_error / g_warning because this machinery used by logging
863 // Don't want to loop back into it.
864 fprintf (stderr
, "Global threads inited twice");
869 while (old
!= NULL
) {
870 GSList
*curr
= (GSList
*) old
;
871 GSList
*next
= old
->next
;
873 mono_os_sem_post ((MonoSemType
*)curr
->data
);
881 mono_thread_info_cleanup ()
883 mono_native_tls_free (thread_info_key
);
884 mono_native_tls_free (thread_exited_key
);
888 mono_thread_info_init (size_t info_size
)
891 thread_info_size
= info_size
;
894 res
= mono_native_tls_alloc (&thread_info_key
, NULL
);
895 res
= mono_native_tls_alloc (&thread_exited_key
, NULL
);
897 res
= mono_native_tls_alloc (&thread_info_key
, (void *) thread_info_key_dtor
);
898 res
= mono_native_tls_alloc (&thread_exited_key
, (void *) thread_exited_dtor
);
903 #ifndef MONO_KEYWORD_THREAD
904 res
= mono_native_tls_alloc (&small_id_key
, NULL
);
908 if ((sleepLimit
= g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL
) {
910 long threshold
= strtol(sleepLimit
, NULL
, 10);
911 if ((errno
== 0) && (threshold
>= 40)) {
912 sleepAbortDuration
= threshold
;
913 sleepWarnDuration
= threshold
/ 20;
915 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
919 mono_os_sem_init (&global_suspend_semaphore
, 1);
920 mono_os_sem_init (&suspend_semaphore
, 0);
921 mono_os_mutex_init (&join_mutex
);
923 mono_lls_init (&thread_list
, NULL
);
924 mono_thread_smr_init ();
925 mono_threads_suspend_init ();
926 mono_threads_coop_init ();
927 mono_threads_platform_init ();
929 mono_thread_info_set_inited ();
931 g_assert (sizeof (MonoNativeThreadId
) <= sizeof (uintptr_t));
935 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks
*callbacks
)
937 threads_callbacks
= *callbacks
;
941 mono_thread_info_signals_init (void)
943 mono_threads_suspend_init_signals ();
947 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks
*callbacks
)
949 runtime_callbacks
= *callbacks
;
952 MonoThreadInfoRuntimeCallbacks
*
953 mono_threads_get_runtime_callbacks (void)
955 return &runtime_callbacks
;
959 mono_thread_info_core_resume (MonoThreadInfo
*info
)
961 gboolean res
= FALSE
;
963 switch (mono_threads_transition_request_resume (info
)) {
970 case ResumeInitSelfResume
:
971 resume_self_suspended (info
);
974 case ResumeInitAsyncResume
:
975 resume_async_suspended (info
);
978 case ResumeInitBlockingResume
:
979 resume_blocking_suspended (info
);
988 * Current thread must hold the global_suspend_semaphore.
989 * The given MonoThreadInfo* is a suspended thread.
990 * Must be using hybrid suspend.
993 mono_thread_info_core_pulse (MonoThreadInfo
*info
)
995 gboolean res
= FALSE
;
997 switch (mono_threads_transition_request_pulse (info
)) {
998 case PulseInitAsyncPulse
:
999 resume_async_suspended (info
);
1007 mono_thread_info_resume (MonoNativeThreadId tid
)
1009 gboolean result
; /* don't initialize it so the compiler can catch unitilized paths. */
1010 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1011 MonoThreadInfo
*info
;
1013 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid
);
1015 mono_thread_info_suspend_lock ();
1017 info
= mono_thread_info_lookup (tid
); /*info on HP1*/
1023 result
= mono_thread_info_core_resume (info
);
1025 //Wait for the pending resume to finish
1026 mono_threads_wait_pending_operations ();
1029 mono_thread_info_suspend_unlock ();
1030 mono_hazard_pointer_clear (hp
, 1);
1034 static MonoThreadBeginSuspendResult
1035 begin_suspend_request_suspension_cordially (MonoThreadInfo
*info
);
1037 static MonoThreadBeginSuspendResult
1038 begin_suspend_peek_and_preempt (MonoThreadInfo
*info
);
1041 MonoThreadBeginSuspendResult
1042 mono_thread_info_begin_suspend (MonoThreadInfo
*info
, MonoThreadSuspendPhase phase
)
1044 if (phase
== MONO_THREAD_SUSPEND_PHASE_MOPUP
&& mono_threads_is_hybrid_suspension_enabled ())
1045 return begin_suspend_peek_and_preempt (info
);
1047 return begin_suspend_request_suspension_cordially (info
);
1050 MonoThreadBeginSuspendResult
1051 begin_suspend_request_suspension_cordially (MonoThreadInfo
*info
)
1053 /* Ask the thread nicely to suspend. In hybrid suspend, blocking
1054 * threads are transitioned to blocking_suspend_requested, but not
1055 * preemptively suspend in the current phase.
1057 switch (mono_threads_transition_request_suspension (info
)) {
1058 case ReqSuspendAlreadySuspended
:
1059 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1060 case ReqSuspendAlreadySuspendedBlocking
:
1061 if (mono_threads_is_hybrid_suspension_enabled ()) {
1062 /* This should only happen in the second phase of
1063 * hybrid suspend. It means that the first phase asked
1064 * the thread to suspend but did not signal it to
1065 * suspend preemptively. */
1066 g_assert_not_reached ();
1068 // This state should not be possible if we're using preemptive
1069 // suspend on a blocking thread - there can only be a single
1070 // suspend initiator at a time (guarded by
1071 // mono_thread_info_suspend_lock), and we expect the victim
1072 // thread to finish the two-phase preemptive suspension
1073 // procedure (and reach the ReqSuspendAlreadySuspended stage)
1074 // before the next suspend initiator can begin.
1075 g_assert (mono_threads_is_blocking_transition_enabled ());
1077 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1079 case ReqSuspendInitSuspendBlocking
:
1080 // in full cooperative mode just leave BLOCKING
1081 // threads running until they try to return to RUNNING, so
1082 // nothing to do, in hybrid coop preempt the thread.
1083 switch (begin_suspend_for_blocking_thread (info
, FALSE
, MONO_THREAD_SUSPEND_PHASE_INITIAL
, NULL
)) {
1084 case BeginSuspendFail
:
1085 return MONO_THREAD_BEGIN_SUSPEND_SKIP
;
1086 case BeginSuspendOkNoWait
:
1087 if (mono_threads_is_hybrid_suspension_enabled ())
1088 return MONO_THREAD_BEGIN_SUSPEND_NEXT_PHASE
;
1090 g_assert (mono_threads_is_cooperative_suspension_enabled ());
1091 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1093 case BeginSuspendOkPreemptive
:
1094 case BeginSuspendOkCooperative
:
1095 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1097 g_assert_not_reached ();
1099 case ReqSuspendInitSuspendRunning
:
1100 // in full preemptive mode this should be a preemptive suspend
1101 // in full and hybrid cooperative modes this should be a coop suspend
1102 if (begin_suspend_for_running_thread (info
, FALSE
) != BeginSuspendFail
)
1103 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1105 return MONO_THREAD_BEGIN_SUSPEND_SKIP
;
1107 g_assert_not_reached ();
1111 MonoThreadBeginSuspendResult
1112 begin_suspend_peek_and_preempt (MonoThreadInfo
*info
)
1114 /* This only makes sense for two-phase hybrid suspension:
1115 * requires that a suspension request transition was already performed for 'info',
1116 * and if it is still in blocking_suspend_requested, preemptively suspends it.
1118 g_assert (mono_threads_is_hybrid_suspension_enabled ());
1119 if (mono_threads_transition_peek_blocking_suspend_requested (info
)) {
1120 // in full cooperative mode just leave BLOCKING
1121 // threads running until they try to return to RUNNING, so
1122 // nothing to do, in hybrid coop preempt the thread.
1123 switch (begin_suspend_for_blocking_thread (info
, FALSE
, MONO_THREAD_SUSPEND_PHASE_MOPUP
, NULL
)) {
1124 case BeginSuspendFail
:
1125 return MONO_THREAD_BEGIN_SUSPEND_SKIP
;
1126 case BeginSuspendOkNoWait
:
1127 case BeginSuspendOkCooperative
:
1128 /* can't happen - should've suspended in the previous phase */
1129 g_assert_not_reached ();
1130 case BeginSuspendOkPreemptive
:
1131 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1133 g_assert_not_reached ();
1136 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1140 mono_thread_info_begin_resume (MonoThreadInfo
*info
)
1142 return mono_thread_info_core_resume (info
);
1146 mono_thread_info_begin_pulse_resume_and_request_suspension (MonoThreadInfo
*info
)
1148 /* For two-phase suspend, we want to atomically resume the thread and
1149 * request that it try to cooperatively suspend again. Specifically,
1150 * we really don't want it to transition from GC Safe to GC Unsafe
1151 * because we then it could (in GC Unsafe) try to take a lock that's
1152 * held by another preemptively-suspended thread, essentially
1153 * recreating the same problem that two-phase suspend intends to
1155 if (mono_threads_is_multiphase_stw_enabled ())
1156 return mono_thread_info_core_pulse (info
);
1158 return mono_thread_info_core_resume (info
);
1161 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
1162 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
1165 is_thread_in_critical_region (MonoThreadInfo
*info
)
1167 gpointer stack_start
;
1168 MonoThreadUnwindState
*state
;
1170 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info
)))
1173 /* Are we inside a system critical region? */
1174 if (info
->inside_critical_region
)
1177 /* Are we inside a GC critical region? */
1178 if (threads_callbacks
.thread_in_critical_region
&& threads_callbacks
.thread_in_critical_region (info
)) {
1182 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
1183 state
= mono_thread_info_get_suspend_state (info
);
1184 if (!state
->unwind_data
[MONO_UNWIND_DATA_DOMAIN
])
1187 stack_start
= MONO_CONTEXT_GET_SP (&state
->ctx
);
1188 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
1189 if (stack_start
< info
->stack_start_limit
|| stack_start
>= info
->stack_end
)
1192 if (threads_callbacks
.ip_in_critical_region
)
1193 return threads_callbacks
.ip_in_critical_region ((MonoDomain
*) state
->unwind_data
[MONO_UNWIND_DATA_DOMAIN
], (char *) MONO_CONTEXT_GET_IP (&state
->ctx
));
1199 mono_thread_info_in_critical_location (MonoThreadInfo
*info
)
1201 return is_thread_in_critical_region (info
);
1205 The return value is only valid until a matching mono_thread_info_resume is called
1207 static MonoThreadInfo
*
1208 suspend_sync (MonoNativeThreadId tid
, gboolean interrupt_kernel
)
1210 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1211 MonoThreadInfo
*info
= mono_thread_info_lookup (tid
); /*info on HP1*/
1215 BeginSuspendResult suspend_result
= BeginSuspendFail
;
1216 switch (mono_threads_transition_request_suspension (info
)) {
1217 case ReqSuspendAlreadySuspended
:
1218 mono_hazard_pointer_clear (hp
, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
1220 case ReqSuspendInitSuspendRunning
:
1221 suspend_result
= begin_suspend_for_running_thread (info
, interrupt_kernel
);
1222 if (suspend_result
== BeginSuspendFail
) {
1223 mono_hazard_pointer_clear (hp
, 1);
1226 //Wait for the pending suspend to finish
1227 g_assert (suspend_result
!= BeginSuspendOkNoWait
);
1228 mono_threads_wait_pending_operations ();
1230 if (!check_async_suspend (info
, suspend_result
)) {
1231 mono_thread_info_core_resume (info
);
1232 mono_threads_wait_pending_operations ();
1233 mono_hazard_pointer_clear (hp
, 1);
1237 case ReqSuspendAlreadySuspendedBlocking
:
1238 // ReqSuspendAlreadySuspendedBlocking should not be possible if
1239 // we're using preemptive suspend on a blocking thread - a
1240 // suspend initiator holds the mono_thread_info_suspend_lock
1241 // (there is a single suspend initiator at a time), and we
1242 // expect the victim thread to finish the two-phase preemptive
1243 // suspension procedure (and reach the
1244 // ReqSuspendAlreadySuspended stage) before the next suspend
1245 // initiator can begin.
1246 g_assert (mono_threads_is_blocking_transition_enabled () && !mono_threads_is_hybrid_suspension_enabled ());
1248 // if we tried to preempt the thread already, do nothing.
1249 // otherwise (if it's running in blocking mode) try to abort the syscall.
1250 if (interrupt_kernel
)
1251 mono_threads_suspend_abort_syscall (info
);
1254 case ReqSuspendInitSuspendBlocking
: {
1255 gboolean did_interrupt
= FALSE
;
1256 suspend_result
= begin_suspend_for_blocking_thread (info
, interrupt_kernel
, MONO_THREAD_SUSPEND_PHASE_MOPUP
, &did_interrupt
);
1257 if (suspend_result
== BeginSuspendFail
) {
1258 mono_hazard_pointer_clear (hp
, 1);
1262 if (suspend_result
!= BeginSuspendOkNoWait
)
1263 mono_threads_wait_pending_operations ();
1265 if (!check_async_suspend (info
, suspend_result
)) {
1266 mono_thread_info_core_resume (info
);
1267 mono_threads_wait_pending_operations ();
1268 mono_hazard_pointer_clear (hp
, 1);
1272 // if we tried to preempt the thread already, do nothing.
1273 // otherwise (if it's running in blocking mode) try to abort the syscall.
1274 if (interrupt_kernel
&& !did_interrupt
)
1275 mono_threads_suspend_abort_syscall (info
);
1280 g_assert_not_reached ();
1282 g_assert_not_reached ();
1285 static MonoThreadInfo
*
1286 suspend_sync_nolock (MonoNativeThreadId id
, gboolean interrupt_kernel
)
1288 MonoThreadInfo
*info
= NULL
;
1289 int sleep_duration
= 0;
1291 if (!(info
= suspend_sync (id
, interrupt_kernel
))) {
1292 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1296 /*WARNING: We now are in interrupt context until we resume the thread. */
1297 if (!is_thread_in_critical_region (info
))
1300 if (!mono_thread_info_core_resume (info
)) {
1301 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1304 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id
);
1306 /* Wait for the pending resume to finish */
1307 mono_threads_wait_pending_operations ();
1309 if (sleep_duration
== 0)
1310 mono_thread_info_yield ();
1312 g_usleep (sleep_duration
);
1314 sleep_duration
+= 10;
1320 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id
, gboolean interrupt_kernel
, MonoSuspendThreadCallback callback
, gpointer user_data
)
1323 MonoThreadInfo
*info
= NULL
;
1324 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1326 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s)\n", (void*)id
, interrupt_kernel
? "int" : "");
1327 /*FIXME: unify this with self-suspend*/
1328 g_assert (id
!= mono_native_thread_id_get ());
1330 /* This can block during stw */
1331 mono_thread_info_suspend_lock ();
1332 mono_threads_begin_global_suspend ();
1334 info
= suspend_sync_nolock (id
, interrupt_kernel
);
1335 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s): info %p\n", (void*)id
, interrupt_kernel
? "int" : "", info
);
1339 switch (result
= callback (info
, user_data
)) {
1340 case MonoResumeThread
:
1341 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): MonoResumeThread\n", (void*)id
, interrupt_kernel
? "int" : "");
1342 mono_hazard_pointer_set (hp
, 1, info
);
1343 mono_thread_info_core_resume (info
);
1344 mono_threads_wait_pending_operations ();
1345 #ifdef USE_WINDOWS_BACKEND
1346 // If we interrupt kernel but have blocking sync IO requests preventing the thread from running APC's
1347 // try to abort all sync blocking IO request. This must be done after thread has been resumed, but before releasing
1348 // global suspend lock (preventing other threads from supsending the thread).
1349 if (interrupt_kernel
)
1350 mono_win32_abort_blocking_io_call (info
);
1354 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): KeepSuspended\n", (void*)id
, interrupt_kernel
? "int" : "");
1355 g_assert (!mono_threads_are_safepoints_enabled ());
1358 g_error ("Invalid suspend_and_run callback return value %d", result
);
1362 mono_hazard_pointer_clear (hp
, 1);
1363 mono_threads_end_global_suspend ();
1364 mono_thread_info_suspend_unlock ();
1368 Inject an assynchronous call into the target thread. The target thread must be suspended and
1369 only a single async call can be setup for a given suspend cycle.
1370 This async call must cause stack unwinding as the current implementation doesn't save enough state
1371 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1372 currently used only to deliver exceptions.
1375 mono_thread_info_setup_async_call (MonoThreadInfo
*info
, void (*target_func
)(void*), void *user_data
)
1377 if (!mono_threads_are_safepoints_enabled ()) {
1378 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1379 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1380 * region or entering a gc unsafe region */
1381 g_assert (mono_thread_info_run_state (info
) == STATE_ASYNC_SUSPENDED
);
1383 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1384 g_assert (!info
->async_target
);
1385 info
->async_target
= target_func
;
1386 /* This is not GC tracked */
1387 info
->user_data
= user_data
;
1391 The suspend lock is held during any suspend in progress.
1392 A GC that has safepoints must take this lock as part of its
1393 STW to make sure no unsafe pending suspend is in progress.
1397 mono_thread_info_suspend_lock_with_info (MonoThreadInfo
*info
)
1400 g_assert (mono_thread_info_is_current (info
));
1401 g_assert (mono_thread_info_is_live (info
));
1403 MONO_ENTER_GC_SAFE_WITH_INFO(info
);
1405 int res
= mono_os_sem_wait (&global_suspend_semaphore
, MONO_SEM_FLAGS_NONE
);
1406 g_assert (res
!= -1);
1408 MONO_EXIT_GC_SAFE_WITH_INFO
;
1412 mono_thread_info_suspend_lock (void)
1414 MonoThreadInfo
*info
;
1417 info
= mono_thread_info_current_unchecked ();
1418 if (info
&& mono_thread_info_is_live (info
)) {
1419 mono_thread_info_suspend_lock_with_info (info
);
1423 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1424 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1426 res
= mono_os_sem_wait (&global_suspend_semaphore
, MONO_SEM_FLAGS_NONE
);
1427 g_assert (res
!= -1);
1431 mono_thread_info_suspend_unlock (void)
1433 mono_os_sem_post (&global_suspend_semaphore
);
1436 /* Return the suspend state for the current thread. Note: the thread must be
1437 * already suspended in order for this function to be callable.
1439 MonoThreadUnwindState
*
1440 mono_thread_info_get_suspend_state (MonoThreadInfo
*info
)
1442 int cur_state
= mono_thread_info_current_state (info
);
1443 switch (cur_state
) {
1444 case STATE_ASYNC_SUSPENDED
:
1445 case STATE_BLOCKING_ASYNC_SUSPENDED
:
1446 return &info
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
];
1447 case STATE_SELF_SUSPENDED
:
1448 case STATE_BLOCKING_SELF_SUSPENDED
:
1449 return &info
->thread_saved_state
[SELF_SUSPEND_STATE_INDEX
];
1450 case STATE_BLOCKING_SUSPEND_REQUESTED
:
1451 // This state is only valid for full cooperative suspend. If
1452 // we're preemptively suspending blocking threads, this is not
1453 // a valid suspend state.
1454 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
1455 return &info
->thread_saved_state
[SELF_SUSPEND_STATE_INDEX
];
1462 STATE_ASYNC_SUSPEND_REQUESTED
1463 STATE_BLOCKING: All those are invalid suspend states.
1464 STATE_BLOCKING_SUSPEND_REQUESTED: Invalid if we're preemptively suspending blocking threads.
1466 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info
), mono_thread_state_name (cur_state
));
1470 * This is a very specific function whose only purpose is to
1471 * break a given thread from socket syscalls.
1473 * This only exists because linux won't fail a call to connect
1474 * if the underlying is closed.
1476 * TODO We should cleanup and unify this with the other syscall abort
1480 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid
)
1482 MonoThreadHazardPointers
*hp
;
1483 MonoThreadInfo
*info
;
1485 if (tid
== mono_native_thread_id_get ())
1488 mono_thread_info_suspend_lock ();
1489 hp
= mono_hazard_pointer_get ();
1490 info
= mono_thread_info_lookup (tid
);
1492 mono_thread_info_suspend_unlock ();
1495 mono_threads_begin_global_suspend ();
1497 mono_threads_suspend_abort_syscall (info
);
1498 mono_threads_wait_pending_operations ();
1500 mono_hazard_pointer_clear (hp
, 1);
1502 mono_threads_end_global_suspend ();
1503 mono_thread_info_suspend_unlock ();
1507 * mono_thread_info_set_is_async_context:
1509 * Set whenever the current thread is in an async context. Some runtime functions might behave
1510 * differently while in an async context in order to be async safe.
1513 mono_thread_info_set_is_async_context (gboolean async_context
)
1515 MonoThreadInfo
*info
= mono_thread_info_current ();
1518 // If this assert fails, that means there is recursion and/or
1519 // concurrency, such that setting async_context to FALSE
1520 // that all the callers do after this, is incorrect,
1521 // and this should instead be incremented/decremented.
1523 // As the value is only accessed via current(), that
1524 // limits the case to recursion, but increment/decrement
1525 // is still fast and correct and simple.
1526 g_assert (!async_context
|| !info
->is_async_context
);
1527 info
->is_async_context
= async_context
;
1532 mono_thread_info_is_async_context (void)
1534 MonoThreadInfo
*info
= mono_thread_info_current ();
1537 return info
->is_async_context
;
1543 * mono_thread_info_get_stack_bounds:
1545 * Return the address and size of the current threads stack. Return NULL as the
1546 * stack address if the stack address cannot be determined.
1549 mono_thread_info_get_stack_bounds (guint8
**staddr
, size_t *stsize
)
1551 guint8
*current
= (guint8
*)&stsize
;
1552 mono_threads_platform_get_stack_bounds (staddr
, stsize
);
1556 /* Sanity check the result */
1557 g_assert ((current
> *staddr
) && (current
< *staddr
+ *stsize
));
1559 /* When running under emacs, sometimes staddr is not aligned to a page size */
1560 *staddr
= (guint8
*)((gssize
)*staddr
& ~(mono_pagesize () - 1));
1564 mono_thread_info_yield (void)
1566 return mono_threads_platform_yield ();
1569 static mono_lazy_init_t sleep_init
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
1570 static MonoCoopMutex sleep_mutex
;
1571 static MonoCoopCond sleep_cond
;
1574 sleep_initialize (void)
1576 mono_coop_mutex_init (&sleep_mutex
);
1577 mono_coop_cond_init (&sleep_cond
);
1581 sleep_interrupt (gpointer data
)
1583 mono_coop_mutex_lock (&sleep_mutex
);
1584 mono_coop_cond_broadcast (&sleep_cond
);
1585 mono_coop_mutex_unlock (&sleep_mutex
);
1588 static inline guint32
1589 sleep_interruptable (guint32 ms
, gboolean
*alerted
)
1593 g_assert (MONO_INFINITE_WAIT
== G_MAXUINT32
);
1598 if (ms
!= MONO_INFINITE_WAIT
)
1599 end
= mono_msec_ticks() + ms
;
1601 mono_lazy_initialize (&sleep_init
, sleep_initialize
);
1603 mono_coop_mutex_lock (&sleep_mutex
);
1606 if (ms
!= MONO_INFINITE_WAIT
) {
1607 now
= mono_msec_ticks();
1612 mono_thread_info_install_interrupt (sleep_interrupt
, NULL
, alerted
);
1614 mono_coop_mutex_unlock (&sleep_mutex
);
1615 return WAIT_IO_COMPLETION
;
1618 if (ms
!= MONO_INFINITE_WAIT
)
1619 mono_coop_cond_timedwait (&sleep_cond
, &sleep_mutex
, end
- now
);
1621 mono_coop_cond_wait (&sleep_cond
, &sleep_mutex
);
1623 mono_thread_info_uninstall_interrupt (alerted
);
1625 mono_coop_mutex_unlock (&sleep_mutex
);
1626 return WAIT_IO_COMPLETION
;
1630 mono_coop_mutex_unlock (&sleep_mutex
);
1636 mono_thread_info_sleep (guint32 ms
, gboolean
*alerted
)
1639 MonoThreadInfo
*info
;
1641 mono_thread_info_yield ();
1643 info
= mono_thread_info_current ();
1644 if (info
&& mono_thread_info_is_interrupt_state (info
))
1645 return WAIT_IO_COMPLETION
;
1651 return sleep_interruptable (ms
, alerted
);
1655 if (ms
== MONO_INFINITE_WAIT
) {
1658 Sleep (G_MAXUINT32
);
1660 sleep (G_MAXUINT32
);
1665 #if defined (__linux__) && !defined(HOST_ANDROID)
1666 struct timespec start
, target
;
1668 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1669 ret
= clock_gettime (CLOCK_MONOTONIC
, &start
);
1670 g_assert (ret
== 0);
1673 target
.tv_sec
+= ms
/ 1000;
1674 target
.tv_nsec
+= (ms
% 1000) * 1000000;
1675 if (target
.tv_nsec
> 999999999) {
1676 target
.tv_nsec
-= 999999999;
1681 ret
= clock_nanosleep (CLOCK_MONOTONIC
, TIMER_ABSTIME
, &target
, NULL
);
1686 struct timespec req
, rem
;
1688 req
.tv_sec
= ms
/ 1000;
1689 req
.tv_nsec
= (ms
% 1000) * 1000000;
1692 memset (&rem
, 0, sizeof (rem
));
1693 ret
= nanosleep (&req
, &rem
);
1695 #endif /* __linux__ */
1704 mono_thread_info_usleep (guint64 us
)
1713 mono_thread_info_tls_get (THREAD_INFO_TYPE
*info
, MonoTlsKey key
)
1715 return ((MonoThreadInfo
*)info
)->tls
[key
];
1719 * mono_threads_info_tls_set:
1721 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1722 * values of TLS variables for threads other than the current thread.
1723 * This should only be used for infrequently changing TLS variables, and it should
1724 * be paired with setting the real TLS variable since this provides no GC tracking.
1727 mono_thread_info_tls_set (THREAD_INFO_TYPE
*info
, MonoTlsKey key
, gpointer value
)
1729 ((MonoThreadInfo
*)info
)->tls
[key
] = value
;
1733 * mono_thread_info_exit:
1735 * Exit the current thread.
1736 * This function doesn't return.
1739 mono_thread_info_exit (gsize exit_code
)
1741 mono_thread_info_detach ();
1743 mono_threads_platform_exit (0);
1747 * mono_threads_open_thread_handle:
1749 * Duplicate the handle. The handle needs to be closed by calling
1750 * mono_threads_close_thread_handle () when it is no longer needed.
1753 mono_threads_open_thread_handle (MonoThreadHandle
*thread_handle
)
1755 return mono_refcount_inc (thread_handle
);
1759 mono_threads_close_thread_handle (MonoThreadHandle
*thread_handle
)
1763 mono_refcount_dec (thread_handle
);
1767 mono_threads_signal_thread_handle (MonoThreadHandle
* thread_handle
)
1769 mono_os_event_set (&thread_handle
->event
);
1772 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1774 struct _MonoThreadInfoInterruptToken
{
1775 void (*callback
) (gpointer data
);
1780 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1782 * - @callback: must be able to be called from another thread and always cancel the wait
1783 * - @data: passed to the callback
1784 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1785 * if set to TRUE, it must mean that the thread is in interrupted state
1788 mono_thread_info_install_interrupt (void (*callback
) (gpointer data
), gpointer data
, gboolean
*interrupted
)
1790 MonoThreadInfo
*info
;
1791 MonoThreadInfoInterruptToken
*previous_token
, *token
;
1793 g_assert (callback
);
1795 g_assert (interrupted
);
1796 *interrupted
= FALSE
;
1798 info
= mono_thread_info_current ();
1801 /* The memory of this token can be freed at 2 places:
1802 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1803 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1804 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1805 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1806 token
= g_new0 (MonoThreadInfoInterruptToken
, 1);
1807 token
->callback
= callback
;
1810 previous_token
= (MonoThreadInfoInterruptToken
*)mono_atomic_cas_ptr ((gpointer
*) &info
->interrupt_token
, token
, NULL
);
1812 if (previous_token
) {
1813 if (previous_token
!= INTERRUPT_STATE
)
1814 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE
, previous_token
);
1818 *interrupted
= TRUE
;
1821 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1822 mono_thread_info_get_tid (info
), token
, previous_token
, *interrupted
? "TRUE" : "FALSE");
1826 mono_thread_info_uninstall_interrupt (gboolean
*interrupted
)
1828 MonoThreadInfo
*info
;
1829 MonoThreadInfoInterruptToken
*previous_token
;
1831 g_assert (interrupted
);
1832 *interrupted
= FALSE
;
1834 info
= mono_thread_info_current ();
1837 previous_token
= (MonoThreadInfoInterruptToken
*)mono_atomic_xchg_ptr ((gpointer
*) &info
->interrupt_token
, NULL
);
1839 /* only the installer can uninstall the token */
1840 g_assert (previous_token
);
1842 if (previous_token
== INTERRUPT_STATE
) {
1843 /* if it is interrupted, then it is going to be freed in finish interrupt */
1844 *interrupted
= TRUE
;
1846 g_free (previous_token
);
1849 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1850 mono_thread_info_get_tid (info
), previous_token
, *interrupted
? "TRUE" : "FALSE");
1853 static MonoThreadInfoInterruptToken
*
1854 set_interrupt_state (MonoThreadInfo
*info
)
1856 MonoThreadInfoInterruptToken
*token
, *previous_token
;
1860 /* Atomically obtain the token the thread is
1861 * waiting on, and change it to a flag value. */
1864 previous_token
= info
->interrupt_token
;
1866 /* Already interrupted */
1867 if (previous_token
== INTERRUPT_STATE
) {
1872 token
= previous_token
;
1873 } while (mono_atomic_cas_ptr ((gpointer
*) &info
->interrupt_token
, INTERRUPT_STATE
, previous_token
) != previous_token
);
1879 * mono_thread_info_prepare_interrupt:
1881 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1882 * - if the thread calls one of the WaitFor functions, the function will return with
1883 * WAIT_IO_COMPLETION instead of waiting
1884 * - if the thread was waiting when this function was called, the wait will be broken
1886 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1887 * didn't receive the interrupt signal yet, in this case it should call the wait function
1888 * again. This essentially means that the target thread will busy wait until it is ready to
1889 * process the interruption.
1891 MonoThreadInfoInterruptToken
*
1892 mono_thread_info_prepare_interrupt (MonoThreadInfo
*info
)
1894 MonoThreadInfoInterruptToken
*token
;
1896 token
= set_interrupt_state (info
);
1898 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1899 mono_thread_info_get_tid (info
), token
);
1905 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken
*token
)
1907 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token
);
1912 g_assert (token
->callback
);
1914 token
->callback (token
->data
);
1920 mono_thread_info_self_interrupt (void)
1922 MonoThreadInfo
*info
;
1923 MonoThreadInfoInterruptToken
*token
;
1925 info
= mono_thread_info_current ();
1928 token
= set_interrupt_state (info
);
1931 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1932 mono_thread_info_get_tid (info
));
1935 /* Clear the interrupted flag of the current thread, set with
1936 * mono_thread_info_self_interrupt, so it can wait again */
1938 mono_thread_info_clear_self_interrupt (void)
1940 MonoThreadInfo
*info
;
1941 MonoThreadInfoInterruptToken
*previous_token
;
1943 info
= mono_thread_info_current ();
1946 previous_token
= (MonoThreadInfoInterruptToken
*)mono_atomic_cas_ptr ((gpointer
*) &info
->interrupt_token
, NULL
, INTERRUPT_STATE
);
1947 g_assert (previous_token
== NULL
|| previous_token
== INTERRUPT_STATE
);
1949 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info
), previous_token
);
1953 mono_thread_info_is_interrupt_state (MonoThreadInfo
*info
)
1956 return mono_atomic_load_ptr ((gpointer
*) &info
->interrupt_token
) == INTERRUPT_STATE
;
1960 mono_thread_info_describe_interrupt_token (MonoThreadInfo
*info
, GString
*text
)
1964 if (!mono_atomic_load_ptr ((gpointer
*) &info
->interrupt_token
))
1965 g_string_append_printf (text
, "not waiting");
1966 else if (mono_atomic_load_ptr ((gpointer
*) &info
->interrupt_token
) == INTERRUPT_STATE
)
1967 g_string_append_printf (text
, "interrupted state");
1969 g_string_append_printf (text
, "waiting");
1973 mono_thread_info_is_current (MonoThreadInfo
*info
)
1975 return mono_thread_info_get_tid (info
) == mono_native_thread_id_get ();
1978 MonoThreadInfoWaitRet
1979 mono_thread_info_wait_one_handle (MonoThreadHandle
*thread_handle
, guint32 timeout
, gboolean alertable
)
1981 MonoOSEventWaitRet res
;
1983 res
= mono_os_event_wait_one (&thread_handle
->event
, timeout
, alertable
);
1984 if (res
== MONO_OS_EVENT_WAIT_RET_SUCCESS_0
)
1985 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
;
1986 else if (res
== MONO_OS_EVENT_WAIT_RET_ALERTED
)
1987 return MONO_THREAD_INFO_WAIT_RET_ALERTED
;
1988 else if (res
== MONO_OS_EVENT_WAIT_RET_TIMEOUT
)
1989 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT
;
1991 g_error ("%s: unknown res value %d", __func__
, res
);
1994 MonoThreadInfoWaitRet
1995 mono_thread_info_wait_multiple_handle (MonoThreadHandle
**thread_handles
, gsize nhandles
, MonoOSEvent
*background_change_event
, gboolean waitall
, guint32 timeout
, gboolean alertable
)
1997 MonoOSEventWaitRet res
;
1998 MonoOSEvent
*thread_events
[MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS
];
2001 g_assert (nhandles
<= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS
);
2002 if (background_change_event
)
2003 g_assert (nhandles
<= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS
- 1);
2005 for (i
= 0; i
< nhandles
; ++i
)
2006 thread_events
[i
] = &thread_handles
[i
]->event
;
2008 if (background_change_event
)
2009 thread_events
[nhandles
++] = background_change_event
;
2011 res
= mono_os_event_wait_multiple (thread_events
, nhandles
, waitall
, timeout
, alertable
);
2012 if (res
>= MONO_OS_EVENT_WAIT_RET_SUCCESS_0
&& res
<= MONO_OS_EVENT_WAIT_RET_SUCCESS_0
+ nhandles
- 1)
2013 return (MonoThreadInfoWaitRet
)(MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
+ (res
- MONO_OS_EVENT_WAIT_RET_SUCCESS_0
));
2014 else if (res
== MONO_OS_EVENT_WAIT_RET_ALERTED
)
2015 return MONO_THREAD_INFO_WAIT_RET_ALERTED
;
2016 else if (res
== MONO_OS_EVENT_WAIT_RET_TIMEOUT
)
2017 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT
;
2019 g_error ("%s: unknown res value %d", __func__
, res
);
2023 * mono_threads_join_mutex:
2025 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
2026 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
2027 * The code inside the lock should not block.
2030 mono_threads_join_lock (void)
2033 mono_os_mutex_lock (&join_mutex
);
2038 mono_threads_join_unlock (void)
2041 mono_os_mutex_unlock (&join_mutex
);
2047 mono_thread_info_set_tools_data (void *data
)
2049 MonoThreadInfo
*info
= mono_thread_info_current_unchecked ();
2052 if (info
->tools_data
)
2054 info
->tools_data
= data
;
2059 mono_thread_info_get_tools_data (void)
2061 MonoThreadInfo
*info
= mono_thread_info_current_unchecked ();
2063 return info
? info
->tools_data
: NULL
;