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>
38 #include <mono/utils/mach-support.h>
42 Mutex that makes sure only a single thread can be suspending others.
43 Suspend is a very racy operation since it requires restarting until
44 the target thread is not on an unsafe region.
46 We could implement this using critical regions, but would be much much
47 harder for an operation that is hardly performance critical.
49 The GC has to acquire this lock before starting a STW to make sure
50 a runtime suspend won't make it wronly see a thread in a safepoint
51 when it is in fact not.
53 This has to be a naked locking primitive, and not a coop aware one, as
54 it needs to be usable when destroying thread_info_key, the TLS key for
55 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
56 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
57 to an assertion error. We then simply switch state manually in
58 mono_thread_info_suspend_lock_with_info.
60 static MonoSemType global_suspend_semaphore;
62 static size_t thread_info_size;
63 static MonoThreadInfoCallbacks threads_callbacks;
64 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
65 static MonoNativeTlsKey thread_info_key, thread_exited_key;
67 static __thread guint32 tls_small_id MONO_TLS_FAST;
69 static MonoNativeTlsKey small_id_key;
71 static MonoLinkedListSet thread_list;
72 static gboolean mono_threads_inited = FALSE;
74 static MonoSemType suspend_semaphore;
75 static size_t pending_suspends;
77 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
80 #define SLEEP_DURATION_BEFORE_WARNING (50)
82 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
84 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
85 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
87 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
90 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
92 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
93 InterlockedIncrement (&abort_posts);
94 mono_os_sem_post (&suspend_semaphore);
98 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
100 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
101 InterlockedIncrement (&suspend_posts);
102 mono_os_sem_post (&suspend_semaphore);
106 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
108 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
109 InterlockedIncrement (&resume_posts);
110 mono_os_sem_post (&suspend_semaphore);
114 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
116 if (mono_threads_is_coop_enabled ()) {
117 /* There's nothing else to do after we async request the thread to suspend */
118 mono_threads_add_to_pending_operation_set (info);
122 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
126 check_async_suspend (MonoThreadInfo *info)
128 if (mono_threads_is_coop_enabled ()) {
129 /* Async suspend can't async fail on coop */
133 return mono_threads_suspend_check_suspend_result (info);
137 resume_async_suspended (MonoThreadInfo *info)
139 if (mono_threads_is_coop_enabled ())
140 g_assert_not_reached ();
142 g_assert (mono_threads_suspend_begin_async_resume (info));
146 resume_self_suspended (MonoThreadInfo* info)
148 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
149 mono_os_sem_post (&info->resume_semaphore);
153 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
156 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
157 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
158 g_assert (res != -1);
162 resume_blocking_suspended (MonoThreadInfo* info)
164 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
165 mono_os_sem_post (&info->resume_semaphore);
169 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
171 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
173 InterlockedIncrement (&pending_ops);
177 mono_threads_begin_global_suspend (void)
179 size_t ps = pending_suspends;
180 if (G_UNLIKELY (ps != 0))
181 g_error ("pending_suspends = %d, but must be 0", ps);
182 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,
183 abort_posts, waits_done, pending_ops);
184 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
185 mono_threads_coop_begin_global_suspend ();
189 mono_threads_end_global_suspend (void)
191 size_t ps = pending_suspends;
192 if (G_UNLIKELY (ps != 0))
193 g_error ("pending_suspends = %d, but must be 0", ps);
194 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
195 abort_posts, waits_done, pending_ops);
196 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
197 mono_threads_coop_end_global_suspend ();
203 MonoThreadInfo *cur = mono_thread_info_current ();
205 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
206 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
207 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
216 FOREACH_THREAD_SAFE (info) {
218 char thread_name [256] = { 0 };
219 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
221 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" : "" );
223 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" : "" );
225 } FOREACH_THREAD_SAFE_END
229 mono_threads_wait_pending_operations (void)
232 int c = pending_suspends;
234 /* Wait threads to park */
235 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
236 if (pending_suspends) {
237 MonoStopwatch suspension_time;
238 mono_stopwatch_start (&suspension_time);
239 for (i = 0; i < pending_suspends; ++i) {
240 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
241 InterlockedIncrement (&waits_done);
242 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
244 mono_stopwatch_stop (&suspension_time);
248 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
249 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
251 mono_stopwatch_stop (&suspension_time);
252 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
256 pending_suspends = 0;
262 //Thread initialization code
265 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
268 mono_hazard_pointer_clear (hp, 0);
270 mono_hazard_pointer_clear (hp, 1);
272 mono_hazard_pointer_clear (hp, 2);
276 If return non null Hazard Pointer 1 holds the return value.
279 mono_thread_info_lookup (MonoNativeThreadId id)
281 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
283 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
284 mono_hazard_pointer_clear_all (hp, -1);
288 mono_hazard_pointer_clear_all (hp, 1);
289 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
293 mono_thread_info_insert (MonoThreadInfo *info)
295 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
297 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
298 mono_hazard_pointer_clear_all (hp, -1);
302 mono_hazard_pointer_clear_all (hp, -1);
307 mono_thread_info_remove (MonoThreadInfo *info)
309 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
312 THREADS_DEBUG ("removing info %p\n", info);
313 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
314 mono_hazard_pointer_clear_all (hp, -1);
319 free_thread_info (gpointer mem)
321 MonoThreadInfo *info = (MonoThreadInfo *) mem;
323 mono_os_sem_destroy (&info->resume_semaphore);
324 mono_threads_suspend_free (info);
330 mono_thread_info_register_small_id (void)
332 int small_id = mono_thread_small_id_alloc ();
333 #ifdef HAVE_KW_THREAD
334 tls_small_id = small_id;
336 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
342 thread_handle_destroy (gpointer data)
344 MonoThreadHandle *thread_handle;
346 thread_handle = (MonoThreadHandle*) data;
348 mono_os_event_destroy (&thread_handle->event);
349 g_free (thread_handle);
353 register_thread (MonoThreadInfo *info, gpointer baseptr)
356 guint8 *staddr = NULL;
357 int small_id = mono_thread_info_register_small_id ();
359 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
360 info->small_id = small_id;
362 info->handle = g_new0 (MonoThreadHandle, 1);
363 mono_refcount_init (info->handle, thread_handle_destroy);
364 mono_os_event_init (&info->handle->event, FALSE);
366 mono_os_sem_init (&info->resume_semaphore, 0);
368 /*set TLS early so SMR works */
369 mono_native_tls_set_value (thread_info_key, info);
371 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
373 if (threads_callbacks.thread_register) {
374 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
375 // g_warning ("thread registation failed\n");
376 mono_native_tls_set_value (thread_info_key, NULL);
382 mono_thread_info_get_stack_bounds (&staddr, &stsize);
385 info->stack_start_limit = staddr;
386 info->stack_end = staddr + stsize;
388 info->stackdata = g_byte_array_new ();
390 mono_threads_suspend_register (info);
393 Transition it before taking any locks or publishing itself to reduce the chance
394 of others witnessing a detached thread.
395 We can reasonably expect that until this thread gets published, no other thread will
396 try to manipulate it.
398 mono_threads_transition_attach (info);
399 mono_thread_info_suspend_lock ();
400 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
401 result = mono_thread_info_insert (info);
403 mono_thread_info_suspend_unlock ();
408 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
411 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
414 unregister_thread (void *arg)
416 gpointer gc_unsafe_stackdata;
417 MonoThreadInfo *info;
422 info = (MonoThreadInfo *) arg;
424 g_assert (mono_thread_info_is_current (info));
425 g_assert (mono_thread_info_is_live (info));
427 small_id = info->small_id;
429 /* We only enter the GC unsafe region, as when exiting this function, the thread
430 * will be detached, and the current MonoThreadInfo* will be destroyed. */
431 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
433 THREADS_DEBUG ("unregistering info %p\n", info);
435 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
438 * TLS destruction order is not reliable so small_id might be cleaned up
441 #ifndef HAVE_KW_THREAD
442 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
445 /* we need to duplicate it, as the info->handle is going
446 * to be closed when unregistering from the platform */
447 handle = mono_threads_open_thread_handle (info->handle);
450 First perform the callback that requires no locks.
451 This callback has the potential of taking other locks, so we do it before.
452 After it completes, the thread remains functional.
454 if (threads_callbacks.thread_detach)
455 threads_callbacks.thread_detach (info);
457 mono_thread_info_suspend_lock_with_info (info);
460 Now perform the callback that must be done under locks.
461 This will render the thread useless and non-suspendable, so it must
462 be done while holding the suspend lock to give no other thread chance
465 if (threads_callbacks.thread_unregister)
466 threads_callbacks.thread_unregister (info);
468 /* The thread is no longer active, so unref its handle */
469 mono_threads_close_thread_handle (info->handle);
472 result = mono_thread_info_remove (info);
474 mono_threads_transition_detach (info);
476 mono_thread_info_suspend_unlock ();
478 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
480 /*now it's safe to free the thread info.*/
481 mono_thread_hazardous_try_free (info, free_thread_info);
482 /* Pump the HP queue */
483 mono_thread_hazardous_try_free_some ();
485 mono_thread_small_id_free (small_id);
487 mono_threads_signal_thread_handle (handle);
489 mono_threads_close_thread_handle (handle);
493 thread_exited_dtor (void *arg)
495 #if defined(__MACH__)
497 * Since we use pthread dtors to clean up thread data, if a thread
498 * is attached to the runtime by another pthread dtor after our dtor
499 * has ran, it will never be detached, leading to various problems
500 * since the thread ids etc. will be reused while they are still in
501 * the threads hashtables etc.
502 * Dtors are called in a loop until all user tls entries are 0,
503 * but the loop has a maximum count (4), so if we set the tls
504 * variable every time, it will remain set when system tls dtors
505 * are ran. This allows mono_thread_info_is_exiting () to detect
506 * whenever the thread is exiting, even if it is executed from a
507 * system tls dtor (i.e. obj-c dealloc methods).
509 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
514 mono_thread_info_current_unchecked (void)
516 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
521 mono_thread_info_current (void)
523 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
527 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
530 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
531 The way to distinguish between before, during and after cleanup is the following:
533 -If the TLS key is set, cleanup has not begun;
534 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
535 -If the thread is nowhere to be found, cleanup has finished.
537 We cannot function after cleanup since there's no way to ensure what will happen.
541 /*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 */
542 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
548 mono_thread_info_get_small_id (void)
550 #ifdef HAVE_KW_THREAD
553 gpointer val = mono_native_tls_get_value (small_id_key);
556 return GPOINTER_TO_INT (val) - 1;
561 mono_thread_info_list_head (void)
567 * mono_threads_attach_tools_thread
569 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
571 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
572 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
575 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
576 * doing things like resolving backtraces in their background processing thread.
579 mono_threads_attach_tools_thread (void)
582 MonoThreadInfo *info;
584 /* Must only be called once */
585 g_assert (!mono_native_tls_get_value (thread_info_key));
587 while (!mono_threads_inited) {
588 mono_thread_info_usleep (10);
591 info = mono_thread_info_attach (&dummy);
594 info->tools_thread = TRUE;
598 mono_thread_info_attach (void *baseptr)
600 MonoThreadInfo *info;
601 if (!mono_threads_inited)
604 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
605 * thread is created before an embedding API user initialized Mono. */
606 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
609 g_assert (mono_threads_inited);
612 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
614 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
615 THREADS_DEBUG ("attaching %p\n", info);
616 if (!register_thread (info, baseptr))
618 } else if (threads_callbacks.thread_attach) {
619 threads_callbacks.thread_attach (info);
625 mono_thread_info_detach (void)
627 MonoThreadInfo *info;
628 if (!mono_threads_inited)
630 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
631 * is created before an embedding API user initialized Mono. */
632 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
635 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
637 THREADS_DEBUG ("detaching %p\n", info);
638 unregister_thread (info);
639 mono_native_tls_set_value (thread_info_key, NULL);
644 * mono_thread_info_is_exiting:
646 * Return whenever the current thread is exiting, i.e. it is running pthread
650 mono_thread_info_is_exiting (void)
652 #if defined(__MACH__)
653 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
661 thread_info_key_dtor (void *arg)
663 /* Put the MonoThreadInfo back for the duration of the
664 * unregister code. In some circumstances the thread needs to
665 * take the GC lock which may block which requires a coop
666 * state transition. */
667 mono_native_tls_set_value (thread_info_key, arg);
668 unregister_thread (arg);
669 mono_native_tls_set_value (thread_info_key, NULL);
674 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
677 threads_callbacks = *callbacks;
678 thread_info_size = info_size;
679 const char *sleepLimit;
681 res = mono_native_tls_alloc (&thread_info_key, NULL);
682 res = mono_native_tls_alloc (&thread_exited_key, NULL);
684 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
685 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
690 #ifndef HAVE_KW_THREAD
691 res = mono_native_tls_alloc (&small_id_key, NULL);
695 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
697 long threshold = strtol(sleepLimit, NULL, 10);
698 if ((errno == 0) && (threshold >= 40)) {
699 sleepAbortDuration = threshold;
700 sleepWarnDuration = threshold / 20;
702 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
705 mono_os_sem_init (&global_suspend_semaphore, 1);
706 mono_os_sem_init (&suspend_semaphore, 0);
708 mono_lls_init (&thread_list, NULL);
709 mono_thread_smr_init ();
710 mono_threads_suspend_init ();
711 mono_threads_suspend_init_signals ();
712 mono_threads_coop_init ();
714 #if defined(__MACH__)
715 mono_mach_init (thread_info_key);
718 mono_threads_inited = TRUE;
720 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
724 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
726 runtime_callbacks = *callbacks;
729 MonoThreadInfoRuntimeCallbacks *
730 mono_threads_get_runtime_callbacks (void)
732 return &runtime_callbacks;
736 mono_thread_info_core_resume (MonoThreadInfo *info)
738 gboolean res = FALSE;
740 switch (mono_threads_transition_request_resume (info)) {
747 case ResumeInitSelfResume:
748 resume_self_suspended (info);
751 case ResumeInitAsyncResume:
752 resume_async_suspended (info);
755 case ResumeInitBlockingResume:
756 resume_blocking_suspended (info);
765 mono_thread_info_resume (MonoNativeThreadId tid)
767 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
768 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
769 MonoThreadInfo *info;
771 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
773 mono_thread_info_suspend_lock ();
775 info = mono_thread_info_lookup (tid); /*info on HP1*/
781 result = mono_thread_info_core_resume (info);
783 //Wait for the pending resume to finish
784 mono_threads_wait_pending_operations ();
787 mono_thread_info_suspend_unlock ();
788 mono_hazard_pointer_clear (hp, 1);
793 mono_thread_info_begin_suspend (MonoThreadInfo *info)
795 switch (mono_threads_transition_request_async_suspension (info)) {
796 case AsyncSuspendAlreadySuspended:
797 case AsyncSuspendBlocking:
799 case AsyncSuspendWait:
800 mono_threads_add_to_pending_operation_set (info);
802 case AsyncSuspendInitSuspend:
803 return begin_async_suspend (info, FALSE);
805 g_assert_not_reached ();
810 mono_thread_info_begin_resume (MonoThreadInfo *info)
812 return mono_thread_info_core_resume (info);
816 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
817 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
820 is_thread_in_critical_region (MonoThreadInfo *info)
824 gpointer stack_start;
825 MonoThreadUnwindState *state;
827 /* Are we inside a system critical region? */
828 if (info->inside_critical_region)
831 /* Are we inside a GC critical region? */
832 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
836 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
837 state = mono_thread_info_get_suspend_state (info);
838 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
841 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
842 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
843 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
846 if (threads_callbacks.ip_in_critical_region)
847 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
849 ji = mono_jit_info_table_find (
850 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
851 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
856 method = mono_jit_info_get_method (ji);
858 return threads_callbacks.mono_method_is_critical (method);
862 mono_thread_info_in_critical_location (MonoThreadInfo *info)
864 return is_thread_in_critical_region (info);
868 The return value is only valid until a matching mono_thread_info_resume is called
870 static MonoThreadInfo*
871 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
873 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
874 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
878 switch (mono_threads_transition_request_async_suspension (info)) {
879 case AsyncSuspendAlreadySuspended:
880 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
882 case AsyncSuspendWait:
883 mono_threads_add_to_pending_operation_set (info);
885 case AsyncSuspendInitSuspend:
886 if (!begin_async_suspend (info, interrupt_kernel)) {
887 mono_hazard_pointer_clear (hp, 1);
891 case AsyncSuspendBlocking:
892 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
893 mono_threads_suspend_abort_syscall (info);
897 g_assert_not_reached ();
900 //Wait for the pending suspend to finish
901 mono_threads_wait_pending_operations ();
903 if (!check_async_suspend (info)) {
904 mono_thread_info_core_resume (info);
905 mono_threads_wait_pending_operations ();
906 mono_hazard_pointer_clear (hp, 1);
912 static MonoThreadInfo*
913 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
915 MonoThreadInfo *info = NULL;
916 int sleep_duration = 0;
918 if (!(info = suspend_sync (id, interrupt_kernel))) {
919 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
923 /*WARNING: We now are in interrupt context until we resume the thread. */
924 if (!is_thread_in_critical_region (info))
927 if (!mono_thread_info_core_resume (info)) {
928 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
931 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
933 /* Wait for the pending resume to finish */
934 mono_threads_wait_pending_operations ();
936 if (sleep_duration == 0)
937 mono_thread_info_yield ();
939 g_usleep (sleep_duration);
941 sleep_duration += 10;
947 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
950 MonoThreadInfo *info = NULL;
951 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
953 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
954 /*FIXME: unify this with self-suspend*/
955 g_assert (id != mono_native_thread_id_get ());
957 /* This can block during stw */
958 mono_thread_info_suspend_lock ();
959 mono_threads_begin_global_suspend ();
961 info = suspend_sync_nolock (id, interrupt_kernel);
965 switch (result = callback (info, user_data)) {
966 case MonoResumeThread:
967 mono_hazard_pointer_set (hp, 1, info);
968 mono_thread_info_core_resume (info);
969 mono_threads_wait_pending_operations ();
972 g_assert (!mono_threads_is_coop_enabled ());
975 g_error ("Invalid suspend_and_run callback return value %d", result);
979 mono_hazard_pointer_clear (hp, 1);
980 mono_threads_end_global_suspend ();
981 mono_thread_info_suspend_unlock ();
985 Inject an assynchronous call into the target thread. The target thread must be suspended and
986 only a single async call can be setup for a given suspend cycle.
987 This async call must cause stack unwinding as the current implementation doesn't save enough state
988 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
989 currently used only to deliver exceptions.
992 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
994 /* An async call can only be setup on an async suspended thread */
995 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
996 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
997 g_assert (!info->async_target);
998 info->async_target = target_func;
999 /* This is not GC tracked */
1000 info->user_data = user_data;
1004 The suspend lock is held during any suspend in progress.
1005 A GC that has safepoints must take this lock as part of its
1006 STW to make sure no unsafe pending suspend is in progress.
1010 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1013 g_assert (mono_thread_info_is_current (info));
1014 g_assert (mono_thread_info_is_live (info));
1016 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1018 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1019 g_assert (res != -1);
1021 MONO_EXIT_GC_SAFE_WITH_INFO;
1025 mono_thread_info_suspend_lock (void)
1027 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1031 mono_thread_info_suspend_unlock (void)
1033 mono_os_sem_post (&global_suspend_semaphore);
1037 * This is a very specific function whose only purpose is to
1038 * break a given thread from socket syscalls.
1040 * This only exists because linux won't fail a call to connect
1041 * if the underlying is closed.
1043 * TODO We should cleanup and unify this with the other syscall abort
1047 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1049 MonoThreadHazardPointers *hp;
1050 MonoThreadInfo *info;
1052 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1055 hp = mono_hazard_pointer_get ();
1056 info = mono_thread_info_lookup (tid);
1060 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1061 mono_hazard_pointer_clear (hp, 1);
1065 mono_thread_info_suspend_lock ();
1066 mono_threads_begin_global_suspend ();
1068 mono_threads_suspend_abort_syscall (info);
1069 mono_threads_wait_pending_operations ();
1071 mono_hazard_pointer_clear (hp, 1);
1073 mono_threads_end_global_suspend ();
1074 mono_thread_info_suspend_unlock ();
1078 * mono_thread_info_set_is_async_context:
1080 * Set whenever the current thread is in an async context. Some runtime functions might behave
1081 * differently while in an async context in order to be async safe.
1084 mono_thread_info_set_is_async_context (gboolean async_context)
1086 MonoThreadInfo *info = mono_thread_info_current ();
1089 info->is_async_context = async_context;
1093 mono_thread_info_is_async_context (void)
1095 MonoThreadInfo *info = mono_thread_info_current ();
1098 return info->is_async_context;
1105 MonoThreadStart start_routine;
1106 gpointer start_routine_arg;
1107 MonoCoopSem registered;
1108 MonoThreadHandle *handle;
1112 create_thread_data_destroy (gpointer data)
1114 CreateThreadData *thread_data;
1116 thread_data = (CreateThreadData*) data;
1118 mono_coop_sem_destroy (&thread_data->registered);
1119 g_free (thread_data);
1123 inner_start_thread (gpointer data)
1125 CreateThreadData *thread_data;
1126 MonoThreadInfo *info;
1127 MonoThreadStart start_routine;
1128 gpointer start_routine_arg;
1129 gsize start_routine_res;
1132 thread_data = (CreateThreadData*) data;
1133 g_assert (thread_data);
1135 start_routine = thread_data->start_routine;
1136 start_routine_arg = thread_data->start_routine_arg;
1138 info = mono_thread_info_attach (&dummy);
1139 info->runtime_thread = TRUE;
1141 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1143 mono_coop_sem_post (&thread_data->registered);
1145 mono_refcount_dec (thread_data);
1147 /* thread_data is not valid anymore */
1150 /* Run the actual main function of the thread */
1151 start_routine_res = start_routine (start_routine_arg);
1153 mono_thread_info_exit (start_routine_res);
1155 g_assert_not_reached ();
1159 * mono_threads_create_thread:
1161 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1162 * Returns: a windows or io-layer handle for the thread.
1165 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1167 CreateThreadData *thread_data;
1169 MonoThreadHandle *ret;
1171 thread_data = g_new0 (CreateThreadData, 1);
1172 mono_refcount_init (thread_data, create_thread_data_destroy);
1173 thread_data->start_routine = start;
1174 thread_data->start_routine_arg = arg;
1175 mono_coop_sem_init (&thread_data->registered, 0);
1177 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
1179 /* ref is not going to be decremented in inner_start_thread */
1180 mono_refcount_dec (thread_data);
1185 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1186 g_assert (res == 0);
1188 ret = thread_data->handle;
1192 mono_refcount_dec (thread_data);
1198 * mono_thread_info_get_stack_bounds:
1200 * Return the address and size of the current threads stack. Return NULL as the
1201 * stack address if the stack address cannot be determined.
1204 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1206 guint8 *current = (guint8 *)&stsize;
1207 mono_threads_platform_get_stack_bounds (staddr, stsize);
1211 /* Sanity check the result */
1212 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1214 /* When running under emacs, sometimes staddr is not aligned to a page size */
1215 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1219 mono_thread_info_yield (void)
1221 return mono_threads_platform_yield ();
1223 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1224 static MonoCoopMutex sleep_mutex;
1225 static MonoCoopCond sleep_cond;
1228 sleep_initialize (void)
1230 mono_coop_mutex_init (&sleep_mutex);
1231 mono_coop_cond_init (&sleep_cond);
1235 sleep_interrupt (gpointer data)
1237 mono_coop_mutex_lock (&sleep_mutex);
1238 mono_coop_cond_broadcast (&sleep_cond);
1239 mono_coop_mutex_unlock (&sleep_mutex);
1242 static inline guint32
1243 sleep_interruptable (guint32 ms, gboolean *alerted)
1247 g_assert (INFINITE == G_MAXUINT32);
1253 end = mono_msec_ticks() + ms;
1255 mono_lazy_initialize (&sleep_init, sleep_initialize);
1257 mono_coop_mutex_lock (&sleep_mutex);
1260 if (ms != INFINITE) {
1261 now = mono_msec_ticks();
1266 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1268 mono_coop_mutex_unlock (&sleep_mutex);
1269 return WAIT_IO_COMPLETION;
1273 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1275 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1277 mono_thread_info_uninstall_interrupt (alerted);
1279 mono_coop_mutex_unlock (&sleep_mutex);
1280 return WAIT_IO_COMPLETION;
1284 mono_coop_mutex_unlock (&sleep_mutex);
1290 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1293 MonoThreadInfo *info;
1295 mono_thread_info_yield ();
1297 info = mono_thread_info_current ();
1298 if (info && mono_thread_info_is_interrupt_state (info))
1299 return WAIT_IO_COMPLETION;
1305 return sleep_interruptable (ms, alerted);
1309 if (ms == INFINITE) {
1312 Sleep (G_MAXUINT32);
1314 sleep (G_MAXUINT32);
1319 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1320 struct timespec start, target;
1322 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1323 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1324 g_assert (ret == 0);
1327 target.tv_sec += ms / 1000;
1328 target.tv_nsec += (ms % 1000) * 1000000;
1329 if (target.tv_nsec > 999999999) {
1330 target.tv_nsec -= 999999999;
1335 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1340 struct timespec req, rem;
1342 req.tv_sec = ms / 1000;
1343 req.tv_nsec = (ms % 1000) * 1000000;
1346 memset (&rem, 0, sizeof (rem));
1347 ret = nanosleep (&req, &rem);
1349 #endif /* __linux__ */
1358 mono_thread_info_usleep (guint64 us)
1367 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1369 return ((MonoThreadInfo*)info)->tls [key];
1373 * mono_threads_info_tls_set:
1375 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1376 * values of TLS variables for threads other than the current thread.
1377 * This should only be used for infrequently changing TLS variables, and it should
1378 * be paired with setting the real TLS variable since this provides no GC tracking.
1381 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1383 ((MonoThreadInfo*)info)->tls [key] = value;
1386 #if defined(__native_client__)
1387 void nacl_shutdown_gc_thread(void);
1391 * mono_thread_info_exit:
1393 * Exit the current thread.
1394 * This function doesn't return.
1397 mono_thread_info_exit (gsize exit_code)
1399 #if defined(__native_client__)
1400 nacl_shutdown_gc_thread();
1403 mono_thread_info_detach ();
1405 mono_threads_platform_exit (0);
1409 * mono_threads_open_thread_handle:
1411 * Duplicate the handle. The handle needs to be closed by calling
1412 * mono_threads_close_thread_handle () when it is no longer needed.
1415 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1417 return mono_refcount_inc (thread_handle);
1421 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1423 mono_refcount_dec (thread_handle);
1427 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1429 mono_os_event_set (&thread_handle->event);
1432 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1434 struct _MonoThreadInfoInterruptToken {
1435 void (*callback) (gpointer data);
1440 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1442 * - @callback: must be able to be called from another thread and always cancel the wait
1443 * - @data: passed to the callback
1444 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1445 * if set to TRUE, it must mean that the thread is in interrupted state
1448 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1450 MonoThreadInfo *info;
1451 MonoThreadInfoInterruptToken *previous_token, *token;
1453 g_assert (callback);
1455 g_assert (interrupted);
1456 *interrupted = FALSE;
1458 info = mono_thread_info_current ();
1461 /* The memory of this token can be freed at 2 places:
1462 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1463 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1464 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1465 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1466 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1467 token->callback = callback;
1470 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1472 if (previous_token) {
1473 if (previous_token != INTERRUPT_STATE)
1474 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1478 *interrupted = TRUE;
1481 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1482 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1486 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1488 MonoThreadInfo *info;
1489 MonoThreadInfoInterruptToken *previous_token;
1491 g_assert (interrupted);
1492 *interrupted = FALSE;
1494 info = mono_thread_info_current ();
1497 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1499 /* only the installer can uninstall the token */
1500 g_assert (previous_token);
1502 if (previous_token == INTERRUPT_STATE) {
1503 /* if it is interrupted, then it is going to be freed in finish interrupt */
1504 *interrupted = TRUE;
1506 g_free (previous_token);
1509 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1510 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1513 static MonoThreadInfoInterruptToken*
1514 set_interrupt_state (MonoThreadInfo *info)
1516 MonoThreadInfoInterruptToken *token, *previous_token;
1520 /* Atomically obtain the token the thread is
1521 * waiting on, and change it to a flag value. */
1524 previous_token = info->interrupt_token;
1526 /* Already interrupted */
1527 if (previous_token == INTERRUPT_STATE) {
1532 token = previous_token;
1533 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1539 * mono_thread_info_prepare_interrupt:
1541 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1542 * - if the thread calls one of the WaitFor functions, the function will return with
1543 * WAIT_IO_COMPLETION instead of waiting
1544 * - if the thread was waiting when this function was called, the wait will be broken
1546 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1547 * didn't receive the interrupt signal yet, in this case it should call the wait function
1548 * again. This essentially means that the target thread will busy wait until it is ready to
1549 * process the interruption.
1551 MonoThreadInfoInterruptToken*
1552 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1554 MonoThreadInfoInterruptToken *token;
1556 token = set_interrupt_state (info);
1558 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1559 mono_thread_info_get_tid (info), token);
1565 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1567 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1572 g_assert (token->callback);
1574 token->callback (token->data);
1580 mono_thread_info_self_interrupt (void)
1582 MonoThreadInfo *info;
1583 MonoThreadInfoInterruptToken *token;
1585 info = mono_thread_info_current ();
1588 token = set_interrupt_state (info);
1591 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1592 mono_thread_info_get_tid (info));
1595 /* Clear the interrupted flag of the current thread, set with
1596 * mono_thread_info_self_interrupt, so it can wait again */
1598 mono_thread_info_clear_self_interrupt ()
1600 MonoThreadInfo *info;
1601 MonoThreadInfoInterruptToken *previous_token;
1603 info = mono_thread_info_current ();
1606 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1607 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1609 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1613 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1616 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1620 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1624 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1625 g_string_append_printf (text, "not waiting");
1626 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1627 g_string_append_printf (text, "interrupted state");
1629 g_string_append_printf (text, "waiting");
1633 mono_thread_info_is_current (MonoThreadInfo *info)
1635 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1638 MonoThreadInfoWaitRet
1639 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1641 MonoOSEventWaitRet res;
1643 res = mono_os_event_wait_one (&thread_handle->event, timeout);
1644 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1645 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1646 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1647 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1648 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1649 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1651 g_error ("%s: unknown res value %d", __func__, res);
1654 MonoThreadInfoWaitRet
1655 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1657 MonoOSEventWaitRet res;
1658 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1661 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1662 if (background_change_event)
1663 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1665 for (i = 0; i < nhandles; ++i)
1666 thread_events [i] = &thread_handles [i]->event;
1668 if (background_change_event)
1669 thread_events [nhandles ++] = background_change_event;
1671 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout);
1672 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1673 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1674 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1675 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1676 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1677 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1679 g_error ("%s: unknown res value %d", __func__, res);