2 * mono-threads.c: Low-level threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 /* enable pthread extensions */
16 #define _DARWIN_C_SOURCE
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-os-semaphore.h>
21 #include <mono/utils/mono-threads.h>
22 #include <mono/utils/mono-tls.h>
23 #include <mono/utils/hazard-pointer.h>
24 #include <mono/utils/mono-memory-model.h>
25 #include <mono/utils/mono-mmap.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-time.h>
28 #include <mono/utils/mono-lazy-init.h>
29 #include <mono/utils/mono-coop-mutex.h>
30 #include <mono/utils/mono-coop-semaphore.h>
31 #include <mono/utils/mono-threads-coop.h>
32 #include <mono/utils/mono-threads-debug.h>
33 #include <mono/utils/os-event.h>
34 #include <mono/utils/w32api.h>
39 #include <mono/utils/mach-support.h>
43 Mutex that makes sure only a single thread can be suspending others.
44 Suspend is a very racy operation since it requires restarting until
45 the target thread is not on an unsafe region.
47 We could implement this using critical regions, but would be much much
48 harder for an operation that is hardly performance critical.
50 The GC has to acquire this lock before starting a STW to make sure
51 a runtime suspend won't make it wronly see a thread in a safepoint
52 when it is in fact not.
54 This has to be a naked locking primitive, and not a coop aware one, as
55 it needs to be usable when destroying thread_info_key, the TLS key for
56 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
57 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
58 to an assertion error. We then simply switch state manually in
59 mono_thread_info_suspend_lock_with_info.
61 static MonoSemType global_suspend_semaphore;
63 static size_t thread_info_size;
64 static MonoThreadInfoCallbacks threads_callbacks;
65 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
66 static MonoNativeTlsKey thread_info_key, thread_exited_key;
68 static __thread guint32 tls_small_id;
70 static MonoNativeTlsKey small_id_key;
72 static MonoLinkedListSet thread_list;
73 static gboolean mono_threads_inited = FALSE;
75 static MonoSemType suspend_semaphore;
76 static size_t pending_suspends;
78 static mono_mutex_t join_mutex;
80 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
83 #define SLEEP_DURATION_BEFORE_WARNING (50)
85 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
87 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
88 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
90 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
93 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
95 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
96 InterlockedIncrement (&abort_posts);
97 mono_os_sem_post (&suspend_semaphore);
101 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
103 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
104 InterlockedIncrement (&suspend_posts);
105 mono_os_sem_post (&suspend_semaphore);
109 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
111 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
112 InterlockedIncrement (&resume_posts);
113 mono_os_sem_post (&suspend_semaphore);
117 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
119 if (mono_threads_is_coop_enabled ()) {
120 /* There's nothing else to do after we async request the thread to suspend */
121 mono_threads_add_to_pending_operation_set (info);
125 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
129 check_async_suspend (MonoThreadInfo *info)
131 if (mono_threads_is_coop_enabled ()) {
132 /* Async suspend can't async fail on coop */
136 return mono_threads_suspend_check_suspend_result (info);
140 resume_async_suspended (MonoThreadInfo *info)
142 if (mono_threads_is_coop_enabled ())
143 g_assert_not_reached ();
145 g_assert (mono_threads_suspend_begin_async_resume (info));
149 resume_self_suspended (MonoThreadInfo* info)
151 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
152 mono_os_sem_post (&info->resume_semaphore);
156 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
159 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
160 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
161 g_assert (res != -1);
165 resume_blocking_suspended (MonoThreadInfo* info)
167 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
168 mono_os_sem_post (&info->resume_semaphore);
172 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
174 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
176 InterlockedIncrement (&pending_ops);
180 mono_threads_begin_global_suspend (void)
182 size_t ps = pending_suspends;
183 if (G_UNLIKELY (ps != 0))
184 g_error ("pending_suspends = %d, but must be 0", ps);
185 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,
186 abort_posts, waits_done, pending_ops);
187 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
188 mono_threads_coop_begin_global_suspend ();
192 mono_threads_end_global_suspend (void)
194 size_t ps = pending_suspends;
195 if (G_UNLIKELY (ps != 0))
196 g_error ("pending_suspends = %d, but must be 0", ps);
197 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
198 abort_posts, waits_done, pending_ops);
199 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
200 mono_threads_coop_end_global_suspend ();
206 MonoThreadInfo *cur = mono_thread_info_current ();
208 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
217 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
219 FOREACH_THREAD_SAFE (info) {
221 char thread_name [256] = { 0 };
222 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
224 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" : "" );
226 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" : "" );
228 } FOREACH_THREAD_SAFE_END
232 mono_threads_wait_pending_operations (void)
235 int c = pending_suspends;
237 /* Wait threads to park */
238 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
239 if (pending_suspends) {
240 MonoStopwatch suspension_time;
241 mono_stopwatch_start (&suspension_time);
242 for (i = 0; i < pending_suspends; ++i) {
243 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
244 InterlockedIncrement (&waits_done);
245 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
247 mono_stopwatch_stop (&suspension_time);
251 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
252 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
254 mono_stopwatch_stop (&suspension_time);
255 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
259 pending_suspends = 0;
265 //Thread initialization code
268 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
271 mono_hazard_pointer_clear (hp, 0);
273 mono_hazard_pointer_clear (hp, 1);
275 mono_hazard_pointer_clear (hp, 2);
279 If return non null Hazard Pointer 1 holds the return value.
282 mono_thread_info_lookup (MonoNativeThreadId id)
284 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
286 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
287 mono_hazard_pointer_clear_all (hp, -1);
291 mono_hazard_pointer_clear_all (hp, 1);
292 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
296 mono_thread_info_insert (MonoThreadInfo *info)
298 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
300 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
301 mono_hazard_pointer_clear_all (hp, -1);
305 mono_hazard_pointer_clear_all (hp, -1);
310 mono_thread_info_remove (MonoThreadInfo *info)
312 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
315 THREADS_DEBUG ("removing info %p\n", info);
316 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
317 mono_hazard_pointer_clear_all (hp, -1);
322 free_thread_info (gpointer mem)
324 MonoThreadInfo *info = (MonoThreadInfo *) mem;
326 mono_os_sem_destroy (&info->resume_semaphore);
327 mono_threads_suspend_free (info);
333 mono_thread_info_register_small_id (void)
335 int small_id = mono_thread_small_id_alloc ();
336 #ifdef HAVE_KW_THREAD
337 tls_small_id = small_id;
339 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
345 thread_handle_destroy (gpointer data)
347 MonoThreadHandle *thread_handle;
349 thread_handle = (MonoThreadHandle*) data;
351 mono_os_event_destroy (&thread_handle->event);
352 g_free (thread_handle);
356 register_thread (MonoThreadInfo *info, gpointer baseptr)
359 guint8 *staddr = NULL;
360 int small_id = mono_thread_info_register_small_id ();
362 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
363 info->small_id = small_id;
365 info->handle = g_new0 (MonoThreadHandle, 1);
366 mono_refcount_init (info->handle, thread_handle_destroy);
367 mono_os_event_init (&info->handle->event, FALSE);
369 mono_os_sem_init (&info->resume_semaphore, 0);
371 /*set TLS early so SMR works */
372 mono_native_tls_set_value (thread_info_key, info);
374 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
376 if (threads_callbacks.thread_register) {
377 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
378 // g_warning ("thread registation failed\n");
379 mono_native_tls_set_value (thread_info_key, NULL);
385 mono_thread_info_get_stack_bounds (&staddr, &stsize);
388 info->stack_start_limit = staddr;
389 info->stack_end = staddr + stsize;
391 info->stackdata = g_byte_array_new ();
393 mono_threads_suspend_register (info);
396 Transition it before taking any locks or publishing itself to reduce the chance
397 of others witnessing a detached thread.
398 We can reasonably expect that until this thread gets published, no other thread will
399 try to manipulate it.
401 mono_threads_transition_attach (info);
402 mono_thread_info_suspend_lock ();
403 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
404 result = mono_thread_info_insert (info);
406 mono_thread_info_suspend_unlock ();
411 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
414 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
417 unregister_thread (void *arg)
419 gpointer gc_unsafe_stackdata;
420 MonoThreadInfo *info;
425 info = (MonoThreadInfo *) arg;
427 g_assert (mono_thread_info_is_current (info));
428 g_assert (mono_thread_info_is_live (info));
430 /* Pump the HP queue while the thread is alive.*/
431 mono_thread_hazardous_try_free_some ();
433 small_id = info->small_id;
435 /* We only enter the GC unsafe region, as when exiting this function, the thread
436 * will be detached, and the current MonoThreadInfo* will be destroyed. */
437 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
439 THREADS_DEBUG ("unregistering info %p\n", info);
441 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
444 * TLS destruction order is not reliable so small_id might be cleaned up
447 #ifndef HAVE_KW_THREAD
448 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
451 /* we need to duplicate it, as the info->handle is going
452 * to be closed when unregistering from the platform */
453 handle = mono_threads_open_thread_handle (info->handle);
456 First perform the callback that requires no locks.
457 This callback has the potential of taking other locks, so we do it before.
458 After it completes, the thread remains functional.
460 if (threads_callbacks.thread_detach)
461 threads_callbacks.thread_detach (info);
463 mono_thread_info_suspend_lock_with_info (info);
466 Now perform the callback that must be done under locks.
467 This will render the thread useless and non-suspendable, so it must
468 be done while holding the suspend lock to give no other thread chance
471 if (threads_callbacks.thread_unregister)
472 threads_callbacks.thread_unregister (info);
474 /* The thread is no longer active, so unref its handle */
475 mono_threads_close_thread_handle (info->handle);
478 result = mono_thread_info_remove (info);
480 mono_threads_transition_detach (info);
482 mono_thread_info_suspend_unlock ();
484 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
486 /*now it's safe to free the thread info.*/
487 mono_thread_hazardous_try_free (info, free_thread_info);
489 mono_thread_small_id_free (small_id);
491 mono_threads_signal_thread_handle (handle);
493 mono_threads_close_thread_handle (handle);
497 thread_exited_dtor (void *arg)
499 #if defined(__MACH__)
501 * Since we use pthread dtors to clean up thread data, if a thread
502 * is attached to the runtime by another pthread dtor after our dtor
503 * has ran, it will never be detached, leading to various problems
504 * since the thread ids etc. will be reused while they are still in
505 * the threads hashtables etc.
506 * Dtors are called in a loop until all user tls entries are 0,
507 * but the loop has a maximum count (4), so if we set the tls
508 * variable every time, it will remain set when system tls dtors
509 * are ran. This allows mono_thread_info_is_exiting () to detect
510 * whenever the thread is exiting, even if it is executed from a
511 * system tls dtor (i.e. obj-c dealloc methods).
513 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
518 mono_thread_info_current_unchecked (void)
520 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
525 mono_thread_info_current (void)
527 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
531 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
534 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
535 The way to distinguish between before, during and after cleanup is the following:
537 -If the TLS key is set, cleanup has not begun;
538 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
539 -If the thread is nowhere to be found, cleanup has finished.
541 We cannot function after cleanup since there's no way to ensure what will happen.
545 /*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 */
546 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
552 mono_thread_info_get_small_id (void)
554 #ifdef HAVE_KW_THREAD
557 gpointer val = mono_native_tls_get_value (small_id_key);
560 return GPOINTER_TO_INT (val) - 1;
565 mono_thread_info_list_head (void)
571 * mono_threads_attach_tools_thread
573 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
575 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
576 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
579 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
580 * doing things like resolving backtraces in their background processing thread.
583 mono_threads_attach_tools_thread (void)
586 MonoThreadInfo *info;
588 /* Must only be called once */
589 g_assert (!mono_native_tls_get_value (thread_info_key));
591 while (!mono_threads_inited) {
592 mono_thread_info_usleep (10);
595 info = mono_thread_info_attach (&dummy);
598 info->tools_thread = TRUE;
602 mono_thread_info_attach (void *baseptr)
604 MonoThreadInfo *info;
605 if (!mono_threads_inited)
608 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
609 * thread is created before an embedding API user initialized Mono. */
610 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
613 g_assert (mono_threads_inited);
616 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
618 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
619 THREADS_DEBUG ("attaching %p\n", info);
620 if (!register_thread (info, baseptr))
622 } else if (threads_callbacks.thread_attach) {
623 threads_callbacks.thread_attach (info);
629 mono_thread_info_detach (void)
631 MonoThreadInfo *info;
632 if (!mono_threads_inited)
634 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
635 * is created before an embedding API user initialized Mono. */
636 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
639 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
641 THREADS_DEBUG ("detaching %p\n", info);
642 unregister_thread (info);
643 mono_native_tls_set_value (thread_info_key, NULL);
648 * mono_thread_info_is_exiting:
650 * Return whenever the current thread is exiting, i.e. it is running pthread
654 mono_thread_info_is_exiting (void)
656 #if defined(__MACH__)
657 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
665 thread_info_key_dtor (void *arg)
667 /* Put the MonoThreadInfo back for the duration of the
668 * unregister code. In some circumstances the thread needs to
669 * take the GC lock which may block which requires a coop
670 * state transition. */
671 mono_native_tls_set_value (thread_info_key, arg);
672 unregister_thread (arg);
673 mono_native_tls_set_value (thread_info_key, NULL);
678 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
681 threads_callbacks = *callbacks;
682 thread_info_size = info_size;
683 const char *sleepLimit;
685 res = mono_native_tls_alloc (&thread_info_key, NULL);
686 res = mono_native_tls_alloc (&thread_exited_key, NULL);
688 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
689 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
694 #ifndef HAVE_KW_THREAD
695 res = mono_native_tls_alloc (&small_id_key, NULL);
699 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
701 long threshold = strtol(sleepLimit, NULL, 10);
702 if ((errno == 0) && (threshold >= 40)) {
703 sleepAbortDuration = threshold;
704 sleepWarnDuration = threshold / 20;
706 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
709 mono_os_sem_init (&global_suspend_semaphore, 1);
710 mono_os_sem_init (&suspend_semaphore, 0);
711 mono_os_mutex_init (&join_mutex);
713 mono_lls_init (&thread_list, NULL);
714 mono_thread_smr_init ();
715 mono_threads_suspend_init ();
716 mono_threads_coop_init ();
717 mono_threads_platform_init ();
719 #if defined(__MACH__)
720 mono_mach_init (thread_info_key);
723 mono_threads_inited = TRUE;
725 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
729 mono_threads_signals_init (void)
731 mono_threads_suspend_init_signals ();
735 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
737 runtime_callbacks = *callbacks;
740 MonoThreadInfoRuntimeCallbacks *
741 mono_threads_get_runtime_callbacks (void)
743 return &runtime_callbacks;
747 mono_thread_info_core_resume (MonoThreadInfo *info)
749 gboolean res = FALSE;
751 switch (mono_threads_transition_request_resume (info)) {
758 case ResumeInitSelfResume:
759 resume_self_suspended (info);
762 case ResumeInitAsyncResume:
763 resume_async_suspended (info);
766 case ResumeInitBlockingResume:
767 resume_blocking_suspended (info);
776 mono_thread_info_resume (MonoNativeThreadId tid)
778 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
779 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
780 MonoThreadInfo *info;
782 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
784 mono_thread_info_suspend_lock ();
786 info = mono_thread_info_lookup (tid); /*info on HP1*/
792 result = mono_thread_info_core_resume (info);
794 //Wait for the pending resume to finish
795 mono_threads_wait_pending_operations ();
798 mono_thread_info_suspend_unlock ();
799 mono_hazard_pointer_clear (hp, 1);
804 mono_thread_info_begin_suspend (MonoThreadInfo *info)
806 switch (mono_threads_transition_request_async_suspension (info)) {
807 case AsyncSuspendAlreadySuspended:
808 case AsyncSuspendBlocking:
810 case AsyncSuspendWait:
811 mono_threads_add_to_pending_operation_set (info);
813 case AsyncSuspendInitSuspend:
814 return begin_async_suspend (info, FALSE);
816 g_assert_not_reached ();
821 mono_thread_info_begin_resume (MonoThreadInfo *info)
823 return mono_thread_info_core_resume (info);
827 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
828 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
831 is_thread_in_critical_region (MonoThreadInfo *info)
835 gpointer stack_start;
836 MonoThreadUnwindState *state;
838 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
841 /* Are we inside a system critical region? */
842 if (info->inside_critical_region)
845 /* Are we inside a GC critical region? */
846 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
850 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
851 state = mono_thread_info_get_suspend_state (info);
852 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
855 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
856 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
857 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
860 if (threads_callbacks.ip_in_critical_region)
861 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
863 ji = mono_jit_info_table_find (
864 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
865 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
870 method = mono_jit_info_get_method (ji);
872 return threads_callbacks.mono_method_is_critical (method);
876 mono_thread_info_in_critical_location (MonoThreadInfo *info)
878 return is_thread_in_critical_region (info);
882 The return value is only valid until a matching mono_thread_info_resume is called
884 static MonoThreadInfo*
885 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
887 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
888 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
892 switch (mono_threads_transition_request_async_suspension (info)) {
893 case AsyncSuspendAlreadySuspended:
894 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
896 case AsyncSuspendWait:
897 mono_threads_add_to_pending_operation_set (info);
899 case AsyncSuspendInitSuspend:
900 if (!begin_async_suspend (info, interrupt_kernel)) {
901 mono_hazard_pointer_clear (hp, 1);
905 case AsyncSuspendBlocking:
906 if (interrupt_kernel)
907 mono_threads_suspend_abort_syscall (info);
911 g_assert_not_reached ();
914 //Wait for the pending suspend to finish
915 mono_threads_wait_pending_operations ();
917 if (!check_async_suspend (info)) {
918 mono_thread_info_core_resume (info);
919 mono_threads_wait_pending_operations ();
920 mono_hazard_pointer_clear (hp, 1);
926 static MonoThreadInfo*
927 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
929 MonoThreadInfo *info = NULL;
930 int sleep_duration = 0;
932 if (!(info = suspend_sync (id, interrupt_kernel))) {
933 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
937 /*WARNING: We now are in interrupt context until we resume the thread. */
938 if (!is_thread_in_critical_region (info))
941 if (!mono_thread_info_core_resume (info)) {
942 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
945 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
947 /* Wait for the pending resume to finish */
948 mono_threads_wait_pending_operations ();
950 if (sleep_duration == 0)
951 mono_thread_info_yield ();
953 g_usleep (sleep_duration);
955 sleep_duration += 10;
961 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
964 MonoThreadInfo *info = NULL;
965 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
967 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
968 /*FIXME: unify this with self-suspend*/
969 g_assert (id != mono_native_thread_id_get ());
971 /* This can block during stw */
972 mono_thread_info_suspend_lock ();
973 mono_threads_begin_global_suspend ();
975 info = suspend_sync_nolock (id, interrupt_kernel);
979 switch (result = callback (info, user_data)) {
980 case MonoResumeThread:
981 mono_hazard_pointer_set (hp, 1, info);
982 mono_thread_info_core_resume (info);
983 mono_threads_wait_pending_operations ();
986 g_assert (!mono_threads_is_coop_enabled ());
989 g_error ("Invalid suspend_and_run callback return value %d", result);
993 mono_hazard_pointer_clear (hp, 1);
994 mono_threads_end_global_suspend ();
995 mono_thread_info_suspend_unlock ();
999 Inject an assynchronous call into the target thread. The target thread must be suspended and
1000 only a single async call can be setup for a given suspend cycle.
1001 This async call must cause stack unwinding as the current implementation doesn't save enough state
1002 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1003 currently used only to deliver exceptions.
1006 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1008 if (!mono_threads_is_coop_enabled ()) {
1009 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1010 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1011 * region or entering a gc unsafe region */
1012 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1014 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1015 g_assert (!info->async_target);
1016 info->async_target = target_func;
1017 /* This is not GC tracked */
1018 info->user_data = user_data;
1022 The suspend lock is held during any suspend in progress.
1023 A GC that has safepoints must take this lock as part of its
1024 STW to make sure no unsafe pending suspend is in progress.
1028 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1031 g_assert (mono_thread_info_is_current (info));
1032 g_assert (mono_thread_info_is_live (info));
1034 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1036 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1037 g_assert (res != -1);
1039 MONO_EXIT_GC_SAFE_WITH_INFO;
1043 mono_thread_info_suspend_lock (void)
1045 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1049 mono_thread_info_suspend_unlock (void)
1051 mono_os_sem_post (&global_suspend_semaphore);
1055 * This is a very specific function whose only purpose is to
1056 * break a given thread from socket syscalls.
1058 * This only exists because linux won't fail a call to connect
1059 * if the underlying is closed.
1061 * TODO We should cleanup and unify this with the other syscall abort
1065 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1067 MonoThreadHazardPointers *hp;
1068 MonoThreadInfo *info;
1070 if (tid == mono_native_thread_id_get ())
1073 hp = mono_hazard_pointer_get ();
1074 info = mono_thread_info_lookup (tid);
1078 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1079 mono_hazard_pointer_clear (hp, 1);
1083 mono_thread_info_suspend_lock ();
1084 mono_threads_begin_global_suspend ();
1086 mono_threads_suspend_abort_syscall (info);
1087 mono_threads_wait_pending_operations ();
1089 mono_hazard_pointer_clear (hp, 1);
1091 mono_threads_end_global_suspend ();
1092 mono_thread_info_suspend_unlock ();
1096 * mono_thread_info_set_is_async_context:
1098 * Set whenever the current thread is in an async context. Some runtime functions might behave
1099 * differently while in an async context in order to be async safe.
1102 mono_thread_info_set_is_async_context (gboolean async_context)
1104 MonoThreadInfo *info = mono_thread_info_current ();
1107 info->is_async_context = async_context;
1111 mono_thread_info_is_async_context (void)
1113 MonoThreadInfo *info = mono_thread_info_current ();
1116 return info->is_async_context;
1123 MonoThreadStart start_routine;
1124 gpointer start_routine_arg;
1125 MonoCoopSem registered;
1126 MonoThreadHandle *handle;
1130 create_thread_data_destroy (gpointer data)
1132 CreateThreadData *thread_data;
1134 thread_data = (CreateThreadData*) data;
1136 mono_coop_sem_destroy (&thread_data->registered);
1137 g_free (thread_data);
1141 inner_start_thread (gpointer data)
1143 CreateThreadData *thread_data;
1144 MonoThreadInfo *info;
1145 MonoThreadStart start_routine;
1146 gpointer start_routine_arg;
1147 gsize start_routine_res;
1150 thread_data = (CreateThreadData*) data;
1151 g_assert (thread_data);
1153 start_routine = thread_data->start_routine;
1154 start_routine_arg = thread_data->start_routine_arg;
1156 info = mono_thread_info_attach (&dummy);
1157 info->runtime_thread = TRUE;
1159 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1161 mono_coop_sem_post (&thread_data->registered);
1163 mono_refcount_dec (thread_data);
1165 /* thread_data is not valid anymore */
1168 /* Run the actual main function of the thread */
1169 start_routine_res = start_routine (start_routine_arg);
1171 mono_thread_info_exit (start_routine_res);
1173 g_assert_not_reached ();
1177 * mono_threads_create_thread:
1179 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1180 * Returns: a windows or io-layer handle for the thread.
1183 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1185 CreateThreadData *thread_data;
1187 MonoThreadHandle *ret;
1189 thread_data = g_new0 (CreateThreadData, 1);
1190 mono_refcount_init (thread_data, create_thread_data_destroy);
1191 thread_data->start_routine = start;
1192 thread_data->start_routine_arg = arg;
1193 mono_coop_sem_init (&thread_data->registered, 0);
1195 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
1197 /* ref is not going to be decremented in inner_start_thread */
1198 mono_refcount_dec (thread_data);
1203 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1204 g_assert (res == 0);
1206 ret = thread_data->handle;
1210 mono_refcount_dec (thread_data);
1216 * mono_thread_info_get_stack_bounds:
1218 * Return the address and size of the current threads stack. Return NULL as the
1219 * stack address if the stack address cannot be determined.
1222 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1224 guint8 *current = (guint8 *)&stsize;
1225 mono_threads_platform_get_stack_bounds (staddr, stsize);
1229 /* Sanity check the result */
1230 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1232 /* When running under emacs, sometimes staddr is not aligned to a page size */
1233 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1237 mono_thread_info_yield (void)
1239 return mono_threads_platform_yield ();
1242 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1243 static MonoCoopMutex sleep_mutex;
1244 static MonoCoopCond sleep_cond;
1247 sleep_initialize (void)
1249 mono_coop_mutex_init (&sleep_mutex);
1250 mono_coop_cond_init (&sleep_cond);
1254 sleep_interrupt (gpointer data)
1256 mono_coop_mutex_lock (&sleep_mutex);
1257 mono_coop_cond_broadcast (&sleep_cond);
1258 mono_coop_mutex_unlock (&sleep_mutex);
1261 static inline guint32
1262 sleep_interruptable (guint32 ms, gboolean *alerted)
1266 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1271 if (ms != MONO_INFINITE_WAIT)
1272 end = mono_msec_ticks() + ms;
1274 mono_lazy_initialize (&sleep_init, sleep_initialize);
1276 mono_coop_mutex_lock (&sleep_mutex);
1279 if (ms != MONO_INFINITE_WAIT) {
1280 now = mono_msec_ticks();
1285 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1287 mono_coop_mutex_unlock (&sleep_mutex);
1288 return WAIT_IO_COMPLETION;
1291 if (ms != MONO_INFINITE_WAIT)
1292 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1294 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1296 mono_thread_info_uninstall_interrupt (alerted);
1298 mono_coop_mutex_unlock (&sleep_mutex);
1299 return WAIT_IO_COMPLETION;
1303 mono_coop_mutex_unlock (&sleep_mutex);
1309 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1312 MonoThreadInfo *info;
1314 mono_thread_info_yield ();
1316 info = mono_thread_info_current ();
1317 if (info && mono_thread_info_is_interrupt_state (info))
1318 return WAIT_IO_COMPLETION;
1324 return sleep_interruptable (ms, alerted);
1328 if (ms == MONO_INFINITE_WAIT) {
1331 Sleep (G_MAXUINT32);
1333 sleep (G_MAXUINT32);
1338 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1339 struct timespec start, target;
1341 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1342 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1343 g_assert (ret == 0);
1346 target.tv_sec += ms / 1000;
1347 target.tv_nsec += (ms % 1000) * 1000000;
1348 if (target.tv_nsec > 999999999) {
1349 target.tv_nsec -= 999999999;
1354 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1359 struct timespec req, rem;
1361 req.tv_sec = ms / 1000;
1362 req.tv_nsec = (ms % 1000) * 1000000;
1365 memset (&rem, 0, sizeof (rem));
1366 ret = nanosleep (&req, &rem);
1368 #endif /* __linux__ */
1377 mono_thread_info_usleep (guint64 us)
1386 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1388 return ((MonoThreadInfo*)info)->tls [key];
1392 * mono_threads_info_tls_set:
1394 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1395 * values of TLS variables for threads other than the current thread.
1396 * This should only be used for infrequently changing TLS variables, and it should
1397 * be paired with setting the real TLS variable since this provides no GC tracking.
1400 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1402 ((MonoThreadInfo*)info)->tls [key] = value;
1405 #if defined(__native_client__)
1406 void nacl_shutdown_gc_thread(void);
1410 * mono_thread_info_exit:
1412 * Exit the current thread.
1413 * This function doesn't return.
1416 mono_thread_info_exit (gsize exit_code)
1418 #if defined(__native_client__)
1419 nacl_shutdown_gc_thread();
1422 mono_thread_info_detach ();
1424 mono_threads_platform_exit (0);
1428 * mono_threads_open_thread_handle:
1430 * Duplicate the handle. The handle needs to be closed by calling
1431 * mono_threads_close_thread_handle () when it is no longer needed.
1434 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1436 return mono_refcount_inc (thread_handle);
1440 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1442 mono_refcount_dec (thread_handle);
1446 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1448 mono_os_event_set (&thread_handle->event);
1451 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1453 struct _MonoThreadInfoInterruptToken {
1454 void (*callback) (gpointer data);
1459 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1461 * - @callback: must be able to be called from another thread and always cancel the wait
1462 * - @data: passed to the callback
1463 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1464 * if set to TRUE, it must mean that the thread is in interrupted state
1467 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1469 MonoThreadInfo *info;
1470 MonoThreadInfoInterruptToken *previous_token, *token;
1472 g_assert (callback);
1474 g_assert (interrupted);
1475 *interrupted = FALSE;
1477 info = mono_thread_info_current ();
1480 /* The memory of this token can be freed at 2 places:
1481 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1482 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1483 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1484 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1485 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1486 token->callback = callback;
1489 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1491 if (previous_token) {
1492 if (previous_token != INTERRUPT_STATE)
1493 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1497 *interrupted = TRUE;
1500 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1501 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1505 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1507 MonoThreadInfo *info;
1508 MonoThreadInfoInterruptToken *previous_token;
1510 g_assert (interrupted);
1511 *interrupted = FALSE;
1513 info = mono_thread_info_current ();
1516 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1518 /* only the installer can uninstall the token */
1519 g_assert (previous_token);
1521 if (previous_token == INTERRUPT_STATE) {
1522 /* if it is interrupted, then it is going to be freed in finish interrupt */
1523 *interrupted = TRUE;
1525 g_free (previous_token);
1528 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1529 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1532 static MonoThreadInfoInterruptToken*
1533 set_interrupt_state (MonoThreadInfo *info)
1535 MonoThreadInfoInterruptToken *token, *previous_token;
1539 /* Atomically obtain the token the thread is
1540 * waiting on, and change it to a flag value. */
1543 previous_token = info->interrupt_token;
1545 /* Already interrupted */
1546 if (previous_token == INTERRUPT_STATE) {
1551 token = previous_token;
1552 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1558 * mono_thread_info_prepare_interrupt:
1560 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1561 * - if the thread calls one of the WaitFor functions, the function will return with
1562 * WAIT_IO_COMPLETION instead of waiting
1563 * - if the thread was waiting when this function was called, the wait will be broken
1565 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1566 * didn't receive the interrupt signal yet, in this case it should call the wait function
1567 * again. This essentially means that the target thread will busy wait until it is ready to
1568 * process the interruption.
1570 MonoThreadInfoInterruptToken*
1571 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1573 MonoThreadInfoInterruptToken *token;
1575 token = set_interrupt_state (info);
1577 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1578 mono_thread_info_get_tid (info), token);
1584 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1586 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1591 g_assert (token->callback);
1593 token->callback (token->data);
1599 mono_thread_info_self_interrupt (void)
1601 MonoThreadInfo *info;
1602 MonoThreadInfoInterruptToken *token;
1604 info = mono_thread_info_current ();
1607 token = set_interrupt_state (info);
1610 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1611 mono_thread_info_get_tid (info));
1614 /* Clear the interrupted flag of the current thread, set with
1615 * mono_thread_info_self_interrupt, so it can wait again */
1617 mono_thread_info_clear_self_interrupt ()
1619 MonoThreadInfo *info;
1620 MonoThreadInfoInterruptToken *previous_token;
1622 info = mono_thread_info_current ();
1625 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1626 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1628 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1632 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1635 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1639 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1643 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1644 g_string_append_printf (text, "not waiting");
1645 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1646 g_string_append_printf (text, "interrupted state");
1648 g_string_append_printf (text, "waiting");
1652 mono_thread_info_is_current (MonoThreadInfo *info)
1654 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1657 MonoThreadInfoWaitRet
1658 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1660 MonoOSEventWaitRet res;
1662 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1663 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1664 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1665 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1666 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1667 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1668 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1670 g_error ("%s: unknown res value %d", __func__, res);
1673 MonoThreadInfoWaitRet
1674 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1676 MonoOSEventWaitRet res;
1677 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1680 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1681 if (background_change_event)
1682 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1684 for (i = 0; i < nhandles; ++i)
1685 thread_events [i] = &thread_handles [i]->event;
1687 if (background_change_event)
1688 thread_events [nhandles ++] = background_change_event;
1690 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1691 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1692 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1693 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1694 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1695 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1696 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1698 g_error ("%s: unknown res value %d", __func__, res);
1702 * mono_threads_join_mutex:
1704 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1705 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1706 * The code inside the lock should not block.
1709 mono_threads_join_lock (void)
1712 mono_os_mutex_lock (&join_mutex);
1717 mono_threads_join_unlock (void)
1720 mono_os_mutex_unlock (&join_mutex);