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>
40 #include <mono/utils/mach-support.h>
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
62 static MonoSemType global_suspend_semaphore;
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
69 static __thread guint32 tls_small_id;
71 static MonoNativeTlsKey small_id_key;
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
79 static mono_mutex_t join_mutex;
81 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
84 #define SLEEP_DURATION_BEFORE_WARNING (50)
86 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
88 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
89 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
91 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
94 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
96 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
97 InterlockedIncrement (&abort_posts);
98 mono_os_sem_post (&suspend_semaphore);
102 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
104 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
105 InterlockedIncrement (&suspend_posts);
106 mono_os_sem_post (&suspend_semaphore);
110 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
112 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
113 InterlockedIncrement (&resume_posts);
114 mono_os_sem_post (&suspend_semaphore);
118 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
120 if (mono_threads_is_coop_enabled ()) {
121 /* There's nothing else to do after we async request the thread to suspend */
122 mono_threads_add_to_pending_operation_set (info);
126 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
130 check_async_suspend (MonoThreadInfo *info)
132 if (mono_threads_is_coop_enabled ()) {
133 /* Async suspend can't async fail on coop */
137 return mono_threads_suspend_check_suspend_result (info);
141 resume_async_suspended (MonoThreadInfo *info)
143 if (mono_threads_is_coop_enabled ())
144 g_assert_not_reached ();
146 g_assert (mono_threads_suspend_begin_async_resume (info));
150 resume_self_suspended (MonoThreadInfo* info)
152 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
153 mono_os_sem_post (&info->resume_semaphore);
157 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
160 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
161 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
162 g_assert (res != -1);
166 resume_blocking_suspended (MonoThreadInfo* info)
168 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
169 mono_os_sem_post (&info->resume_semaphore);
173 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
175 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
177 InterlockedIncrement (&pending_ops);
181 mono_threads_begin_global_suspend (void)
183 size_t ps = pending_suspends;
184 if (G_UNLIKELY (ps != 0))
185 g_error ("pending_suspends = %d, but must be 0", ps);
186 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,
187 abort_posts, waits_done, pending_ops);
188 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
189 mono_threads_coop_begin_global_suspend ();
193 mono_threads_end_global_suspend (void)
195 size_t ps = pending_suspends;
196 if (G_UNLIKELY (ps != 0))
197 g_error ("pending_suspends = %d, but must be 0", ps);
198 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
199 abort_posts, waits_done, pending_ops);
200 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
201 mono_threads_coop_end_global_suspend ();
207 MonoThreadInfo *cur = mono_thread_info_current ();
209 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
217 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
218 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
220 FOREACH_THREAD_SAFE (info) {
222 char thread_name [256] = { 0 };
223 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
225 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
227 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
229 } FOREACH_THREAD_SAFE_END
233 mono_threads_wait_pending_operations (void)
236 int c = pending_suspends;
238 /* Wait threads to park */
239 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
240 if (pending_suspends) {
241 MonoStopwatch suspension_time;
242 mono_stopwatch_start (&suspension_time);
243 for (i = 0; i < pending_suspends; ++i) {
244 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
245 InterlockedIncrement (&waits_done);
246 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
248 mono_stopwatch_stop (&suspension_time);
252 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
253 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
255 mono_stopwatch_stop (&suspension_time);
256 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
260 pending_suspends = 0;
266 //Thread initialization code
269 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
272 mono_hazard_pointer_clear (hp, 0);
274 mono_hazard_pointer_clear (hp, 1);
276 mono_hazard_pointer_clear (hp, 2);
280 If return non null Hazard Pointer 1 holds the return value.
283 mono_thread_info_lookup (MonoNativeThreadId id)
285 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
287 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
288 mono_hazard_pointer_clear_all (hp, -1);
292 mono_hazard_pointer_clear_all (hp, 1);
293 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
297 mono_thread_info_insert (MonoThreadInfo *info)
299 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
301 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
302 mono_hazard_pointer_clear_all (hp, -1);
306 mono_hazard_pointer_clear_all (hp, -1);
311 mono_thread_info_remove (MonoThreadInfo *info)
313 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
316 THREADS_DEBUG ("removing info %p\n", info);
317 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
318 mono_hazard_pointer_clear_all (hp, -1);
323 free_thread_info (gpointer mem)
325 MonoThreadInfo *info = (MonoThreadInfo *) mem;
327 mono_os_sem_destroy (&info->resume_semaphore);
328 mono_threads_suspend_free (info);
334 mono_thread_info_register_small_id (void)
336 int small_id = mono_thread_small_id_alloc ();
337 #ifdef HAVE_KW_THREAD
338 tls_small_id = small_id;
340 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
346 thread_handle_destroy (gpointer data)
348 MonoThreadHandle *thread_handle;
350 thread_handle = (MonoThreadHandle*) data;
352 mono_os_event_destroy (&thread_handle->event);
353 g_free (thread_handle);
357 register_thread (MonoThreadInfo *info)
360 guint8 *staddr = NULL;
363 info->small_id = mono_thread_info_register_small_id ();
364 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
366 info->handle = g_new0 (MonoThreadHandle, 1);
367 mono_refcount_init (info->handle, thread_handle_destroy);
368 mono_os_event_init (&info->handle->event, FALSE);
370 mono_os_sem_init (&info->resume_semaphore, 0);
372 /*set TLS early so SMR works */
373 mono_native_tls_set_value (thread_info_key, info);
375 mono_thread_info_get_stack_bounds (&staddr, &stsize);
378 info->stack_start_limit = staddr;
379 info->stack_end = staddr + stsize;
381 info->stackdata = g_byte_array_new ();
383 info->internal_thread_gchandle = G_MAXUINT32;
385 mono_threads_suspend_register (info);
387 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
389 if (threads_callbacks.thread_attach) {
390 if (!threads_callbacks.thread_attach (info)) {
391 // g_warning ("thread registation failed\n");
392 mono_native_tls_set_value (thread_info_key, NULL);
398 Transition it before taking any locks or publishing itself to reduce the chance
399 of others witnessing a detached thread.
400 We can reasonably expect that until this thread gets published, no other thread will
401 try to manipulate it.
403 mono_threads_transition_attach (info);
404 mono_thread_info_suspend_lock ();
405 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
406 result = mono_thread_info_insert (info);
408 mono_thread_info_suspend_unlock ();
414 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
417 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
420 unregister_thread (void *arg)
422 gpointer gc_unsafe_stackdata;
423 MonoThreadInfo *info;
428 info = (MonoThreadInfo *) arg;
430 g_assert (mono_thread_info_is_current (info));
431 g_assert (mono_thread_info_is_live (info));
433 /* Pump the HP queue while the thread is alive.*/
434 mono_thread_hazardous_try_free_some ();
436 small_id = info->small_id;
438 /* We only enter the GC unsafe region, as when exiting this function, the thread
439 * will be detached, and the current MonoThreadInfo* will be destroyed. */
440 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
442 THREADS_DEBUG ("unregistering info %p\n", info);
444 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
447 * TLS destruction order is not reliable so small_id might be cleaned up
450 #ifndef HAVE_KW_THREAD
451 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
454 /* we need to duplicate it, as the info->handle is going
455 * to be closed when unregistering from the platform */
456 handle = mono_threads_open_thread_handle (info->handle);
459 First perform the callback that requires no locks.
460 This callback has the potential of taking other locks, so we do it before.
461 After it completes, the thread remains functional.
463 if (threads_callbacks.thread_detach)
464 threads_callbacks.thread_detach (info);
466 mono_thread_info_suspend_lock_with_info (info);
469 Now perform the callback that must be done under locks.
470 This will render the thread useless and non-suspendable, so it must
471 be done while holding the suspend lock to give no other thread chance
474 if (threads_callbacks.thread_detach_with_lock)
475 threads_callbacks.thread_detach_with_lock (info);
477 /* The thread is no longer active, so unref its handle */
478 mono_threads_close_thread_handle (info->handle);
481 result = mono_thread_info_remove (info);
483 mono_threads_transition_detach (info);
485 mono_thread_info_suspend_unlock ();
487 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
489 /*now it's safe to free the thread info.*/
490 mono_thread_hazardous_try_free (info, free_thread_info);
492 mono_thread_small_id_free (small_id);
494 mono_threads_signal_thread_handle (handle);
496 mono_threads_close_thread_handle (handle);
498 mono_native_tls_set_value (thread_info_key, NULL);
502 thread_exited_dtor (void *arg)
504 #if defined(__MACH__)
506 * Since we use pthread dtors to clean up thread data, if a thread
507 * is attached to the runtime by another pthread dtor after our dtor
508 * has ran, it will never be detached, leading to various problems
509 * since the thread ids etc. will be reused while they are still in
510 * the threads hashtables etc.
511 * Dtors are called in a loop until all user tls entries are 0,
512 * but the loop has a maximum count (4), so if we set the tls
513 * variable every time, it will remain set when system tls dtors
514 * are ran. This allows mono_thread_info_is_exiting () to detect
515 * whenever the thread is exiting, even if it is executed from a
516 * system tls dtor (i.e. obj-c dealloc methods).
518 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
523 mono_thread_info_current_unchecked (void)
525 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
530 mono_thread_info_current (void)
532 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
536 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
539 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
540 The way to distinguish between before, during and after cleanup is the following:
542 -If the TLS key is set, cleanup has not begun;
543 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
544 -If the thread is nowhere to be found, cleanup has finished.
546 We cannot function after cleanup since there's no way to ensure what will happen.
550 /*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 */
551 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
557 mono_thread_info_get_small_id (void)
559 #ifdef HAVE_KW_THREAD
562 gpointer val = mono_native_tls_get_value (small_id_key);
565 return GPOINTER_TO_INT (val) - 1;
570 mono_thread_info_list_head (void)
576 * mono_threads_attach_tools_thread
578 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
580 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
581 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
584 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
585 * doing things like resolving backtraces in their background processing thread.
588 mono_threads_attach_tools_thread (void)
590 MonoThreadInfo *info;
592 /* Must only be called once */
593 g_assert (!mono_native_tls_get_value (thread_info_key));
595 while (!mono_threads_inited) {
596 mono_thread_info_usleep (10);
599 info = mono_thread_info_attach ();
602 info->tools_thread = TRUE;
606 mono_thread_info_attach (void)
608 MonoThreadInfo *info;
611 if (!mono_threads_inited)
613 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
614 * thread is created before an embedding API user initialized Mono. */
615 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
620 g_assert (mono_threads_inited);
622 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
624 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
625 THREADS_DEBUG ("attaching %p\n", info);
626 if (!register_thread (info)) {
636 mono_thread_info_detach (void)
638 MonoThreadInfo *info;
641 if (!mono_threads_inited)
643 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
644 * is created before an embedding API user initialized Mono. */
645 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
650 g_assert (mono_threads_inited);
652 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
654 THREADS_DEBUG ("detaching %p\n", info);
655 unregister_thread (info);
660 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
664 if (info->internal_thread_gchandle == G_MAXUINT32)
667 *gchandle = info->internal_thread_gchandle;
672 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
675 g_assert (gchandle != G_MAXUINT32);
676 info->internal_thread_gchandle = gchandle;
680 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
683 info->internal_thread_gchandle = G_MAXUINT32;
687 * mono_thread_info_is_exiting:
689 * Return whenever the current thread is exiting, i.e. it is running pthread
693 mono_thread_info_is_exiting (void)
695 #if defined(__MACH__)
696 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
704 thread_info_key_dtor (void *arg)
706 /* Put the MonoThreadInfo back for the duration of the
707 * unregister code. In some circumstances the thread needs to
708 * take the GC lock which may block which requires a coop
709 * state transition. */
710 mono_native_tls_set_value (thread_info_key, arg);
711 unregister_thread (arg);
712 mono_native_tls_set_value (thread_info_key, NULL);
717 mono_thread_info_init (size_t info_size)
720 thread_info_size = info_size;
723 res = mono_native_tls_alloc (&thread_info_key, NULL);
724 res = mono_native_tls_alloc (&thread_exited_key, NULL);
726 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
727 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
732 #ifndef HAVE_KW_THREAD
733 res = mono_native_tls_alloc (&small_id_key, NULL);
737 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
739 long threshold = strtol(sleepLimit, NULL, 10);
740 if ((errno == 0) && (threshold >= 40)) {
741 sleepAbortDuration = threshold;
742 sleepWarnDuration = threshold / 20;
744 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
748 mono_os_sem_init (&global_suspend_semaphore, 1);
749 mono_os_sem_init (&suspend_semaphore, 0);
750 mono_os_mutex_init (&join_mutex);
752 mono_lls_init (&thread_list, NULL);
753 mono_thread_smr_init ();
754 mono_threads_suspend_init ();
755 mono_threads_coop_init ();
756 mono_threads_platform_init ();
758 #if defined(__MACH__)
759 mono_mach_init (thread_info_key);
762 mono_threads_inited = TRUE;
764 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
768 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
770 threads_callbacks = *callbacks;
774 mono_thread_info_signals_init (void)
776 mono_threads_suspend_init_signals ();
780 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
782 runtime_callbacks = *callbacks;
785 MonoThreadInfoRuntimeCallbacks *
786 mono_threads_get_runtime_callbacks (void)
788 return &runtime_callbacks;
792 mono_thread_info_core_resume (MonoThreadInfo *info)
794 gboolean res = FALSE;
796 switch (mono_threads_transition_request_resume (info)) {
803 case ResumeInitSelfResume:
804 resume_self_suspended (info);
807 case ResumeInitAsyncResume:
808 resume_async_suspended (info);
811 case ResumeInitBlockingResume:
812 resume_blocking_suspended (info);
821 mono_thread_info_resume (MonoNativeThreadId tid)
823 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
824 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
825 MonoThreadInfo *info;
827 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
829 mono_thread_info_suspend_lock ();
831 info = mono_thread_info_lookup (tid); /*info on HP1*/
837 result = mono_thread_info_core_resume (info);
839 //Wait for the pending resume to finish
840 mono_threads_wait_pending_operations ();
843 mono_thread_info_suspend_unlock ();
844 mono_hazard_pointer_clear (hp, 1);
849 mono_thread_info_begin_suspend (MonoThreadInfo *info)
851 switch (mono_threads_transition_request_async_suspension (info)) {
852 case AsyncSuspendAlreadySuspended:
853 case AsyncSuspendBlocking:
855 case AsyncSuspendWait:
856 mono_threads_add_to_pending_operation_set (info);
858 case AsyncSuspendInitSuspend:
859 return begin_async_suspend (info, FALSE);
861 g_assert_not_reached ();
866 mono_thread_info_begin_resume (MonoThreadInfo *info)
868 return mono_thread_info_core_resume (info);
872 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
873 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
876 is_thread_in_critical_region (MonoThreadInfo *info)
878 gpointer stack_start;
879 MonoThreadUnwindState *state;
881 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
884 /* Are we inside a system critical region? */
885 if (info->inside_critical_region)
888 /* Are we inside a GC critical region? */
889 if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
893 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
894 state = mono_thread_info_get_suspend_state (info);
895 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
898 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
899 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
900 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
903 if (threads_callbacks.ip_in_critical_region)
904 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
910 mono_thread_info_in_critical_location (MonoThreadInfo *info)
912 return is_thread_in_critical_region (info);
916 The return value is only valid until a matching mono_thread_info_resume is called
918 static MonoThreadInfo*
919 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
921 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
922 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
926 switch (mono_threads_transition_request_async_suspension (info)) {
927 case AsyncSuspendAlreadySuspended:
928 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
930 case AsyncSuspendWait:
931 mono_threads_add_to_pending_operation_set (info);
933 case AsyncSuspendInitSuspend:
934 if (!begin_async_suspend (info, interrupt_kernel)) {
935 mono_hazard_pointer_clear (hp, 1);
939 case AsyncSuspendBlocking:
940 if (interrupt_kernel)
941 mono_threads_suspend_abort_syscall (info);
945 g_assert_not_reached ();
948 //Wait for the pending suspend to finish
949 mono_threads_wait_pending_operations ();
951 if (!check_async_suspend (info)) {
952 mono_thread_info_core_resume (info);
953 mono_threads_wait_pending_operations ();
954 mono_hazard_pointer_clear (hp, 1);
960 static MonoThreadInfo*
961 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
963 MonoThreadInfo *info = NULL;
964 int sleep_duration = 0;
966 if (!(info = suspend_sync (id, interrupt_kernel))) {
967 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
971 /*WARNING: We now are in interrupt context until we resume the thread. */
972 if (!is_thread_in_critical_region (info))
975 if (!mono_thread_info_core_resume (info)) {
976 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
979 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
981 /* Wait for the pending resume to finish */
982 mono_threads_wait_pending_operations ();
984 if (sleep_duration == 0)
985 mono_thread_info_yield ();
987 g_usleep (sleep_duration);
989 sleep_duration += 10;
995 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
998 MonoThreadInfo *info = NULL;
999 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1001 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
1002 /*FIXME: unify this with self-suspend*/
1003 g_assert (id != mono_native_thread_id_get ());
1005 /* This can block during stw */
1006 mono_thread_info_suspend_lock ();
1007 mono_threads_begin_global_suspend ();
1009 info = suspend_sync_nolock (id, interrupt_kernel);
1013 switch (result = callback (info, user_data)) {
1014 case MonoResumeThread:
1015 mono_hazard_pointer_set (hp, 1, info);
1016 mono_thread_info_core_resume (info);
1017 mono_threads_wait_pending_operations ();
1020 g_assert (!mono_threads_is_coop_enabled ());
1023 g_error ("Invalid suspend_and_run callback return value %d", result);
1027 mono_hazard_pointer_clear (hp, 1);
1028 mono_threads_end_global_suspend ();
1029 mono_thread_info_suspend_unlock ();
1033 Inject an assynchronous call into the target thread. The target thread must be suspended and
1034 only a single async call can be setup for a given suspend cycle.
1035 This async call must cause stack unwinding as the current implementation doesn't save enough state
1036 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1037 currently used only to deliver exceptions.
1040 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1042 if (!mono_threads_is_coop_enabled ()) {
1043 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1044 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1045 * region or entering a gc unsafe region */
1046 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1048 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1049 g_assert (!info->async_target);
1050 info->async_target = target_func;
1051 /* This is not GC tracked */
1052 info->user_data = user_data;
1056 The suspend lock is held during any suspend in progress.
1057 A GC that has safepoints must take this lock as part of its
1058 STW to make sure no unsafe pending suspend is in progress.
1062 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1064 if (mono_threads_is_coop_enabled ()) {
1066 g_assert (mono_thread_info_is_current (info));
1067 g_assert (mono_thread_info_is_live (info));
1069 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1071 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1072 g_assert (res != -1);
1074 MONO_EXIT_GC_SAFE_WITH_INFO;
1076 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1077 g_assert (res != -1);
1082 mono_thread_info_suspend_lock (void)
1084 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1088 mono_thread_info_suspend_unlock (void)
1090 mono_os_sem_post (&global_suspend_semaphore);
1094 * This is a very specific function whose only purpose is to
1095 * break a given thread from socket syscalls.
1097 * This only exists because linux won't fail a call to connect
1098 * if the underlying is closed.
1100 * TODO We should cleanup and unify this with the other syscall abort
1104 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1106 MonoThreadHazardPointers *hp;
1107 MonoThreadInfo *info;
1109 if (tid == mono_native_thread_id_get ())
1112 hp = mono_hazard_pointer_get ();
1113 info = mono_thread_info_lookup (tid);
1117 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1118 mono_hazard_pointer_clear (hp, 1);
1122 mono_thread_info_suspend_lock ();
1123 mono_threads_begin_global_suspend ();
1125 mono_threads_suspend_abort_syscall (info);
1126 mono_threads_wait_pending_operations ();
1128 mono_hazard_pointer_clear (hp, 1);
1130 mono_threads_end_global_suspend ();
1131 mono_thread_info_suspend_unlock ();
1135 * mono_thread_info_set_is_async_context:
1137 * Set whenever the current thread is in an async context. Some runtime functions might behave
1138 * differently while in an async context in order to be async safe.
1141 mono_thread_info_set_is_async_context (gboolean async_context)
1143 MonoThreadInfo *info = mono_thread_info_current ();
1146 info->is_async_context = async_context;
1150 mono_thread_info_is_async_context (void)
1152 MonoThreadInfo *info = mono_thread_info_current ();
1155 return info->is_async_context;
1161 * mono_thread_info_get_stack_bounds:
1163 * Return the address and size of the current threads stack. Return NULL as the
1164 * stack address if the stack address cannot be determined.
1167 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1169 guint8 *current = (guint8 *)&stsize;
1170 mono_threads_platform_get_stack_bounds (staddr, stsize);
1174 /* Sanity check the result */
1175 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1177 /* When running under emacs, sometimes staddr is not aligned to a page size */
1178 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1182 mono_thread_info_yield (void)
1184 return mono_threads_platform_yield ();
1187 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1188 static MonoCoopMutex sleep_mutex;
1189 static MonoCoopCond sleep_cond;
1192 sleep_initialize (void)
1194 mono_coop_mutex_init (&sleep_mutex);
1195 mono_coop_cond_init (&sleep_cond);
1199 sleep_interrupt (gpointer data)
1201 mono_coop_mutex_lock (&sleep_mutex);
1202 mono_coop_cond_broadcast (&sleep_cond);
1203 mono_coop_mutex_unlock (&sleep_mutex);
1206 static inline guint32
1207 sleep_interruptable (guint32 ms, gboolean *alerted)
1211 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1216 if (ms != MONO_INFINITE_WAIT)
1217 end = mono_msec_ticks() + ms;
1219 mono_lazy_initialize (&sleep_init, sleep_initialize);
1221 mono_coop_mutex_lock (&sleep_mutex);
1224 if (ms != MONO_INFINITE_WAIT) {
1225 now = mono_msec_ticks();
1230 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1232 mono_coop_mutex_unlock (&sleep_mutex);
1233 return WAIT_IO_COMPLETION;
1236 if (ms != MONO_INFINITE_WAIT)
1237 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1239 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1241 mono_thread_info_uninstall_interrupt (alerted);
1243 mono_coop_mutex_unlock (&sleep_mutex);
1244 return WAIT_IO_COMPLETION;
1248 mono_coop_mutex_unlock (&sleep_mutex);
1254 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1257 MonoThreadInfo *info;
1259 mono_thread_info_yield ();
1261 info = mono_thread_info_current ();
1262 if (info && mono_thread_info_is_interrupt_state (info))
1263 return WAIT_IO_COMPLETION;
1269 return sleep_interruptable (ms, alerted);
1273 if (ms == MONO_INFINITE_WAIT) {
1276 Sleep (G_MAXUINT32);
1278 sleep (G_MAXUINT32);
1283 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1284 struct timespec start, target;
1286 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1287 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1288 g_assert (ret == 0);
1291 target.tv_sec += ms / 1000;
1292 target.tv_nsec += (ms % 1000) * 1000000;
1293 if (target.tv_nsec > 999999999) {
1294 target.tv_nsec -= 999999999;
1299 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1304 struct timespec req, rem;
1306 req.tv_sec = ms / 1000;
1307 req.tv_nsec = (ms % 1000) * 1000000;
1310 memset (&rem, 0, sizeof (rem));
1311 ret = nanosleep (&req, &rem);
1313 #endif /* __linux__ */
1322 mono_thread_info_usleep (guint64 us)
1331 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1333 return ((MonoThreadInfo*)info)->tls [key];
1337 * mono_threads_info_tls_set:
1339 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1340 * values of TLS variables for threads other than the current thread.
1341 * This should only be used for infrequently changing TLS variables, and it should
1342 * be paired with setting the real TLS variable since this provides no GC tracking.
1345 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1347 ((MonoThreadInfo*)info)->tls [key] = value;
1351 * mono_thread_info_exit:
1353 * Exit the current thread.
1354 * This function doesn't return.
1357 mono_thread_info_exit (gsize exit_code)
1359 mono_thread_info_detach ();
1361 mono_threads_platform_exit (0);
1365 * mono_threads_open_thread_handle:
1367 * Duplicate the handle. The handle needs to be closed by calling
1368 * mono_threads_close_thread_handle () when it is no longer needed.
1371 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1373 return mono_refcount_inc (thread_handle);
1377 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1379 mono_refcount_dec (thread_handle);
1383 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1385 mono_os_event_set (&thread_handle->event);
1388 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1390 struct _MonoThreadInfoInterruptToken {
1391 void (*callback) (gpointer data);
1396 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1398 * - @callback: must be able to be called from another thread and always cancel the wait
1399 * - @data: passed to the callback
1400 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1401 * if set to TRUE, it must mean that the thread is in interrupted state
1404 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1406 MonoThreadInfo *info;
1407 MonoThreadInfoInterruptToken *previous_token, *token;
1409 g_assert (callback);
1411 g_assert (interrupted);
1412 *interrupted = FALSE;
1414 info = mono_thread_info_current ();
1417 /* The memory of this token can be freed at 2 places:
1418 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1419 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1420 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1421 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1422 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1423 token->callback = callback;
1426 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1428 if (previous_token) {
1429 if (previous_token != INTERRUPT_STATE)
1430 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1434 *interrupted = TRUE;
1437 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1438 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1442 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1444 MonoThreadInfo *info;
1445 MonoThreadInfoInterruptToken *previous_token;
1447 g_assert (interrupted);
1448 *interrupted = FALSE;
1450 info = mono_thread_info_current ();
1453 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1455 /* only the installer can uninstall the token */
1456 g_assert (previous_token);
1458 if (previous_token == INTERRUPT_STATE) {
1459 /* if it is interrupted, then it is going to be freed in finish interrupt */
1460 *interrupted = TRUE;
1462 g_free (previous_token);
1465 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1466 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1469 static MonoThreadInfoInterruptToken*
1470 set_interrupt_state (MonoThreadInfo *info)
1472 MonoThreadInfoInterruptToken *token, *previous_token;
1476 /* Atomically obtain the token the thread is
1477 * waiting on, and change it to a flag value. */
1480 previous_token = info->interrupt_token;
1482 /* Already interrupted */
1483 if (previous_token == INTERRUPT_STATE) {
1488 token = previous_token;
1489 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1495 * mono_thread_info_prepare_interrupt:
1497 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1498 * - if the thread calls one of the WaitFor functions, the function will return with
1499 * WAIT_IO_COMPLETION instead of waiting
1500 * - if the thread was waiting when this function was called, the wait will be broken
1502 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1503 * didn't receive the interrupt signal yet, in this case it should call the wait function
1504 * again. This essentially means that the target thread will busy wait until it is ready to
1505 * process the interruption.
1507 MonoThreadInfoInterruptToken*
1508 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1510 MonoThreadInfoInterruptToken *token;
1512 token = set_interrupt_state (info);
1514 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1515 mono_thread_info_get_tid (info), token);
1521 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1523 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1528 g_assert (token->callback);
1530 token->callback (token->data);
1536 mono_thread_info_self_interrupt (void)
1538 MonoThreadInfo *info;
1539 MonoThreadInfoInterruptToken *token;
1541 info = mono_thread_info_current ();
1544 token = set_interrupt_state (info);
1547 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1548 mono_thread_info_get_tid (info));
1551 /* Clear the interrupted flag of the current thread, set with
1552 * mono_thread_info_self_interrupt, so it can wait again */
1554 mono_thread_info_clear_self_interrupt ()
1556 MonoThreadInfo *info;
1557 MonoThreadInfoInterruptToken *previous_token;
1559 info = mono_thread_info_current ();
1562 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1563 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1565 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1569 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1572 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1576 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1580 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1581 g_string_append_printf (text, "not waiting");
1582 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1583 g_string_append_printf (text, "interrupted state");
1585 g_string_append_printf (text, "waiting");
1589 mono_thread_info_is_current (MonoThreadInfo *info)
1591 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1594 MonoThreadInfoWaitRet
1595 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1597 MonoOSEventWaitRet res;
1599 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1600 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1601 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1602 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1603 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1604 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1605 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1607 g_error ("%s: unknown res value %d", __func__, res);
1610 MonoThreadInfoWaitRet
1611 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1613 MonoOSEventWaitRet res;
1614 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1617 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1618 if (background_change_event)
1619 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1621 for (i = 0; i < nhandles; ++i)
1622 thread_events [i] = &thread_handles [i]->event;
1624 if (background_change_event)
1625 thread_events [nhandles ++] = background_change_event;
1627 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1628 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1629 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1630 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1631 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1632 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1633 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1635 g_error ("%s: unknown res value %d", __func__, res);
1639 * mono_threads_join_mutex:
1641 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1642 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1643 * The code inside the lock should not block.
1646 mono_threads_join_lock (void)
1649 mono_os_mutex_lock (&join_mutex);
1654 mono_threads_join_unlock (void)
1657 mono_os_mutex_unlock (&join_mutex);