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 #pragma warning(disable:4312) // FIXME pointer cast to different size
49 Mutex that makes sure only a single thread can be suspending others.
50 Suspend is a very racy operation since it requires restarting until
51 the target thread is not on an unsafe region.
53 We could implement this using critical regions, but would be much much
54 harder for an operation that is hardly performance critical.
56 The GC has to acquire this lock before starting a STW to make sure
57 a runtime suspend won't make it wronly see a thread in a safepoint
58 when it is in fact not.
60 This has to be a naked locking primitive, and not a coop aware one, as
61 it needs to be usable when destroying thread_info_key, the TLS key for
62 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
63 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
64 to an assertion error. We then simply switch state manually in
65 mono_thread_info_suspend_lock_with_info.
67 static MonoSemType global_suspend_semaphore
;
69 static size_t thread_info_size
;
70 static MonoThreadInfoCallbacks threads_callbacks
;
71 const MonoThreadInfoRuntimeCallbacks
*mono_runtime_callbacks
;
72 static MonoNativeTlsKey thread_info_key
, thread_exited_key
;
73 #ifdef MONO_KEYWORD_THREAD
74 static MONO_KEYWORD_THREAD gint32 tls_small_id
= -1;
76 static MonoNativeTlsKey small_id_key
;
78 static MonoLinkedListSet thread_list
;
79 static gboolean mono_threads_inited
= FALSE
;
81 static MonoSemType suspend_semaphore
;
82 static size_t pending_suspends
;
84 static mono_mutex_t join_mutex
;
86 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state.state)
89 #define SLEEP_DURATION_BEFORE_WARNING (50)
91 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
93 static guint32 sleepWarnDuration
= SLEEP_DURATION_BEFORE_WARNING
,
94 sleepAbortDuration
= SLEEP_DURATION_BEFORE_ABORT
;
96 static int suspend_posts
, resume_posts
, abort_posts
, waits_done
, pending_ops
;
99 mono_threads_notify_initiator_of_abort (MonoThreadInfo
* info
)
101 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info
));
102 mono_atomic_inc_i32 (&abort_posts
);
103 mono_os_sem_post (&suspend_semaphore
);
107 mono_threads_notify_initiator_of_suspend (MonoThreadInfo
* info
)
109 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info
));
110 // check that the thread is really in a valid suspended state.
111 g_assert (mono_thread_info_get_suspend_state (info
) != NULL
);
112 mono_atomic_inc_i32 (&suspend_posts
);
113 mono_os_sem_post (&suspend_semaphore
);
117 mono_threads_notify_initiator_of_resume (MonoThreadInfo
* info
)
119 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info
));
120 mono_atomic_inc_i32 (&resume_posts
);
121 mono_os_sem_post (&suspend_semaphore
);
125 BeginSuspendFail
= 0,
126 BeginSuspendOkPreemptive
= 1,
127 BeginSuspendOkCooperative
= 2,
128 BeginSuspendOkNoWait
= 3,
129 } BeginSuspendResult
;
131 static BeginSuspendResult
132 begin_cooperative_suspend (MonoThreadInfo
*info
)
134 /* There's nothing else to do after we async request the thread to suspend */
135 mono_threads_add_to_pending_operation_set (info
);
136 return BeginSuspendOkCooperative
;
139 static BeginSuspendResult
140 begin_preemptive_suspend (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
142 if (mono_threads_suspend_begin_async_suspend (info
, interrupt_kernel
))
143 return BeginSuspendOkPreemptive
;
145 return BeginSuspendFail
;
148 static BeginSuspendResult
149 begin_suspend_for_running_thread (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
151 /* If we're using full cooperative suspend or hybrid suspend,
152 * cooperatively suspend RUNNING threads */
153 if (mono_threads_are_safepoints_enabled ())
154 return begin_cooperative_suspend (info
);
156 return begin_preemptive_suspend (info
, interrupt_kernel
);
160 thread_is_cooperative_suspend_aware (MonoThreadInfo
*info
)
162 return (mono_threads_is_cooperative_suspension_enabled () || mono_atomic_load_i32 (&(info
->coop_aware_thread
)));
165 static BeginSuspendResult
166 begin_suspend_for_blocking_thread (MonoThreadInfo
*info
, gboolean interrupt_kernel
, MonoThreadSuspendPhase phase
, gboolean coop_aware_thread
, gboolean
*did_interrupt
)
168 // if a thread can't transition to blocking, we certainly shouldn't be
169 // trying to suspend it like it's blocking.
170 g_assert (mono_threads_is_blocking_transition_enabled ());
171 // with hybrid suspend, preemptively suspend blocking threads (if thread is not coop aware),
172 // otherwise blocking already counts as suspended.
173 if (mono_threads_is_hybrid_suspension_enabled () && !coop_aware_thread
) {
175 *did_interrupt
= interrupt_kernel
;
178 case MONO_THREAD_SUSPEND_PHASE_INITIAL
:
179 /* In hybrid suspend, in the first phase, a thread in
180 * blocking can continue running (and possibly
181 * self-suspend). We'll preemptively suspend it in the
182 * second phase, if thread is not coop aware. */
183 return BeginSuspendOkNoWait
;
184 case MONO_THREAD_SUSPEND_PHASE_MOPUP
:
185 return begin_preemptive_suspend (info
, interrupt_kernel
);
187 g_assert_not_reached ();
191 *did_interrupt
= FALSE
;
192 // In full cooperative suspend, treat a thread in BLOCKING as
193 // already suspended and don't wait for it.
194 return BeginSuspendOkNoWait
;
199 check_async_suspend (MonoThreadInfo
*info
, BeginSuspendResult result
)
202 case BeginSuspendOkCooperative
:
204 case BeginSuspendOkPreemptive
:
205 return mono_threads_suspend_check_suspend_result (info
);
206 case BeginSuspendFail
:
208 case BeginSuspendOkNoWait
:
211 g_assert_not_reached ();
216 resume_async_suspended (MonoThreadInfo
*info
)
218 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
219 g_assert_not_reached ();
221 g_assert (mono_threads_suspend_begin_async_resume (info
));
225 resume_self_suspended (MonoThreadInfo
* info
)
227 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info
));
228 mono_os_sem_post (&info
->resume_semaphore
);
232 mono_thread_info_wait_for_resume (MonoThreadInfo
* info
)
235 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info
));
236 res
= mono_os_sem_wait (&info
->resume_semaphore
, MONO_SEM_FLAGS_NONE
);
237 g_assert (res
!= -1);
241 resume_blocking_suspended (MonoThreadInfo
* info
)
243 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info
));
244 mono_os_sem_post (&info
->resume_semaphore
);
248 mono_threads_add_to_pending_operation_set (MonoThreadInfo
* info
)
250 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info
));
252 mono_atomic_inc_i32 (&pending_ops
);
256 mono_threads_begin_global_suspend (void)
258 size_t ps
= pending_suspends
;
259 if (G_UNLIKELY (ps
!= 0))
260 g_error ("pending_suspends = %d, but must be 0", ps
);
261 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
,
262 abort_posts
, waits_done
, pending_ops
);
263 g_assert ((suspend_posts
+ resume_posts
+ abort_posts
) == waits_done
);
264 mono_threads_coop_begin_global_suspend ();
268 mono_threads_end_global_suspend (void)
270 size_t ps
= pending_suspends
;
271 if (G_UNLIKELY (ps
!= 0))
272 g_error ("pending_suspends = %d, but must be 0", ps
);
273 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts
, resume_posts
,
274 abort_posts
, waits_done
, pending_ops
);
275 g_assert ((suspend_posts
+ resume_posts
+ abort_posts
) == waits_done
);
276 mono_threads_coop_end_global_suspend ();
282 MonoThreadInfo
*cur
= mono_thread_info_current ();
284 g_async_safe_printf ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
285 g_async_safe_printf ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
286 g_async_safe_printf ("\t0x1\t- detached (GOOD, unless the thread is running managed code)\n");
287 g_async_safe_printf ("\t0x2\t- running (BAD, unless it's the gc thread)\n");
288 g_async_safe_printf ("\t0x?03\t- async suspended (GOOD)\n");
289 g_async_safe_printf ("\t0x?04\t- self suspended (GOOD)\n");
290 g_async_safe_printf ("\t0x?05\t- async suspend requested (BAD)\n");
291 g_async_safe_printf ("\t0x6\t- blocking (BAD, unless there's no suspend initiator)\n");
292 g_async_safe_printf ("\t0x?07\t- blocking async suspended (GOOD)\n");
293 g_async_safe_printf ("\t0x?08\t- blocking self suspended (GOOD)\n");
294 g_async_safe_printf ("\t0x?09\t- blocking suspend requested (BAD in coop; GOOD in hybrid)\n");
296 FOREACH_THREAD_SAFE_ALL (info
) {
298 char thread_name
[256] = { 0 };
299 pthread_getname_np (mono_thread_info_get_tid (info
), thread_name
, 255);
301 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" : "" );
303 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" : "" );
305 } FOREACH_THREAD_SAFE_END
309 mono_threads_wait_pending_operations (void)
312 int c
= pending_suspends
;
314 /* Wait threads to park */
315 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c
);
316 if (pending_suspends
) {
317 MonoStopwatch suspension_time
;
318 mono_stopwatch_start (&suspension_time
);
319 for (i
= 0; i
< pending_suspends
; ++i
) {
320 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
321 mono_atomic_inc_i32 (&waits_done
);
322 if (mono_os_sem_timedwait (&suspend_semaphore
, sleepAbortDuration
, MONO_SEM_FLAGS_NONE
) == MONO_SEM_TIMEDWAIT_RET_SUCCESS
)
324 mono_stopwatch_stop (&suspension_time
);
328 g_async_safe_printf ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends
, i
);
329 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time
), sleepAbortDuration
);
331 mono_stopwatch_stop (&suspension_time
);
332 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends
, (int)mono_stopwatch_elapsed_ms (&suspension_time
));
336 pending_suspends
= 0;
342 //Thread initialization code
345 mono_hazard_pointer_clear_all (MonoThreadHazardPointers
*hp
, int retain
)
348 mono_hazard_pointer_clear (hp
, 0);
350 mono_hazard_pointer_clear (hp
, 1);
352 mono_hazard_pointer_clear (hp
, 2);
356 If return non null Hazard Pointer 1 holds the return value.
359 mono_thread_info_lookup (MonoNativeThreadId id
)
361 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
363 if (!mono_lls_find (&thread_list
, hp
, (uintptr_t)id
)) {
364 mono_hazard_pointer_clear_all (hp
, -1);
368 mono_hazard_pointer_clear_all (hp
, 1);
369 return (MonoThreadInfo
*) mono_hazard_pointer_get_val (hp
, 1);
373 mono_thread_info_insert (MonoThreadInfo
*info
)
375 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
377 if (!mono_lls_insert (&thread_list
, hp
, (MonoLinkedListSetNode
*)info
)) {
378 mono_hazard_pointer_clear_all (hp
, -1);
382 mono_hazard_pointer_clear_all (hp
, -1);
387 mono_thread_info_remove (MonoThreadInfo
*info
)
389 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
392 THREADS_DEBUG ("removing info %p\n", info
);
393 res
= mono_lls_remove (&thread_list
, hp
, (MonoLinkedListSetNode
*)info
);
394 mono_hazard_pointer_clear_all (hp
, -1);
399 free_thread_info (gpointer mem
)
401 MonoThreadInfo
*info
= (MonoThreadInfo
*) mem
;
403 mono_os_sem_destroy (&info
->resume_semaphore
);
404 mono_threads_suspend_free (info
);
410 * mono_thread_info_register_small_id
412 * Registers a small ID for the current thread. This is a 16-bit value uniquely
413 * identifying the current thread. If the current thread already has a small ID
414 * assigned, that small ID will be returned; otherwise, the newly assigned small
418 mono_thread_info_register_small_id (void)
420 int small_id
= mono_thread_info_get_small_id ();
425 small_id
= mono_thread_small_id_alloc ();
426 #ifdef MONO_KEYWORD_THREAD
427 tls_small_id
= small_id
;
429 mono_native_tls_set_value (small_id_key
, GUINT_TO_POINTER (small_id
+ 1));
435 thread_handle_destroy (gpointer data
)
437 MonoThreadHandle
*thread_handle
;
439 thread_handle
= (MonoThreadHandle
*) data
;
441 mono_os_event_destroy (&thread_handle
->event
);
442 g_free (thread_handle
);
446 register_thread (MonoThreadInfo
*info
)
449 guint8
*staddr
= NULL
;
452 info
->small_id
= mono_thread_info_register_small_id ();
453 mono_thread_info_set_tid (info
, mono_native_thread_id_get ());
455 info
->handle
= g_new0 (MonoThreadHandle
, 1);
456 mono_refcount_init (info
->handle
, thread_handle_destroy
);
457 mono_os_event_init (&info
->handle
->event
, FALSE
);
459 mono_os_sem_init (&info
->resume_semaphore
, 0);
461 /*set TLS early so SMR works */
462 mono_native_tls_set_value (thread_info_key
, info
);
464 mono_thread_info_get_stack_bounds (&staddr
, &stsize
);
467 info
->stack_start_limit
= staddr
;
468 info
->stack_end
= staddr
+ stsize
;
469 info
->stackdata
= g_byte_array_new ();
471 info
->internal_thread_gchandle
= G_MAXUINT32
;
473 info
->profiler_signal_ack
= 1;
475 #ifdef USE_WINDOWS_BACKEND
476 info
->windows_tib
= (PNT_TIB
)NtCurrentTeb ();
477 info
->win32_apc_info
= 0;
478 info
->win32_apc_info_io_handle
= INVALID_HANDLE_VALUE
;
481 mono_threads_suspend_register (info
);
483 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info
, mono_thread_info_get_tid (info
), info
->small_id
);
485 if (threads_callbacks
.thread_attach
) {
486 if (!threads_callbacks
.thread_attach (info
)) {
487 // g_warning ("thread registation failed\n");
488 mono_native_tls_set_value (thread_info_key
, NULL
);
494 Transition it before taking any locks or publishing itself to reduce the chance
495 of others witnessing a detached thread.
496 We can reasonably expect that until this thread gets published, no other thread will
497 try to manipulate it.
499 mono_threads_transition_attach (info
);
500 mono_thread_info_suspend_lock ();
501 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
502 result
= mono_thread_info_insert (info
);
504 mono_thread_info_suspend_unlock ();
510 mono_thread_info_suspend_lock_with_info (MonoThreadInfo
*info
);
513 mono_threads_signal_thread_handle (MonoThreadHandle
* thread_handle
);
516 unregister_thread (void *arg
)
518 MONO_STACKDATA (gc_unsafe_stackdata
);
519 MonoThreadInfo
*info
;
522 MonoThreadHandle
* handle
;
524 info
= (MonoThreadInfo
*) arg
;
525 g_assertf (info
, ""); // f includes __func__
526 g_assert (mono_thread_info_is_current (info
));
527 g_assert (mono_thread_info_is_live (info
));
529 /* We only enter the GC unsafe region, as when exiting this function, the thread
530 * will be detached, and the current MonoThreadInfo* will be destroyed. */
531 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info
, &gc_unsafe_stackdata
);
533 /* Need to be in GC Unsafe to pump the HP queue - some of the cleanup
534 * methods need to use coop-aware locks. For example: jit_info_table_free_duplicate.
537 /* Pump the HP queue while the thread is alive.*/
538 mono_thread_hazardous_try_free_some ();
540 small_id
= info
->small_id
;
542 THREADS_DEBUG ("unregistering info %p\n", info
);
544 mono_native_tls_set_value (thread_exited_key
, GUINT_TO_POINTER (1));
547 * TLS destruction order is not reliable so small_id might be cleaned up
550 #ifndef MONO_KEYWORD_THREAD
551 mono_native_tls_set_value (small_id_key
, GUINT_TO_POINTER (info
->small_id
+ 1));
554 /* we need to duplicate it, as the info->handle is going
555 * to be closed when unregistering from the platform */
556 handle
= mono_threads_open_thread_handle (info
->handle
);
559 First perform the callback that requires no locks.
560 This callback has the potential of taking other locks, so we do it before.
561 After it completes, the thread remains functional.
563 if (threads_callbacks
.thread_detach
)
564 threads_callbacks
.thread_detach (info
);
566 mono_thread_info_suspend_lock_with_info (info
);
569 Now perform the callback that must be done under locks.
570 This will render the thread useless and non-suspendable, so it must
571 be done while holding the suspend lock to give no other thread chance
574 if (threads_callbacks
.thread_detach_with_lock
)
575 threads_callbacks
.thread_detach_with_lock (info
);
577 /* The thread is no longer active, so unref its handle */
578 mono_threads_close_thread_handle (info
->handle
);
581 result
= mono_thread_info_remove (info
);
583 mono_threads_transition_detach (info
);
585 mono_thread_info_suspend_unlock ();
587 g_byte_array_free (info
->stackdata
, /*free_segment=*/TRUE
);
589 /*now it's safe to free the thread info.*/
590 mono_thread_hazardous_try_free (info
, free_thread_info
);
592 mono_thread_small_id_free (small_id
);
593 // clear the small_id thread local, in case this thread so that if it is reattached while running other TLS key dtors it will get a new small id
594 #ifdef MONO_KEYWORD_THREAD
597 mono_native_tls_set_value (small_id_key
, NULL
);
600 mono_threads_signal_thread_handle (handle
);
602 mono_threads_close_thread_handle (handle
);
604 mono_native_tls_set_value (thread_info_key
, NULL
);
608 thread_exited_dtor (void *arg
)
610 #if defined(__MACH__)
612 * Since we use pthread dtors to clean up thread data, if a thread
613 * is attached to the runtime by another pthread dtor after our dtor
614 * has ran, it will never be detached, leading to various problems
615 * since the thread ids etc. will be reused while they are still in
616 * the threads hashtables etc.
617 * Dtors are called in a loop until all user tls entries are 0,
618 * but the loop has a maximum count (4), so if we set the tls
619 * variable every time, it will remain set when system tls dtors
620 * are ran. This allows mono_thread_info_is_exiting () to detect
621 * whenever the thread is exiting, even if it is executed from a
622 * system tls dtor (i.e. obj-c dealloc methods).
624 mono_native_tls_set_value (thread_exited_key
, GUINT_TO_POINTER (1));
629 mono_thread_info_current_unchecked (void)
631 return mono_threads_inited
? (MonoThreadInfo
*)mono_native_tls_get_value (thread_info_key
) : NULL
;
636 mono_thread_info_current (void)
638 MonoThreadInfo
*info
= (MonoThreadInfo
*)mono_native_tls_get_value (thread_info_key
);
642 info
= mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
645 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
646 The way to distinguish between before, during and after cleanup is the following:
648 -If the TLS key is set, cleanup has not begun;
649 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
650 -If the thread is nowhere to be found, cleanup has finished.
652 We cannot function after cleanup since there's no way to ensure what will happen.
654 g_assertf (info
, ""); // f includes __func__
656 /*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 */
657 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
663 * mono_thread_info_get_small_id
665 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
666 * identifying the current thread. Returns -1 if the current thread doesn't have
667 * a small ID assigned.
669 * To ensure that the calling thread has a small ID assigned, call either
670 * mono_thread_info_attach or mono_thread_info_register_small_id.
673 mono_thread_info_get_small_id (void)
675 #ifdef MONO_KEYWORD_THREAD
678 gpointer val
= mono_native_tls_get_value (small_id_key
);
681 return GPOINTER_TO_INT (val
) - 1;
686 mono_thread_info_list_head (void)
692 mono_thread_info_attach (void)
694 MonoThreadInfo
*info
;
697 if (!mono_threads_inited
)
699 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
700 * thread is created before an embedding API user initialized Mono. */
701 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
706 g_assert (mono_threads_inited
);
708 info
= (MonoThreadInfo
*) mono_native_tls_get_value (thread_info_key
);
710 info
= (MonoThreadInfo
*) g_malloc0 (thread_info_size
);
711 THREADS_DEBUG ("attaching %p\n", info
);
712 if (!register_thread (info
)) {
722 mono_thread_info_detach (void)
724 MonoThreadInfo
*info
;
727 if (!mono_threads_inited
)
729 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
730 * is created before an embedding API user initialized Mono. */
731 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
736 g_assert (mono_threads_inited
);
738 info
= (MonoThreadInfo
*) mono_native_tls_get_value (thread_info_key
);
740 THREADS_DEBUG ("detaching %p\n", info
);
741 unregister_thread (info
);
746 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo
*info
, guint32
*gchandle
)
748 g_assertf (info
, ""); // f includes __func__
749 g_assert (mono_thread_info_is_current (info
));
751 if (info
->internal_thread_gchandle
== G_MAXUINT32
)
754 *gchandle
= info
->internal_thread_gchandle
;
759 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo
*info
, guint32 gchandle
)
761 g_assertf (info
, ""); // f includes __func__
762 g_assert (mono_thread_info_is_current (info
));
763 g_assert (gchandle
!= G_MAXUINT32
);
764 info
->internal_thread_gchandle
= gchandle
;
768 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE
*info
)
770 g_assertf (info
, ""); // f includes __func__
771 g_assert (mono_thread_info_is_current (info
));
772 info
->internal_thread_gchandle
= G_MAXUINT32
;
776 * mono_thread_info_is_exiting:
778 * Return whenever the current thread is exiting, i.e. it is running pthread
782 mono_thread_info_is_exiting (void)
784 #if defined(__MACH__)
785 if (mono_native_tls_get_value (thread_exited_key
) == GUINT_TO_POINTER (1))
793 thread_info_key_dtor (void *arg
)
795 /* Put the MonoThreadInfo back for the duration of the
796 * unregister code. In some circumstances the thread needs to
797 * take the GC lock which may block which requires a coop
798 * state transition. */
799 mono_native_tls_set_value (thread_info_key
, arg
);
800 unregister_thread (arg
);
801 mono_native_tls_set_value (thread_info_key
, NULL
);
806 mono_thread_info_get_flags (MonoThreadInfo
*info
)
808 return (MonoThreadInfoFlags
)mono_atomic_load_i32 (&info
->flags
);
812 mono_thread_info_set_flags (MonoThreadInfoFlags flags
)
814 MonoThreadInfo
*info
= mono_thread_info_current ();
815 MonoThreadInfoFlags old
= (MonoThreadInfoFlags
)mono_atomic_load_i32 (&info
->flags
);
817 if (threads_callbacks
.thread_flags_changing
)
818 threads_callbacks
.thread_flags_changing (old
, flags
);
820 mono_atomic_store_i32 (&info
->flags
, flags
);
822 if (threads_callbacks
.thread_flags_changed
)
823 threads_callbacks
.thread_flags_changed (old
, flags
);
826 #define MONO_END_INIT_CB GINT_TO_POINTER(-1)
827 static GSList
*init_callbacks
;
830 mono_thread_info_wait_inited (void)
833 mono_os_sem_init (&cb
, 0);
834 GSList
*old
= init_callbacks
;
837 wait_request
.data
= &cb
;
838 wait_request
.next
= old
;
840 while (mono_threads_inited
!= TRUE
) {
841 GSList
*old_read
= (GSList
*)mono_atomic_cas_ptr ((gpointer
*) &init_callbacks
, &wait_request
, old
);
843 // Queued up waiter, need to be unstuck
844 if (old_read
== old
) {
846 } else if (old_read
== GINT_TO_POINTER (MONO_END_INIT_CB
)) {
850 // We raced with another writer
851 wait_request
.next
= (GSList
*) old_read
;
856 while (mono_threads_inited
!= TRUE
) {
857 gboolean timedout
= mono_os_sem_timedwait (&cb
, 1000, MONO_SEM_FLAGS_NONE
) == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
;
862 g_assert (mono_threads_inited
);
867 mono_thread_info_set_inited (void)
869 mono_threads_inited
= TRUE
;
870 mono_memory_barrier ();
872 GSList
*old
= init_callbacks
;
875 GSList
* old_read
= (GSList
*)mono_atomic_cas_ptr ((gpointer
*) &init_callbacks
, MONO_END_INIT_CB
, (gpointer
) old
);
881 if (old
== MONO_END_INIT_CB
) {
882 // Try not to use g_error / g_warning because this machinery used by logging
883 // Don't want to loop back into it.
884 fprintf (stderr
, "Global threads inited twice");
889 while (old
!= NULL
) {
890 GSList
*curr
= (GSList
*) old
;
891 GSList
*next
= old
->next
;
893 mono_os_sem_post ((MonoSemType
*)curr
->data
);
901 mono_thread_info_cleanup ()
903 mono_native_tls_free (thread_info_key
);
904 mono_native_tls_free (thread_exited_key
);
908 mono_thread_info_init (size_t info_size
)
911 thread_info_size
= info_size
;
914 mono_threads_suspend_policy_init ();
917 res
= mono_native_tls_alloc (&thread_info_key
, NULL
);
918 res
= mono_native_tls_alloc (&thread_exited_key
, NULL
);
920 res
= mono_native_tls_alloc (&thread_info_key
, (void *) thread_info_key_dtor
);
921 res
= mono_native_tls_alloc (&thread_exited_key
, (void *) thread_exited_dtor
);
926 #ifndef MONO_KEYWORD_THREAD
927 res
= mono_native_tls_alloc (&small_id_key
, NULL
);
931 if ((sleepLimit
= g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL
) {
933 long threshold
= strtol(sleepLimit
, NULL
, 10);
934 if ((errno
== 0) && (threshold
>= 40)) {
935 sleepAbortDuration
= threshold
;
936 sleepWarnDuration
= threshold
/ 20;
938 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
942 mono_os_sem_init (&global_suspend_semaphore
, 1);
943 mono_os_sem_init (&suspend_semaphore
, 0);
944 mono_os_mutex_init (&join_mutex
);
946 mono_lls_init (&thread_list
, NULL
);
947 mono_thread_smr_init ();
948 mono_threads_suspend_init ();
949 mono_threads_coop_init ();
950 mono_threads_platform_init ();
952 mono_thread_info_set_inited ();
954 g_assert (sizeof (MonoNativeThreadId
) <= sizeof (uintptr_t));
958 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks
*callbacks
)
960 threads_callbacks
= *callbacks
;
964 mono_thread_info_signals_init (void)
966 mono_threads_suspend_init_signals ();
970 mono_thread_info_runtime_init (const MonoThreadInfoRuntimeCallbacks
*callbacks
)
972 mono_runtime_callbacks
= callbacks
;
976 mono_thread_info_core_resume (MonoThreadInfo
*info
)
978 gboolean res
= FALSE
;
980 switch (mono_threads_transition_request_resume (info
)) {
987 case ResumeInitSelfResume
:
988 resume_self_suspended (info
);
991 case ResumeInitAsyncResume
:
992 resume_async_suspended (info
);
995 case ResumeInitBlockingResume
:
996 resume_blocking_suspended (info
);
1005 * Current thread must hold the global_suspend_semaphore.
1006 * The given MonoThreadInfo* is a suspended thread.
1007 * Must be using hybrid suspend.
1010 mono_thread_info_core_pulse (MonoThreadInfo
*info
)
1012 gboolean res
= FALSE
;
1014 switch (mono_threads_transition_request_pulse (info
)) {
1015 case PulseInitAsyncPulse
:
1016 resume_async_suspended (info
);
1024 mono_thread_info_resume (MonoNativeThreadId tid
)
1026 gboolean result
; /* don't initialize it so the compiler can catch unitilized paths. */
1027 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1028 MonoThreadInfo
*info
;
1030 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid
);
1032 mono_thread_info_suspend_lock ();
1034 info
= mono_thread_info_lookup (tid
); /*info on HP1*/
1040 result
= mono_thread_info_core_resume (info
);
1042 //Wait for the pending resume to finish
1043 mono_threads_wait_pending_operations ();
1046 mono_thread_info_suspend_unlock ();
1047 mono_hazard_pointer_clear (hp
, 1);
1051 static MonoThreadBeginSuspendResult
1052 begin_suspend_request_suspension_cordially (MonoThreadInfo
*info
);
1054 static MonoThreadBeginSuspendResult
1055 begin_suspend_peek_and_preempt (MonoThreadInfo
*info
);
1058 MonoThreadBeginSuspendResult
1059 mono_thread_info_begin_suspend (MonoThreadInfo
*info
, MonoThreadSuspendPhase phase
)
1061 if (phase
== MONO_THREAD_SUSPEND_PHASE_MOPUP
&& mono_threads_is_hybrid_suspension_enabled ())
1062 return begin_suspend_peek_and_preempt (info
);
1064 return begin_suspend_request_suspension_cordially (info
);
1067 MonoThreadBeginSuspendResult
1068 begin_suspend_request_suspension_cordially (MonoThreadInfo
*info
)
1070 gboolean coop_aware_thread
= FALSE
;
1072 /* Ask the thread nicely to suspend. In hybrid suspend, blocking
1073 * threads are transitioned to blocking_suspend_requested, but not
1074 * preemptively suspend in the current phase.
1076 switch (mono_threads_transition_request_suspension (info
)) {
1077 case ReqSuspendAlreadySuspended
:
1078 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1079 case ReqSuspendAlreadySuspendedBlocking
:
1080 if (mono_threads_is_hybrid_suspension_enabled ()) {
1081 /* This should only happen in the second phase of
1082 * hybrid suspend. It means that the first phase asked
1083 * the thread to suspend but did not signal it to
1084 * suspend preemptively. */
1085 g_assert_not_reached ();
1087 // This state should not be possible if we're using preemptive
1088 // suspend on a blocking thread - there can only be a single
1089 // suspend initiator at a time (guarded by
1090 // mono_thread_info_suspend_lock), and we expect the victim
1091 // thread to finish the two-phase preemptive suspension
1092 // procedure (and reach the ReqSuspendAlreadySuspended stage)
1093 // before the next suspend initiator can begin.
1094 g_assert (mono_threads_is_blocking_transition_enabled ());
1096 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1098 case ReqSuspendInitSuspendBlocking
:
1099 // in full cooperative mode just leave BLOCKING
1100 // threads running until they try to return to RUNNING, so
1101 // nothing to do, in hybrid coop preempt the thread. If thread is coop aware,
1102 // handle its as normal cooperative mode.
1103 if (mono_threads_is_blocking_transition_enabled ())
1104 coop_aware_thread
= thread_is_cooperative_suspend_aware (info
);
1106 switch (begin_suspend_for_blocking_thread (info
, FALSE
, MONO_THREAD_SUSPEND_PHASE_INITIAL
, coop_aware_thread
, NULL
)) {
1107 case BeginSuspendFail
:
1108 return MONO_THREAD_BEGIN_SUSPEND_SKIP
;
1109 case BeginSuspendOkNoWait
:
1110 if (mono_threads_is_hybrid_suspension_enabled () && !coop_aware_thread
)
1111 return MONO_THREAD_BEGIN_SUSPEND_NEXT_PHASE
;
1113 g_assert (thread_is_cooperative_suspend_aware (info
));
1114 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1116 case BeginSuspendOkPreemptive
:
1117 case BeginSuspendOkCooperative
:
1118 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1120 g_assert_not_reached ();
1122 case ReqSuspendInitSuspendRunning
:
1123 // in full preemptive mode this should be a preemptive suspend
1124 // in full and hybrid cooperative modes this should be a coop suspend
1125 if (begin_suspend_for_running_thread (info
, FALSE
) != BeginSuspendFail
)
1126 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1128 return MONO_THREAD_BEGIN_SUSPEND_SKIP
;
1130 g_assert_not_reached ();
1134 MonoThreadBeginSuspendResult
1135 begin_suspend_peek_and_preempt (MonoThreadInfo
*info
)
1137 /* This only makes sense for two-phase hybrid suspension:
1138 * requires that a suspension request transition was already performed for 'info',
1139 * and if it is still in blocking_suspend_requested, preemptively suspends it.
1141 g_assert (mono_threads_is_hybrid_suspension_enabled ());
1142 if (mono_threads_transition_peek_blocking_suspend_requested (info
)) {
1143 // in full cooperative mode just leave BLOCKING
1144 // threads running until they try to return to RUNNING, so
1145 // nothing to do, in hybrid coop preempt the thread.
1146 switch (begin_suspend_for_blocking_thread (info
, FALSE
, MONO_THREAD_SUSPEND_PHASE_MOPUP
, FALSE
, NULL
)) {
1147 case BeginSuspendFail
:
1148 return MONO_THREAD_BEGIN_SUSPEND_SKIP
;
1149 case BeginSuspendOkNoWait
:
1150 case BeginSuspendOkCooperative
:
1151 /* can't happen - should've suspended in the previous phase */
1152 g_assert_not_reached ();
1153 case BeginSuspendOkPreemptive
:
1154 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1156 g_assert_not_reached ();
1159 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED
;
1163 mono_thread_info_begin_resume (MonoThreadInfo
*info
)
1165 return mono_thread_info_core_resume (info
);
1169 mono_thread_info_begin_pulse_resume_and_request_suspension (MonoThreadInfo
*info
)
1171 /* For two-phase suspend, we want to atomically resume the thread and
1172 * request that it try to cooperatively suspend again. Specifically,
1173 * we really don't want it to transition from GC Safe to GC Unsafe
1174 * because we then it could (in GC Unsafe) try to take a lock that's
1175 * held by another preemptively-suspended thread, essentially
1176 * recreating the same problem that two-phase suspend intends to
1178 if (mono_threads_is_multiphase_stw_enabled ())
1179 return mono_thread_info_core_pulse (info
);
1181 return mono_thread_info_core_resume (info
);
1184 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
1185 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
1188 is_thread_in_critical_region (MonoThreadInfo
*info
)
1190 gpointer stack_start
;
1191 MonoThreadUnwindState
*state
;
1193 if (mono_threads_platform_in_critical_region (info
))
1196 /* Are we inside a system critical region? */
1197 if (info
->inside_critical_region
)
1200 /* Are we inside a GC critical region? */
1201 if (threads_callbacks
.thread_in_critical_region
&& threads_callbacks
.thread_in_critical_region (info
)) {
1205 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
1206 state
= mono_thread_info_get_suspend_state (info
);
1207 if (!state
->unwind_data
[MONO_UNWIND_DATA_DOMAIN
])
1210 stack_start
= MONO_CONTEXT_GET_SP (&state
->ctx
);
1211 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
1212 if (stack_start
< info
->stack_start_limit
|| stack_start
>= info
->stack_end
)
1215 if (threads_callbacks
.ip_in_critical_region
)
1216 return threads_callbacks
.ip_in_critical_region ((MonoDomain
*) state
->unwind_data
[MONO_UNWIND_DATA_DOMAIN
], (char *) MONO_CONTEXT_GET_IP (&state
->ctx
));
1222 mono_thread_info_in_critical_location (MonoThreadInfo
*info
)
1224 return is_thread_in_critical_region (info
);
1228 The return value is only valid until a matching mono_thread_info_resume is called
1230 static MonoThreadInfo
*
1231 suspend_sync (MonoNativeThreadId tid
, gboolean interrupt_kernel
)
1233 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1234 MonoThreadInfo
*info
= mono_thread_info_lookup (tid
); /*info on HP1*/
1238 BeginSuspendResult suspend_result
= BeginSuspendFail
;
1239 switch (mono_threads_transition_request_suspension (info
)) {
1240 case ReqSuspendAlreadySuspended
:
1241 mono_hazard_pointer_clear (hp
, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
1243 case ReqSuspendInitSuspendRunning
:
1244 suspend_result
= begin_suspend_for_running_thread (info
, interrupt_kernel
);
1245 if (suspend_result
== BeginSuspendFail
) {
1246 mono_hazard_pointer_clear (hp
, 1);
1249 //Wait for the pending suspend to finish
1250 g_assert (suspend_result
!= BeginSuspendOkNoWait
);
1251 mono_threads_wait_pending_operations ();
1253 if (!check_async_suspend (info
, suspend_result
)) {
1254 mono_thread_info_core_resume (info
);
1255 mono_threads_wait_pending_operations ();
1256 mono_hazard_pointer_clear (hp
, 1);
1260 case ReqSuspendAlreadySuspendedBlocking
:
1261 // ReqSuspendAlreadySuspendedBlocking should not be possible if
1262 // we're using preemptive suspend on a blocking thread - a
1263 // suspend initiator holds the mono_thread_info_suspend_lock
1264 // (there is a single suspend initiator at a time), and we
1265 // expect the victim thread to finish the two-phase preemptive
1266 // suspension procedure (and reach the
1267 // ReqSuspendAlreadySuspended stage) before the next suspend
1268 // initiator can begin.
1269 g_assert (mono_threads_is_blocking_transition_enabled () && !mono_threads_is_hybrid_suspension_enabled ());
1271 // if we tried to preempt the thread already, do nothing.
1272 // otherwise (if it's running in blocking mode) try to abort the syscall.
1273 if (interrupt_kernel
)
1274 mono_threads_suspend_abort_syscall (info
);
1277 case ReqSuspendInitSuspendBlocking
: {
1278 gboolean did_interrupt
= FALSE
;
1279 suspend_result
= begin_suspend_for_blocking_thread (info
, interrupt_kernel
, MONO_THREAD_SUSPEND_PHASE_MOPUP
, FALSE
, &did_interrupt
);
1280 if (suspend_result
== BeginSuspendFail
) {
1281 mono_hazard_pointer_clear (hp
, 1);
1285 if (suspend_result
!= BeginSuspendOkNoWait
)
1286 mono_threads_wait_pending_operations ();
1288 if (!check_async_suspend (info
, suspend_result
)) {
1289 mono_thread_info_core_resume (info
);
1290 mono_threads_wait_pending_operations ();
1291 mono_hazard_pointer_clear (hp
, 1);
1295 // if we tried to preempt the thread already, do nothing.
1296 // otherwise (if it's running in blocking mode) try to abort the syscall.
1297 if (interrupt_kernel
&& !did_interrupt
)
1298 mono_threads_suspend_abort_syscall (info
);
1303 g_assert_not_reached ();
1305 g_assert_not_reached ();
1308 static MonoThreadInfo
*
1309 suspend_sync_nolock (MonoNativeThreadId id
, gboolean interrupt_kernel
)
1311 MonoThreadInfo
*info
= NULL
;
1312 int sleep_duration
= 0;
1314 if (!(info
= suspend_sync (id
, interrupt_kernel
))) {
1315 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1319 /*WARNING: We now are in interrupt context until we resume the thread. */
1320 if (!is_thread_in_critical_region (info
))
1323 if (!mono_thread_info_core_resume (info
)) {
1324 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1327 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id
);
1329 /* Wait for the pending resume to finish */
1330 mono_threads_wait_pending_operations ();
1332 if (sleep_duration
== 0)
1333 mono_thread_info_yield ();
1335 g_usleep (sleep_duration
);
1337 sleep_duration
+= 10;
1343 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id
, gboolean interrupt_kernel
, MonoSuspendThreadCallback callback
, gpointer user_data
)
1346 MonoThreadInfo
*info
= NULL
;
1347 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1349 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s)\n", (void*)id
, interrupt_kernel
? "int" : "");
1350 /*FIXME: unify this with self-suspend*/
1351 g_assert (id
!= mono_native_thread_id_get ());
1353 /* This can block during stw */
1354 mono_thread_info_suspend_lock ();
1355 mono_threads_begin_global_suspend ();
1357 info
= suspend_sync_nolock (id
, interrupt_kernel
);
1358 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s): info %p\n", (void*)id
, interrupt_kernel
? "int" : "", info
);
1362 switch (result
= callback (info
, user_data
)) {
1363 case MonoResumeThread
:
1364 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): MonoResumeThread\n", (void*)id
, interrupt_kernel
? "int" : "");
1365 mono_hazard_pointer_set (hp
, 1, info
);
1366 mono_thread_info_core_resume (info
);
1367 mono_threads_wait_pending_operations ();
1368 #ifdef USE_WINDOWS_BACKEND
1369 // If we interrupt kernel but have blocking sync IO requests preventing the thread from running APC's
1370 // try to abort all sync blocking IO request. This must be done after thread has been resumed, but before releasing
1371 // global suspend lock (preventing other threads from supsending the thread).
1372 if (interrupt_kernel
)
1373 mono_win32_abort_blocking_io_call (info
);
1377 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): KeepSuspended\n", (void*)id
, interrupt_kernel
? "int" : "");
1378 g_assert (!mono_threads_are_safepoints_enabled ());
1381 g_error ("Invalid suspend_and_run callback return value %d", result
);
1385 mono_hazard_pointer_clear (hp
, 1);
1386 mono_threads_end_global_suspend ();
1387 mono_thread_info_suspend_unlock ();
1391 Inject an assynchronous call into the target thread. The target thread must be suspended and
1392 only a single async call can be setup for a given suspend cycle.
1393 This async call must cause stack unwinding as the current implementation doesn't save enough state
1394 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1395 currently used only to deliver exceptions.
1398 mono_thread_info_setup_async_call (MonoThreadInfo
*info
, void (*target_func
)(void*), void *user_data
)
1400 if (!mono_threads_are_safepoints_enabled ()) {
1401 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1402 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1403 * region or entering a gc unsafe region */
1404 g_assert (mono_thread_info_run_state (info
) == STATE_ASYNC_SUSPENDED
);
1406 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1407 g_assert (!info
->async_target
);
1408 info
->async_target
= target_func
;
1409 /* This is not GC tracked */
1410 info
->user_data
= user_data
;
1414 The suspend lock is held during any suspend in progress.
1415 A GC that has safepoints must take this lock as part of its
1416 STW to make sure no unsafe pending suspend is in progress.
1420 mono_thread_info_suspend_lock_with_info (MonoThreadInfo
*info
)
1422 g_assertf (info
, ""); // f includes __func__
1423 g_assert (mono_thread_info_is_current (info
));
1424 g_assert (mono_thread_info_is_live (info
));
1426 MONO_ENTER_GC_SAFE_WITH_INFO(info
);
1428 int res
= mono_os_sem_wait (&global_suspend_semaphore
, MONO_SEM_FLAGS_NONE
);
1429 g_assert (res
!= -1);
1431 MONO_EXIT_GC_SAFE_WITH_INFO
;
1435 mono_thread_info_suspend_lock (void)
1437 MonoThreadInfo
*info
;
1440 info
= mono_thread_info_current_unchecked ();
1441 if (info
&& mono_thread_info_is_live (info
)) {
1442 mono_thread_info_suspend_lock_with_info (info
);
1446 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1447 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1449 res
= mono_os_sem_wait (&global_suspend_semaphore
, MONO_SEM_FLAGS_NONE
);
1450 g_assert (res
!= -1);
1454 mono_thread_info_suspend_unlock (void)
1456 mono_os_sem_post (&global_suspend_semaphore
);
1459 /* Return the suspend state for the current thread. Note: the thread must be
1460 * already suspended in order for this function to be callable.
1462 MonoThreadUnwindState
*
1463 mono_thread_info_get_suspend_state (MonoThreadInfo
*info
)
1465 int cur_state
= mono_thread_info_current_state (info
);
1466 switch (cur_state
) {
1467 case STATE_ASYNC_SUSPENDED
:
1468 case STATE_BLOCKING_ASYNC_SUSPENDED
:
1469 return &info
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
];
1470 case STATE_SELF_SUSPENDED
:
1471 case STATE_BLOCKING_SELF_SUSPENDED
:
1472 return &info
->thread_saved_state
[SELF_SUSPEND_STATE_INDEX
];
1473 case STATE_BLOCKING_SUSPEND_REQUESTED
:
1474 // This state is only valid for full cooperative suspend or cooparative suspend
1475 // aware threads. If we're preemptively suspending blocking threads,
1476 // this is not a valid suspend state.
1477 if ((mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ()) || thread_is_cooperative_suspend_aware (info
))
1478 return &info
->thread_saved_state
[SELF_SUSPEND_STATE_INDEX
];
1485 STATE_ASYNC_SUSPEND_REQUESTED
1486 STATE_BLOCKING: All those are invalid suspend states.
1487 STATE_BLOCKING_SUSPEND_REQUESTED: Invalid if we're preemptively suspending blocking threads.
1489 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
));
1493 * This is a very specific function whose only purpose is to
1494 * break a given thread from socket syscalls.
1496 * This only exists because linux won't fail a call to connect
1497 * if the underlying is closed.
1499 * TODO We should cleanup and unify this with the other syscall abort
1503 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid
)
1505 MonoThreadHazardPointers
*hp
;
1506 MonoThreadInfo
*info
;
1508 if (tid
== mono_native_thread_id_get ())
1511 mono_thread_info_suspend_lock ();
1512 hp
= mono_hazard_pointer_get ();
1513 info
= mono_thread_info_lookup (tid
);
1515 mono_thread_info_suspend_unlock ();
1518 mono_threads_begin_global_suspend ();
1520 mono_threads_suspend_abort_syscall (info
);
1521 mono_threads_wait_pending_operations ();
1523 mono_hazard_pointer_clear (hp
, 1);
1525 mono_threads_end_global_suspend ();
1526 mono_thread_info_suspend_unlock ();
1530 * mono_thread_info_set_is_async_context:
1532 * Set whenever the current thread is in an async context. Some runtime functions might behave
1533 * differently while in an async context in order to be async safe.
1536 mono_thread_info_set_is_async_context (gboolean async_context
)
1538 MonoThreadInfo
*info
= mono_thread_info_current ();
1541 // If this assert fails, that means there is recursion and/or
1542 // concurrency, such that setting async_context to FALSE
1543 // that all the callers do after this, is incorrect,
1544 // and this should instead be incremented/decremented.
1546 // As the value is only accessed via current(), that
1547 // limits the case to recursion, but increment/decrement
1548 // is still fast and correct and simple.
1549 g_assert (!async_context
|| !info
->is_async_context
);
1550 info
->is_async_context
= async_context
;
1555 mono_thread_info_is_async_context (void)
1557 MonoThreadInfo
*info
= mono_thread_info_current ();
1560 return info
->is_async_context
;
1566 * mono_thread_info_get_stack_bounds:
1568 * Return the address and size of the current threads stack. Return NULL as the
1569 * stack address if the stack address cannot be determined.
1572 mono_thread_info_get_stack_bounds (guint8
**staddr
, size_t *stsize
)
1574 guint8
*current
= (guint8
*)&stsize
;
1575 mono_threads_platform_get_stack_bounds (staddr
, stsize
);
1579 /* Sanity check the result */
1580 g_assert ((current
> *staddr
) && (current
< *staddr
+ *stsize
));
1583 /* When running under emacs, sometimes staddr is not aligned to a page size */
1584 *staddr
= (guint8
*)((gssize
)*staddr
& ~(mono_pagesize () - 1));
1589 mono_thread_info_yield (void)
1591 return mono_threads_platform_yield ();
1594 static mono_lazy_init_t sleep_init
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
1595 static MonoCoopMutex sleep_mutex
;
1596 static MonoCoopCond sleep_cond
;
1599 sleep_initialize (void)
1601 mono_coop_mutex_init (&sleep_mutex
);
1602 mono_coop_cond_init (&sleep_cond
);
1606 sleep_interrupt (gpointer data
)
1608 mono_coop_mutex_lock (&sleep_mutex
);
1609 mono_coop_cond_broadcast (&sleep_cond
);
1610 mono_coop_mutex_unlock (&sleep_mutex
);
1614 sleep_interruptable (guint32 ms
, gboolean
*alerted
)
1618 g_assert (MONO_INFINITE_WAIT
== G_MAXUINT32
);
1623 if (ms
!= MONO_INFINITE_WAIT
)
1624 end
= mono_msec_ticks() + ms
;
1626 mono_lazy_initialize (&sleep_init
, sleep_initialize
);
1628 mono_coop_mutex_lock (&sleep_mutex
);
1631 if (ms
!= MONO_INFINITE_WAIT
) {
1632 now
= mono_msec_ticks();
1637 mono_thread_info_install_interrupt (sleep_interrupt
, NULL
, alerted
);
1639 mono_coop_mutex_unlock (&sleep_mutex
);
1640 return WAIT_IO_COMPLETION
;
1643 if (ms
!= MONO_INFINITE_WAIT
)
1644 mono_coop_cond_timedwait (&sleep_cond
, &sleep_mutex
, end
- now
);
1646 mono_coop_cond_wait (&sleep_cond
, &sleep_mutex
);
1648 mono_thread_info_uninstall_interrupt (alerted
);
1650 mono_coop_mutex_unlock (&sleep_mutex
);
1651 return WAIT_IO_COMPLETION
;
1655 mono_coop_mutex_unlock (&sleep_mutex
);
1661 mono_thread_info_sleep (guint32 ms
, gboolean
*alerted
)
1664 MonoThreadInfo
*info
;
1666 mono_thread_info_yield ();
1668 info
= mono_thread_info_current ();
1669 if (info
&& mono_thread_info_is_interrupt_state (info
))
1670 return WAIT_IO_COMPLETION
;
1676 return sleep_interruptable (ms
, alerted
);
1680 if (ms
== MONO_INFINITE_WAIT
) {
1683 Sleep (G_MAXUINT32
);
1685 sleep (G_MAXUINT32
);
1689 #if defined (HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__)
1691 struct timespec start
, target
;
1693 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1694 ret
= clock_gettime (CLOCK_MONOTONIC
, &start
);
1695 g_assert (ret
== 0);
1698 target
.tv_sec
+= ms
/ 1000;
1699 target
.tv_nsec
+= (ms
% 1000) * 1000000;
1700 if (target
.tv_nsec
> 999999999) {
1701 target
.tv_nsec
-= 999999999;
1706 ret
= clock_nanosleep (CLOCK_MONOTONIC
, TIMER_ABSTIME
, &target
, NULL
);
1712 struct timespec req
, rem
;
1714 req
.tv_sec
= ms
/ 1000;
1715 req
.tv_nsec
= (ms
% 1000) * 1000000;
1718 memset (&rem
, 0, sizeof (rem
));
1719 ret
= nanosleep (&req
, &rem
);
1721 #endif /* __linux__ */
1730 mono_thread_info_usleep (guint64 us
)
1739 mono_thread_info_tls_get (THREAD_INFO_TYPE
*info
, MonoTlsKey key
)
1741 return ((MonoThreadInfo
*)info
)->tls
[key
];
1745 * mono_threads_info_tls_set:
1747 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1748 * values of TLS variables for threads other than the current thread.
1749 * This should only be used for infrequently changing TLS variables, and it should
1750 * be paired with setting the real TLS variable since this provides no GC tracking.
1753 mono_thread_info_tls_set (THREAD_INFO_TYPE
*info
, MonoTlsKey key
, gpointer value
)
1755 ((MonoThreadInfo
*)info
)->tls
[key
] = value
;
1759 * mono_thread_info_exit:
1761 * Exit the current thread.
1762 * This function doesn't return.
1765 mono_thread_info_exit (gsize exit_code
)
1767 mono_thread_info_detach ();
1769 mono_threads_platform_exit (0);
1773 * mono_threads_open_thread_handle:
1775 * Duplicate the handle. The handle needs to be closed by calling
1776 * mono_threads_close_thread_handle () when it is no longer needed.
1779 mono_threads_open_thread_handle (MonoThreadHandle
*thread_handle
)
1781 return mono_refcount_inc (thread_handle
);
1785 mono_threads_close_thread_handle (MonoThreadHandle
*thread_handle
)
1789 mono_refcount_dec (thread_handle
);
1793 * mono_threads_open_native_thread_handle:
1795 * Duplicate the handle. The handle needs to be closed by calling
1796 * mono_threads_close_native_thread_handle () when it is no longer needed.
1798 MonoNativeThreadHandle
1799 mono_threads_open_native_thread_handle (MonoNativeThreadHandle thread_handle
)
1802 BOOL success
= FALSE
;
1803 HANDLE new_thread_handle
= NULL
;
1805 g_assert (thread_handle
&& thread_handle
!= INVALID_HANDLE_VALUE
);
1806 return DuplicateHandle (GetCurrentProcess (), thread_handle
, GetCurrentProcess (), &new_thread_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
) ? new_thread_handle
: NULL
;
1808 return MONO_GPOINTER_TO_NATIVE_THREAD_HANDLE (NULL
);
1813 mono_threads_close_native_thread_handle (MonoNativeThreadHandle thread_handle
)
1816 g_assert (thread_handle
!= INVALID_HANDLE_VALUE
);
1818 CloseHandle (thread_handle
);
1823 mono_threads_signal_thread_handle (MonoThreadHandle
* thread_handle
)
1825 mono_os_event_set (&thread_handle
->event
);
1828 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1830 struct _MonoThreadInfoInterruptToken
{
1831 void (*callback
) (gpointer data
);
1836 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1838 * - @callback: must be able to be called from another thread and always cancel the wait
1839 * - @data: passed to the callback
1840 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1841 * if set to TRUE, it must mean that the thread is in interrupted state
1844 mono_thread_info_install_interrupt (void (*callback
) (gpointer data
), gpointer data
, gboolean
*interrupted
)
1846 MonoThreadInfo
*info
;
1847 MonoThreadInfoInterruptToken
*previous_token
, *token
;
1849 g_assert (callback
);
1851 g_assert (interrupted
);
1852 *interrupted
= FALSE
;
1854 info
= mono_thread_info_current ();
1855 g_assertf (info
, ""); // f includes __func__
1857 /* The memory of this token can be freed at 2 places:
1858 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1859 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1860 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1861 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1862 token
= g_new0 (MonoThreadInfoInterruptToken
, 1);
1863 token
->callback
= callback
;
1866 previous_token
= (MonoThreadInfoInterruptToken
*)mono_atomic_cas_ptr ((gpointer
*) &info
->interrupt_token
, token
, NULL
);
1868 if (previous_token
) {
1869 if (previous_token
!= INTERRUPT_STATE
)
1870 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE
, previous_token
);
1874 *interrupted
= TRUE
;
1877 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1878 mono_thread_info_get_tid (info
), token
, previous_token
, *interrupted
? "TRUE" : "FALSE");
1882 mono_thread_info_uninstall_interrupt (gboolean
*interrupted
)
1884 MonoThreadInfo
*info
;
1885 MonoThreadInfoInterruptToken
*previous_token
;
1887 /* Common to uninstall interrupt handler around OS API's affecting last error. */
1888 /* This method could call OS API's on some platforms that will reset last error so make sure to restore */
1889 /* last error before exit. */
1890 W32_DEFINE_LAST_ERROR_RESTORE_POINT
;
1892 g_assert (interrupted
);
1893 *interrupted
= FALSE
;
1895 info
= mono_thread_info_current ();
1896 g_assertf (info
, ""); // f includes __func__
1898 previous_token
= (MonoThreadInfoInterruptToken
*)mono_atomic_xchg_ptr ((gpointer
*) &info
->interrupt_token
, NULL
);
1900 /* only the installer can uninstall the token */
1901 g_assert (previous_token
);
1903 if (previous_token
== INTERRUPT_STATE
) {
1904 /* if it is interrupted, then it is going to be freed in finish interrupt */
1905 *interrupted
= TRUE
;
1907 g_free (previous_token
);
1910 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1911 mono_thread_info_get_tid (info
), previous_token
, *interrupted
? "TRUE" : "FALSE");
1913 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT
;
1916 static MonoThreadInfoInterruptToken
*
1917 set_interrupt_state (MonoThreadInfo
*info
)
1919 MonoThreadInfoInterruptToken
*token
, *previous_token
;
1921 g_assertf (info
, ""); // f includes __func__
1923 /* Atomically obtain the token the thread is
1924 * waiting on, and change it to a flag value. */
1927 previous_token
= info
->interrupt_token
;
1929 /* Already interrupted */
1930 if (previous_token
== INTERRUPT_STATE
) {
1935 token
= previous_token
;
1936 } while (mono_atomic_cas_ptr ((gpointer
*) &info
->interrupt_token
, INTERRUPT_STATE
, previous_token
) != previous_token
);
1942 * mono_thread_info_prepare_interrupt:
1944 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1945 * - if the thread calls one of the WaitFor functions, the function will return with
1946 * WAIT_IO_COMPLETION instead of waiting
1947 * - if the thread was waiting when this function was called, the wait will be broken
1949 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1950 * didn't receive the interrupt signal yet, in this case it should call the wait function
1951 * again. This essentially means that the target thread will busy wait until it is ready to
1952 * process the interruption.
1954 MonoThreadInfoInterruptToken
*
1955 mono_thread_info_prepare_interrupt (MonoThreadInfo
*info
)
1957 MonoThreadInfoInterruptToken
*token
;
1959 token
= set_interrupt_state (info
);
1961 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1962 mono_thread_info_get_tid (info
), token
);
1968 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken
*token
)
1970 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token
);
1975 g_assert (token
->callback
);
1977 token
->callback (token
->data
);
1983 mono_thread_info_self_interrupt (void)
1985 MonoThreadInfo
*info
;
1986 MonoThreadInfoInterruptToken
*token
;
1988 info
= mono_thread_info_current ();
1989 g_assertf (info
, ""); // f includes __func__
1991 token
= set_interrupt_state (info
);
1994 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1995 mono_thread_info_get_tid (info
));
1998 /* Clear the interrupted flag of the current thread, set with
1999 * mono_thread_info_self_interrupt, so it can wait again */
2001 mono_thread_info_clear_self_interrupt (void)
2003 MonoThreadInfo
*info
;
2004 MonoThreadInfoInterruptToken
*previous_token
;
2006 info
= mono_thread_info_current ();
2007 g_assertf (info
, ""); // f includes __func__
2009 previous_token
= (MonoThreadInfoInterruptToken
*)mono_atomic_cas_ptr ((gpointer
*) &info
->interrupt_token
, NULL
, INTERRUPT_STATE
);
2010 g_assert (previous_token
== NULL
|| previous_token
== INTERRUPT_STATE
);
2012 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info
), previous_token
);
2016 mono_thread_info_is_interrupt_state (MonoThreadInfo
*info
)
2018 g_assertf (info
, ""); // f includes __func__
2019 return mono_atomic_load_ptr ((gpointer
*) &info
->interrupt_token
) == INTERRUPT_STATE
;
2023 mono_thread_info_describe_interrupt_token (MonoThreadInfo
*info
, GString
*text
)
2025 g_assertf (info
, ""); // f includes __func__
2027 if (!mono_atomic_load_ptr ((gpointer
*) &info
->interrupt_token
))
2028 g_string_append_printf (text
, "not waiting");
2029 else if (mono_atomic_load_ptr ((gpointer
*) &info
->interrupt_token
) == INTERRUPT_STATE
)
2030 g_string_append_printf (text
, "interrupted state");
2032 g_string_append_printf (text
, "waiting");
2036 mono_thread_info_is_current (MonoThreadInfo
*info
)
2038 return mono_thread_info_get_tid (info
) == mono_native_thread_id_get ();
2041 MonoThreadInfoWaitRet
2042 mono_thread_info_wait_one_handle (MonoThreadHandle
*thread_handle
, guint32 timeout
, gboolean alertable
)
2044 MonoOSEventWaitRet res
;
2046 res
= mono_os_event_wait_one (&thread_handle
->event
, timeout
, alertable
);
2047 if (res
== MONO_OS_EVENT_WAIT_RET_SUCCESS_0
)
2048 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
;
2049 else if (res
== MONO_OS_EVENT_WAIT_RET_ALERTED
)
2050 return MONO_THREAD_INFO_WAIT_RET_ALERTED
;
2051 else if (res
== MONO_OS_EVENT_WAIT_RET_TIMEOUT
)
2052 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT
;
2054 g_error ("%s: unknown res value %d", __func__
, res
);
2057 MonoThreadInfoWaitRet
2058 mono_thread_info_wait_multiple_handle (MonoThreadHandle
**thread_handles
, gsize nhandles
, MonoOSEvent
*background_change_event
, gboolean waitall
, guint32 timeout
, gboolean alertable
)
2060 MonoOSEventWaitRet res
;
2061 MonoOSEvent
*thread_events
[MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS
];
2064 g_assert (nhandles
<= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS
);
2065 if (background_change_event
)
2066 g_assert (nhandles
<= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS
- 1);
2068 for (i
= 0; i
< nhandles
; ++i
)
2069 thread_events
[i
] = &thread_handles
[i
]->event
;
2071 if (background_change_event
)
2072 thread_events
[nhandles
++] = background_change_event
;
2074 res
= mono_os_event_wait_multiple (thread_events
, nhandles
, waitall
, timeout
, alertable
);
2075 if (res
>= MONO_OS_EVENT_WAIT_RET_SUCCESS_0
&& res
<= MONO_OS_EVENT_WAIT_RET_SUCCESS_0
+ nhandles
- 1)
2076 return (MonoThreadInfoWaitRet
)(MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
+ (res
- MONO_OS_EVENT_WAIT_RET_SUCCESS_0
));
2077 else if (res
== MONO_OS_EVENT_WAIT_RET_ALERTED
)
2078 return MONO_THREAD_INFO_WAIT_RET_ALERTED
;
2079 else if (res
== MONO_OS_EVENT_WAIT_RET_TIMEOUT
)
2080 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT
;
2082 g_error ("%s: unknown res value %d", __func__
, res
);
2086 * mono_threads_join_mutex:
2088 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
2089 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
2090 * The code inside the lock should not block.
2093 mono_threads_join_lock (void)
2096 mono_os_mutex_lock (&join_mutex
);
2101 mono_threads_join_unlock (void)
2104 mono_os_mutex_unlock (&join_mutex
);
2110 mono_thread_info_set_tools_data (void *data
)
2112 MonoThreadInfo
*info
= mono_thread_info_current_unchecked ();
2115 if (info
->tools_data
)
2117 info
->tools_data
= data
;
2122 mono_thread_info_get_tools_data (void)
2124 MonoThreadInfo
*info
= mono_thread_info_current_unchecked ();
2126 return info
? info
->tools_data
: NULL
;