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 gint32 tls_small_id = -1;
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
336 * Registers a small ID for the current thread. This is a 16-bit value uniquely
337 * identifying the current thread. If the current thread already has a small ID
338 * assigned, that small ID will be returned; otherwise, the newly assigned small
342 mono_thread_info_register_small_id (void)
344 int small_id = mono_thread_info_get_small_id ();
349 small_id = mono_thread_small_id_alloc ();
350 #ifdef HAVE_KW_THREAD
351 tls_small_id = small_id;
353 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
359 thread_handle_destroy (gpointer data)
361 MonoThreadHandle *thread_handle;
363 thread_handle = (MonoThreadHandle*) data;
365 mono_os_event_destroy (&thread_handle->event);
366 g_free (thread_handle);
370 register_thread (MonoThreadInfo *info)
373 guint8 *staddr = NULL;
376 info->small_id = mono_thread_info_register_small_id ();
377 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
379 info->handle = g_new0 (MonoThreadHandle, 1);
380 mono_refcount_init (info->handle, thread_handle_destroy);
381 mono_os_event_init (&info->handle->event, FALSE);
383 mono_os_sem_init (&info->resume_semaphore, 0);
385 /*set TLS early so SMR works */
386 mono_native_tls_set_value (thread_info_key, info);
388 mono_thread_info_get_stack_bounds (&staddr, &stsize);
391 info->stack_start_limit = staddr;
392 info->stack_end = staddr + stsize;
394 info->stackdata = g_byte_array_new ();
396 info->internal_thread_gchandle = G_MAXUINT32;
398 info->profiler_signal_ack = 1;
400 mono_threads_suspend_register (info);
402 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
404 if (threads_callbacks.thread_attach) {
405 if (!threads_callbacks.thread_attach (info)) {
406 // g_warning ("thread registation failed\n");
407 mono_native_tls_set_value (thread_info_key, NULL);
413 Transition it before taking any locks or publishing itself to reduce the chance
414 of others witnessing a detached thread.
415 We can reasonably expect that until this thread gets published, no other thread will
416 try to manipulate it.
418 mono_threads_transition_attach (info);
419 mono_thread_info_suspend_lock ();
420 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
421 result = mono_thread_info_insert (info);
423 mono_thread_info_suspend_unlock ();
429 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
432 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
435 unregister_thread (void *arg)
437 gpointer gc_unsafe_stackdata;
438 MonoThreadInfo *info;
443 info = (MonoThreadInfo *) arg;
445 g_assert (mono_thread_info_is_current (info));
446 g_assert (mono_thread_info_is_live (info));
448 /* Pump the HP queue while the thread is alive.*/
449 mono_thread_hazardous_try_free_some ();
451 small_id = info->small_id;
453 /* We only enter the GC unsafe region, as when exiting this function, the thread
454 * will be detached, and the current MonoThreadInfo* will be destroyed. */
455 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
457 THREADS_DEBUG ("unregistering info %p\n", info);
459 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
462 * TLS destruction order is not reliable so small_id might be cleaned up
465 #ifndef HAVE_KW_THREAD
466 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
469 /* we need to duplicate it, as the info->handle is going
470 * to be closed when unregistering from the platform */
471 handle = mono_threads_open_thread_handle (info->handle);
474 First perform the callback that requires no locks.
475 This callback has the potential of taking other locks, so we do it before.
476 After it completes, the thread remains functional.
478 if (threads_callbacks.thread_detach)
479 threads_callbacks.thread_detach (info);
481 mono_thread_info_suspend_lock_with_info (info);
484 Now perform the callback that must be done under locks.
485 This will render the thread useless and non-suspendable, so it must
486 be done while holding the suspend lock to give no other thread chance
489 if (threads_callbacks.thread_detach_with_lock)
490 threads_callbacks.thread_detach_with_lock (info);
492 /* The thread is no longer active, so unref its handle */
493 mono_threads_close_thread_handle (info->handle);
496 result = mono_thread_info_remove (info);
498 mono_threads_transition_detach (info);
500 mono_thread_info_suspend_unlock ();
502 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
504 /*now it's safe to free the thread info.*/
505 mono_thread_hazardous_try_free (info, free_thread_info);
507 mono_thread_small_id_free (small_id);
509 mono_threads_signal_thread_handle (handle);
511 mono_threads_close_thread_handle (handle);
513 mono_native_tls_set_value (thread_info_key, NULL);
517 thread_exited_dtor (void *arg)
519 #if defined(__MACH__)
521 * Since we use pthread dtors to clean up thread data, if a thread
522 * is attached to the runtime by another pthread dtor after our dtor
523 * has ran, it will never be detached, leading to various problems
524 * since the thread ids etc. will be reused while they are still in
525 * the threads hashtables etc.
526 * Dtors are called in a loop until all user tls entries are 0,
527 * but the loop has a maximum count (4), so if we set the tls
528 * variable every time, it will remain set when system tls dtors
529 * are ran. This allows mono_thread_info_is_exiting () to detect
530 * whenever the thread is exiting, even if it is executed from a
531 * system tls dtor (i.e. obj-c dealloc methods).
533 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
538 mono_thread_info_current_unchecked (void)
540 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
545 mono_thread_info_current (void)
547 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
551 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
554 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
555 The way to distinguish between before, during and after cleanup is the following:
557 -If the TLS key is set, cleanup has not begun;
558 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
559 -If the thread is nowhere to be found, cleanup has finished.
561 We cannot function after cleanup since there's no way to ensure what will happen.
565 /*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 */
566 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
572 * mono_thread_info_get_small_id
574 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
575 * identifying the current thread. Returns -1 if the current thread doesn't have
576 * a small ID assigned.
578 * To ensure that the calling thread has a small ID assigned, call either
579 * mono_thread_info_attach or mono_thread_info_register_small_id.
582 mono_thread_info_get_small_id (void)
584 #ifdef HAVE_KW_THREAD
587 gpointer val = mono_native_tls_get_value (small_id_key);
590 return GPOINTER_TO_INT (val) - 1;
595 mono_thread_info_list_head (void)
601 * mono_threads_attach_tools_thread
603 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
605 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
606 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
609 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
610 * doing things like resolving backtraces in their background processing thread.
613 mono_threads_attach_tools_thread (void)
615 MonoThreadInfo *info;
617 /* Must only be called once */
618 g_assert (!mono_native_tls_get_value (thread_info_key));
620 while (!mono_threads_inited) {
621 mono_thread_info_usleep (10);
624 info = mono_thread_info_attach ();
627 info->tools_thread = TRUE;
631 mono_thread_info_attach (void)
633 MonoThreadInfo *info;
636 if (!mono_threads_inited)
638 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
639 * thread is created before an embedding API user initialized Mono. */
640 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
645 g_assert (mono_threads_inited);
647 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
649 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
650 THREADS_DEBUG ("attaching %p\n", info);
651 if (!register_thread (info)) {
661 mono_thread_info_detach (void)
663 MonoThreadInfo *info;
666 if (!mono_threads_inited)
668 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
669 * is created before an embedding API user initialized Mono. */
670 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
675 g_assert (mono_threads_inited);
677 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
679 THREADS_DEBUG ("detaching %p\n", info);
680 unregister_thread (info);
685 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
689 if (info->internal_thread_gchandle == G_MAXUINT32)
692 *gchandle = info->internal_thread_gchandle;
697 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
700 g_assert (gchandle != G_MAXUINT32);
701 info->internal_thread_gchandle = gchandle;
705 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
708 info->internal_thread_gchandle = G_MAXUINT32;
712 * mono_thread_info_is_exiting:
714 * Return whenever the current thread is exiting, i.e. it is running pthread
718 mono_thread_info_is_exiting (void)
720 #if defined(__MACH__)
721 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
729 thread_info_key_dtor (void *arg)
731 /* Put the MonoThreadInfo back for the duration of the
732 * unregister code. In some circumstances the thread needs to
733 * take the GC lock which may block which requires a coop
734 * state transition. */
735 mono_native_tls_set_value (thread_info_key, arg);
736 unregister_thread (arg);
737 mono_native_tls_set_value (thread_info_key, NULL);
742 mono_thread_info_init (size_t info_size)
745 thread_info_size = info_size;
748 res = mono_native_tls_alloc (&thread_info_key, NULL);
749 res = mono_native_tls_alloc (&thread_exited_key, NULL);
751 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
752 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
757 #ifndef HAVE_KW_THREAD
758 res = mono_native_tls_alloc (&small_id_key, NULL);
762 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
764 long threshold = strtol(sleepLimit, NULL, 10);
765 if ((errno == 0) && (threshold >= 40)) {
766 sleepAbortDuration = threshold;
767 sleepWarnDuration = threshold / 20;
769 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
773 mono_os_sem_init (&global_suspend_semaphore, 1);
774 mono_os_sem_init (&suspend_semaphore, 0);
775 mono_os_mutex_init (&join_mutex);
777 mono_lls_init (&thread_list, NULL);
778 mono_thread_smr_init ();
779 mono_threads_suspend_init ();
780 mono_threads_coop_init ();
781 mono_threads_platform_init ();
783 #if defined(__MACH__)
784 mono_mach_init (thread_info_key);
787 mono_threads_inited = TRUE;
789 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
793 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
795 threads_callbacks = *callbacks;
799 mono_thread_info_signals_init (void)
801 mono_threads_suspend_init_signals ();
805 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
807 runtime_callbacks = *callbacks;
810 MonoThreadInfoRuntimeCallbacks *
811 mono_threads_get_runtime_callbacks (void)
813 return &runtime_callbacks;
817 mono_thread_info_core_resume (MonoThreadInfo *info)
819 gboolean res = FALSE;
821 switch (mono_threads_transition_request_resume (info)) {
828 case ResumeInitSelfResume:
829 resume_self_suspended (info);
832 case ResumeInitAsyncResume:
833 resume_async_suspended (info);
836 case ResumeInitBlockingResume:
837 resume_blocking_suspended (info);
846 mono_thread_info_resume (MonoNativeThreadId tid)
848 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
849 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
850 MonoThreadInfo *info;
852 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
854 mono_thread_info_suspend_lock ();
856 info = mono_thread_info_lookup (tid); /*info on HP1*/
862 result = mono_thread_info_core_resume (info);
864 //Wait for the pending resume to finish
865 mono_threads_wait_pending_operations ();
868 mono_thread_info_suspend_unlock ();
869 mono_hazard_pointer_clear (hp, 1);
874 mono_thread_info_begin_suspend (MonoThreadInfo *info)
876 switch (mono_threads_transition_request_async_suspension (info)) {
877 case AsyncSuspendAlreadySuspended:
878 case AsyncSuspendBlocking:
880 case AsyncSuspendWait:
881 mono_threads_add_to_pending_operation_set (info);
883 case AsyncSuspendInitSuspend:
884 return begin_async_suspend (info, FALSE);
886 g_assert_not_reached ();
891 mono_thread_info_begin_resume (MonoThreadInfo *info)
893 return mono_thread_info_core_resume (info);
897 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
898 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
901 is_thread_in_critical_region (MonoThreadInfo *info)
903 gpointer stack_start;
904 MonoThreadUnwindState *state;
906 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
909 /* Are we inside a system critical region? */
910 if (info->inside_critical_region)
913 /* Are we inside a GC critical region? */
914 if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
918 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
919 state = mono_thread_info_get_suspend_state (info);
920 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
923 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
924 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
925 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
928 if (threads_callbacks.ip_in_critical_region)
929 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
935 mono_thread_info_in_critical_location (MonoThreadInfo *info)
937 return is_thread_in_critical_region (info);
941 The return value is only valid until a matching mono_thread_info_resume is called
943 static MonoThreadInfo*
944 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
946 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
947 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
951 switch (mono_threads_transition_request_async_suspension (info)) {
952 case AsyncSuspendAlreadySuspended:
953 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
955 case AsyncSuspendWait:
956 mono_threads_add_to_pending_operation_set (info);
958 case AsyncSuspendInitSuspend:
959 if (!begin_async_suspend (info, interrupt_kernel)) {
960 mono_hazard_pointer_clear (hp, 1);
964 case AsyncSuspendBlocking:
965 if (interrupt_kernel)
966 mono_threads_suspend_abort_syscall (info);
970 g_assert_not_reached ();
973 //Wait for the pending suspend to finish
974 mono_threads_wait_pending_operations ();
976 if (!check_async_suspend (info)) {
977 mono_thread_info_core_resume (info);
978 mono_threads_wait_pending_operations ();
979 mono_hazard_pointer_clear (hp, 1);
985 static MonoThreadInfo*
986 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
988 MonoThreadInfo *info = NULL;
989 int sleep_duration = 0;
991 if (!(info = suspend_sync (id, interrupt_kernel))) {
992 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
996 /*WARNING: We now are in interrupt context until we resume the thread. */
997 if (!is_thread_in_critical_region (info))
1000 if (!mono_thread_info_core_resume (info)) {
1001 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1004 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
1006 /* Wait for the pending resume to finish */
1007 mono_threads_wait_pending_operations ();
1009 if (sleep_duration == 0)
1010 mono_thread_info_yield ();
1012 g_usleep (sleep_duration);
1014 sleep_duration += 10;
1020 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
1023 MonoThreadInfo *info = NULL;
1024 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1026 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
1027 /*FIXME: unify this with self-suspend*/
1028 g_assert (id != mono_native_thread_id_get ());
1030 /* This can block during stw */
1031 mono_thread_info_suspend_lock ();
1032 mono_threads_begin_global_suspend ();
1034 info = suspend_sync_nolock (id, interrupt_kernel);
1038 switch (result = callback (info, user_data)) {
1039 case MonoResumeThread:
1040 mono_hazard_pointer_set (hp, 1, info);
1041 mono_thread_info_core_resume (info);
1042 mono_threads_wait_pending_operations ();
1045 g_assert (!mono_threads_is_coop_enabled ());
1048 g_error ("Invalid suspend_and_run callback return value %d", result);
1052 mono_hazard_pointer_clear (hp, 1);
1053 mono_threads_end_global_suspend ();
1054 mono_thread_info_suspend_unlock ();
1058 Inject an assynchronous call into the target thread. The target thread must be suspended and
1059 only a single async call can be setup for a given suspend cycle.
1060 This async call must cause stack unwinding as the current implementation doesn't save enough state
1061 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1062 currently used only to deliver exceptions.
1065 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1067 if (!mono_threads_is_coop_enabled ()) {
1068 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1069 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1070 * region or entering a gc unsafe region */
1071 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1073 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1074 g_assert (!info->async_target);
1075 info->async_target = target_func;
1076 /* This is not GC tracked */
1077 info->user_data = user_data;
1081 The suspend lock is held during any suspend in progress.
1082 A GC that has safepoints must take this lock as part of its
1083 STW to make sure no unsafe pending suspend is in progress.
1087 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1090 g_assert (mono_thread_info_is_current (info));
1091 g_assert (mono_thread_info_is_live (info));
1093 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1095 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1096 g_assert (res != -1);
1098 MONO_EXIT_GC_SAFE_WITH_INFO;
1102 mono_thread_info_suspend_lock (void)
1104 MonoThreadInfo *info;
1107 info = mono_thread_info_current_unchecked ();
1108 if (info && mono_thread_info_is_live (info)) {
1109 mono_thread_info_suspend_lock_with_info (info);
1113 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1114 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1116 res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1117 g_assert (res != -1);
1121 mono_thread_info_suspend_unlock (void)
1123 mono_os_sem_post (&global_suspend_semaphore);
1127 * This is a very specific function whose only purpose is to
1128 * break a given thread from socket syscalls.
1130 * This only exists because linux won't fail a call to connect
1131 * if the underlying is closed.
1133 * TODO We should cleanup and unify this with the other syscall abort
1137 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1139 MonoThreadHazardPointers *hp;
1140 MonoThreadInfo *info;
1142 if (tid == mono_native_thread_id_get ())
1145 hp = mono_hazard_pointer_get ();
1146 info = mono_thread_info_lookup (tid);
1150 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1151 mono_hazard_pointer_clear (hp, 1);
1155 mono_thread_info_suspend_lock ();
1156 mono_threads_begin_global_suspend ();
1158 mono_threads_suspend_abort_syscall (info);
1159 mono_threads_wait_pending_operations ();
1161 mono_hazard_pointer_clear (hp, 1);
1163 mono_threads_end_global_suspend ();
1164 mono_thread_info_suspend_unlock ();
1168 * mono_thread_info_set_is_async_context:
1170 * Set whenever the current thread is in an async context. Some runtime functions might behave
1171 * differently while in an async context in order to be async safe.
1174 mono_thread_info_set_is_async_context (gboolean async_context)
1176 MonoThreadInfo *info = mono_thread_info_current ();
1179 info->is_async_context = async_context;
1183 mono_thread_info_is_async_context (void)
1185 MonoThreadInfo *info = mono_thread_info_current ();
1188 return info->is_async_context;
1194 * mono_thread_info_get_stack_bounds:
1196 * Return the address and size of the current threads stack. Return NULL as the
1197 * stack address if the stack address cannot be determined.
1200 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1202 guint8 *current = (guint8 *)&stsize;
1203 mono_threads_platform_get_stack_bounds (staddr, stsize);
1207 /* Sanity check the result */
1208 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1210 /* When running under emacs, sometimes staddr is not aligned to a page size */
1211 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1215 mono_thread_info_yield (void)
1217 return mono_threads_platform_yield ();
1220 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1221 static MonoCoopMutex sleep_mutex;
1222 static MonoCoopCond sleep_cond;
1225 sleep_initialize (void)
1227 mono_coop_mutex_init (&sleep_mutex);
1228 mono_coop_cond_init (&sleep_cond);
1232 sleep_interrupt (gpointer data)
1234 mono_coop_mutex_lock (&sleep_mutex);
1235 mono_coop_cond_broadcast (&sleep_cond);
1236 mono_coop_mutex_unlock (&sleep_mutex);
1239 static inline guint32
1240 sleep_interruptable (guint32 ms, gboolean *alerted)
1244 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1249 if (ms != MONO_INFINITE_WAIT)
1250 end = mono_msec_ticks() + ms;
1252 mono_lazy_initialize (&sleep_init, sleep_initialize);
1254 mono_coop_mutex_lock (&sleep_mutex);
1257 if (ms != MONO_INFINITE_WAIT) {
1258 now = mono_msec_ticks();
1263 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1265 mono_coop_mutex_unlock (&sleep_mutex);
1266 return WAIT_IO_COMPLETION;
1269 if (ms != MONO_INFINITE_WAIT)
1270 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1272 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1274 mono_thread_info_uninstall_interrupt (alerted);
1276 mono_coop_mutex_unlock (&sleep_mutex);
1277 return WAIT_IO_COMPLETION;
1281 mono_coop_mutex_unlock (&sleep_mutex);
1287 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1290 MonoThreadInfo *info;
1292 mono_thread_info_yield ();
1294 info = mono_thread_info_current ();
1295 if (info && mono_thread_info_is_interrupt_state (info))
1296 return WAIT_IO_COMPLETION;
1302 return sleep_interruptable (ms, alerted);
1306 if (ms == MONO_INFINITE_WAIT) {
1309 Sleep (G_MAXUINT32);
1311 sleep (G_MAXUINT32);
1316 #if defined (__linux__) && !defined(HOST_ANDROID)
1317 struct timespec start, target;
1319 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1320 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1321 g_assert (ret == 0);
1324 target.tv_sec += ms / 1000;
1325 target.tv_nsec += (ms % 1000) * 1000000;
1326 if (target.tv_nsec > 999999999) {
1327 target.tv_nsec -= 999999999;
1332 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1337 struct timespec req, rem;
1339 req.tv_sec = ms / 1000;
1340 req.tv_nsec = (ms % 1000) * 1000000;
1343 memset (&rem, 0, sizeof (rem));
1344 ret = nanosleep (&req, &rem);
1346 #endif /* __linux__ */
1355 mono_thread_info_usleep (guint64 us)
1364 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1366 return ((MonoThreadInfo*)info)->tls [key];
1370 * mono_threads_info_tls_set:
1372 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1373 * values of TLS variables for threads other than the current thread.
1374 * This should only be used for infrequently changing TLS variables, and it should
1375 * be paired with setting the real TLS variable since this provides no GC tracking.
1378 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1380 ((MonoThreadInfo*)info)->tls [key] = value;
1384 * mono_thread_info_exit:
1386 * Exit the current thread.
1387 * This function doesn't return.
1390 mono_thread_info_exit (gsize exit_code)
1392 mono_thread_info_detach ();
1394 mono_threads_platform_exit (0);
1398 * mono_threads_open_thread_handle:
1400 * Duplicate the handle. The handle needs to be closed by calling
1401 * mono_threads_close_thread_handle () when it is no longer needed.
1404 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1406 return mono_refcount_inc (thread_handle);
1410 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1412 mono_refcount_dec (thread_handle);
1416 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1418 mono_os_event_set (&thread_handle->event);
1421 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1423 struct _MonoThreadInfoInterruptToken {
1424 void (*callback) (gpointer data);
1429 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1431 * - @callback: must be able to be called from another thread and always cancel the wait
1432 * - @data: passed to the callback
1433 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1434 * if set to TRUE, it must mean that the thread is in interrupted state
1437 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1439 MonoThreadInfo *info;
1440 MonoThreadInfoInterruptToken *previous_token, *token;
1442 g_assert (callback);
1444 g_assert (interrupted);
1445 *interrupted = FALSE;
1447 info = mono_thread_info_current ();
1450 /* The memory of this token can be freed at 2 places:
1451 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1452 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1453 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1454 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1455 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1456 token->callback = callback;
1459 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1461 if (previous_token) {
1462 if (previous_token != INTERRUPT_STATE)
1463 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1467 *interrupted = TRUE;
1470 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1471 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1475 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1477 MonoThreadInfo *info;
1478 MonoThreadInfoInterruptToken *previous_token;
1480 g_assert (interrupted);
1481 *interrupted = FALSE;
1483 info = mono_thread_info_current ();
1486 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1488 /* only the installer can uninstall the token */
1489 g_assert (previous_token);
1491 if (previous_token == INTERRUPT_STATE) {
1492 /* if it is interrupted, then it is going to be freed in finish interrupt */
1493 *interrupted = TRUE;
1495 g_free (previous_token);
1498 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1499 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1502 static MonoThreadInfoInterruptToken*
1503 set_interrupt_state (MonoThreadInfo *info)
1505 MonoThreadInfoInterruptToken *token, *previous_token;
1509 /* Atomically obtain the token the thread is
1510 * waiting on, and change it to a flag value. */
1513 previous_token = info->interrupt_token;
1515 /* Already interrupted */
1516 if (previous_token == INTERRUPT_STATE) {
1521 token = previous_token;
1522 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1528 * mono_thread_info_prepare_interrupt:
1530 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1531 * - if the thread calls one of the WaitFor functions, the function will return with
1532 * WAIT_IO_COMPLETION instead of waiting
1533 * - if the thread was waiting when this function was called, the wait will be broken
1535 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1536 * didn't receive the interrupt signal yet, in this case it should call the wait function
1537 * again. This essentially means that the target thread will busy wait until it is ready to
1538 * process the interruption.
1540 MonoThreadInfoInterruptToken*
1541 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1543 MonoThreadInfoInterruptToken *token;
1545 token = set_interrupt_state (info);
1547 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1548 mono_thread_info_get_tid (info), token);
1554 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1556 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1561 g_assert (token->callback);
1563 token->callback (token->data);
1569 mono_thread_info_self_interrupt (void)
1571 MonoThreadInfo *info;
1572 MonoThreadInfoInterruptToken *token;
1574 info = mono_thread_info_current ();
1577 token = set_interrupt_state (info);
1580 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1581 mono_thread_info_get_tid (info));
1584 /* Clear the interrupted flag of the current thread, set with
1585 * mono_thread_info_self_interrupt, so it can wait again */
1587 mono_thread_info_clear_self_interrupt ()
1589 MonoThreadInfo *info;
1590 MonoThreadInfoInterruptToken *previous_token;
1592 info = mono_thread_info_current ();
1595 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1596 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1598 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1602 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1605 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1609 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1613 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1614 g_string_append_printf (text, "not waiting");
1615 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1616 g_string_append_printf (text, "interrupted state");
1618 g_string_append_printf (text, "waiting");
1622 mono_thread_info_is_current (MonoThreadInfo *info)
1624 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1627 MonoThreadInfoWaitRet
1628 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1630 MonoOSEventWaitRet res;
1632 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1633 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1634 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1635 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1636 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1637 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1638 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1640 g_error ("%s: unknown res value %d", __func__, res);
1643 MonoThreadInfoWaitRet
1644 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1646 MonoOSEventWaitRet res;
1647 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1650 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1651 if (background_change_event)
1652 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1654 for (i = 0; i < nhandles; ++i)
1655 thread_events [i] = &thread_handles [i]->event;
1657 if (background_change_event)
1658 thread_events [nhandles ++] = background_change_event;
1660 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1661 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1662 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1663 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1664 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1665 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1666 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1668 g_error ("%s: unknown res value %d", __func__, res);
1672 * mono_threads_join_mutex:
1674 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1675 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1676 * The code inside the lock should not block.
1679 mono_threads_join_lock (void)
1682 mono_os_mutex_lock (&join_mutex);
1687 mono_threads_join_unlock (void)
1690 mono_os_mutex_unlock (&join_mutex);