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>
35 #include <mono/io-layer/io-layer.h>
40 #include <mono/utils/mach-support.h>
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
62 static MonoSemType global_suspend_semaphore;
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
69 static __thread guint32 tls_small_id;
71 static MonoNativeTlsKey small_id_key;
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
79 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
82 #define SLEEP_DURATION_BEFORE_WARNING (50)
84 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
86 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
87 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
89 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
92 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
94 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
95 InterlockedIncrement (&abort_posts);
96 mono_os_sem_post (&suspend_semaphore);
100 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
103 InterlockedIncrement (&suspend_posts);
104 mono_os_sem_post (&suspend_semaphore);
108 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
110 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
111 InterlockedIncrement (&resume_posts);
112 mono_os_sem_post (&suspend_semaphore);
116 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
118 if (mono_threads_is_coop_enabled ()) {
119 /* There's nothing else to do after we async request the thread to suspend */
120 mono_threads_add_to_pending_operation_set (info);
124 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
128 check_async_suspend (MonoThreadInfo *info)
130 if (mono_threads_is_coop_enabled ()) {
131 /* Async suspend can't async fail on coop */
135 return mono_threads_suspend_check_suspend_result (info);
139 resume_async_suspended (MonoThreadInfo *info)
141 if (mono_threads_is_coop_enabled ())
142 g_assert_not_reached ();
144 g_assert (mono_threads_suspend_begin_async_resume (info));
148 resume_self_suspended (MonoThreadInfo* info)
150 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
151 mono_os_sem_post (&info->resume_semaphore);
155 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
158 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
159 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
160 g_assert (res != -1);
164 resume_blocking_suspended (MonoThreadInfo* info)
166 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
167 mono_os_sem_post (&info->resume_semaphore);
171 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
173 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
175 InterlockedIncrement (&pending_ops);
179 mono_threads_begin_global_suspend (void)
181 size_t ps = pending_suspends;
182 if (G_UNLIKELY (ps != 0))
183 g_error ("pending_suspends = %d, but must be 0", ps);
184 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,
185 abort_posts, waits_done, pending_ops);
186 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
187 mono_threads_coop_begin_global_suspend ();
191 mono_threads_end_global_suspend (void)
193 size_t ps = pending_suspends;
194 if (G_UNLIKELY (ps != 0))
195 g_error ("pending_suspends = %d, but must be 0", ps);
196 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
197 abort_posts, waits_done, pending_ops);
198 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
199 mono_threads_coop_end_global_suspend ();
205 MonoThreadInfo *cur = mono_thread_info_current ();
207 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
218 FOREACH_THREAD_SAFE (info) {
220 char thread_name [256] = { 0 };
221 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
223 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" : "" );
225 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" : "" );
227 } FOREACH_THREAD_SAFE_END
231 mono_threads_wait_pending_operations (void)
234 int c = pending_suspends;
236 /* Wait threads to park */
237 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
238 if (pending_suspends) {
239 MonoStopwatch suspension_time;
240 mono_stopwatch_start (&suspension_time);
241 for (i = 0; i < pending_suspends; ++i) {
242 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
243 InterlockedIncrement (&waits_done);
244 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
246 mono_stopwatch_stop (&suspension_time);
250 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
251 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
253 mono_stopwatch_stop (&suspension_time);
254 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
258 pending_suspends = 0;
264 //Thread initialization code
267 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
270 mono_hazard_pointer_clear (hp, 0);
272 mono_hazard_pointer_clear (hp, 1);
274 mono_hazard_pointer_clear (hp, 2);
278 If return non null Hazard Pointer 1 holds the return value.
281 mono_thread_info_lookup (MonoNativeThreadId id)
283 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
285 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
286 mono_hazard_pointer_clear_all (hp, -1);
290 mono_hazard_pointer_clear_all (hp, 1);
291 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
295 mono_thread_info_insert (MonoThreadInfo *info)
297 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
299 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
300 mono_hazard_pointer_clear_all (hp, -1);
304 mono_hazard_pointer_clear_all (hp, -1);
309 mono_thread_info_remove (MonoThreadInfo *info)
311 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
314 THREADS_DEBUG ("removing info %p\n", info);
315 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
316 mono_hazard_pointer_clear_all (hp, -1);
321 free_thread_info (gpointer mem)
323 MonoThreadInfo *info = (MonoThreadInfo *) mem;
325 mono_os_sem_destroy (&info->resume_semaphore);
326 mono_threads_suspend_free (info);
332 mono_thread_info_register_small_id (void)
334 int small_id = mono_thread_small_id_alloc ();
335 #ifdef HAVE_KW_THREAD
336 tls_small_id = small_id;
338 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
344 thread_handle_destroy (gpointer data)
346 MonoThreadHandle *thread_handle;
348 thread_handle = (MonoThreadHandle*) data;
350 mono_os_event_destroy (&thread_handle->event);
351 g_free (thread_handle);
355 register_thread (MonoThreadInfo *info, gpointer baseptr)
358 guint8 *staddr = NULL;
359 int small_id = mono_thread_info_register_small_id ();
361 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
362 info->small_id = small_id;
364 info->handle = g_new0 (MonoThreadHandle, 1);
365 mono_refcount_init (info->handle, thread_handle_destroy);
366 mono_os_event_init (&info->handle->event, FALSE);
368 mono_os_sem_init (&info->resume_semaphore, 0);
370 /*set TLS early so SMR works */
371 mono_native_tls_set_value (thread_info_key, info);
373 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
375 if (threads_callbacks.thread_register) {
376 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
377 // g_warning ("thread registation failed\n");
378 mono_native_tls_set_value (thread_info_key, NULL);
384 mono_thread_info_get_stack_bounds (&staddr, &stsize);
387 info->stack_start_limit = staddr;
388 info->stack_end = staddr + stsize;
390 info->stackdata = g_byte_array_new ();
392 mono_threads_suspend_register (info);
395 Transition it before taking any locks or publishing itself to reduce the chance
396 of others witnessing a detached thread.
397 We can reasonably expect that until this thread gets published, no other thread will
398 try to manipulate it.
400 mono_threads_transition_attach (info);
401 mono_thread_info_suspend_lock ();
402 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
403 result = mono_thread_info_insert (info);
405 mono_thread_info_suspend_unlock ();
410 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
413 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
416 unregister_thread (void *arg)
418 gpointer gc_unsafe_stackdata;
419 MonoThreadInfo *info;
424 info = (MonoThreadInfo *) arg;
426 g_assert (mono_thread_info_is_current (info));
427 g_assert (mono_thread_info_is_live (info));
429 small_id = info->small_id;
431 /* We only enter the GC unsafe region, as when exiting this function, the thread
432 * will be detached, and the current MonoThreadInfo* will be destroyed. */
433 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
435 THREADS_DEBUG ("unregistering info %p\n", info);
437 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
440 * TLS destruction order is not reliable so small_id might be cleaned up
443 #ifndef HAVE_KW_THREAD
444 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
447 /* we need to duplicate it, as the info->handle is going
448 * to be closed when unregistering from the platform */
449 handle = mono_threads_open_thread_handle (info->handle);
452 First perform the callback that requires no locks.
453 This callback has the potential of taking other locks, so we do it before.
454 After it completes, the thread remains functional.
456 if (threads_callbacks.thread_detach)
457 threads_callbacks.thread_detach (info);
459 mono_thread_info_suspend_lock_with_info (info);
462 Now perform the callback that must be done under locks.
463 This will render the thread useless and non-suspendable, so it must
464 be done while holding the suspend lock to give no other thread chance
467 if (threads_callbacks.thread_unregister)
468 threads_callbacks.thread_unregister (info);
470 /* The thread is no longer active, so unref its handle */
471 mono_threads_close_thread_handle (info->handle);
474 result = mono_thread_info_remove (info);
476 mono_threads_transition_detach (info);
478 mono_thread_info_suspend_unlock ();
480 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
482 /*now it's safe to free the thread info.*/
483 mono_thread_hazardous_try_free (info, free_thread_info);
484 /* Pump the HP queue */
485 mono_thread_hazardous_try_free_some ();
487 mono_thread_small_id_free (small_id);
489 mono_threads_signal_thread_handle (handle);
491 mono_threads_close_thread_handle (handle);
495 thread_exited_dtor (void *arg)
497 #if defined(__MACH__)
499 * Since we use pthread dtors to clean up thread data, if a thread
500 * is attached to the runtime by another pthread dtor after our dtor
501 * has ran, it will never be detached, leading to various problems
502 * since the thread ids etc. will be reused while they are still in
503 * the threads hashtables etc.
504 * Dtors are called in a loop until all user tls entries are 0,
505 * but the loop has a maximum count (4), so if we set the tls
506 * variable every time, it will remain set when system tls dtors
507 * are ran. This allows mono_thread_info_is_exiting () to detect
508 * whenever the thread is exiting, even if it is executed from a
509 * system tls dtor (i.e. obj-c dealloc methods).
511 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
516 mono_thread_info_current_unchecked (void)
518 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
523 mono_thread_info_current (void)
525 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
529 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
532 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
533 The way to distinguish between before, during and after cleanup is the following:
535 -If the TLS key is set, cleanup has not begun;
536 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
537 -If the thread is nowhere to be found, cleanup has finished.
539 We cannot function after cleanup since there's no way to ensure what will happen.
543 /*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 */
544 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
550 mono_thread_info_get_small_id (void)
552 #ifdef HAVE_KW_THREAD
555 gpointer val = mono_native_tls_get_value (small_id_key);
558 return GPOINTER_TO_INT (val) - 1;
563 mono_thread_info_list_head (void)
569 * mono_threads_attach_tools_thread
571 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
573 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
574 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
577 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
578 * doing things like resolving backtraces in their background processing thread.
581 mono_threads_attach_tools_thread (void)
584 MonoThreadInfo *info;
586 /* Must only be called once */
587 g_assert (!mono_native_tls_get_value (thread_info_key));
589 while (!mono_threads_inited) {
590 mono_thread_info_usleep (10);
593 info = mono_thread_info_attach (&dummy);
596 info->tools_thread = TRUE;
600 mono_thread_info_attach (void *baseptr)
602 MonoThreadInfo *info;
603 if (!mono_threads_inited)
606 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
607 * thread is created before an embedding API user initialized Mono. */
608 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
611 g_assert (mono_threads_inited);
614 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
616 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
617 THREADS_DEBUG ("attaching %p\n", info);
618 if (!register_thread (info, baseptr))
620 } else if (threads_callbacks.thread_attach) {
621 threads_callbacks.thread_attach (info);
627 mono_thread_info_detach (void)
629 MonoThreadInfo *info;
630 if (!mono_threads_inited)
632 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
633 * is created before an embedding API user initialized Mono. */
634 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
637 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
639 THREADS_DEBUG ("detaching %p\n", info);
640 unregister_thread (info);
641 mono_native_tls_set_value (thread_info_key, NULL);
646 * mono_thread_info_is_exiting:
648 * Return whenever the current thread is exiting, i.e. it is running pthread
652 mono_thread_info_is_exiting (void)
654 #if defined(__MACH__)
655 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
663 thread_info_key_dtor (void *arg)
665 /* Put the MonoThreadInfo back for the duration of the
666 * unregister code. In some circumstances the thread needs to
667 * take the GC lock which may block which requires a coop
668 * state transition. */
669 mono_native_tls_set_value (thread_info_key, arg);
670 unregister_thread (arg);
671 mono_native_tls_set_value (thread_info_key, NULL);
676 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
679 threads_callbacks = *callbacks;
680 thread_info_size = info_size;
681 const char *sleepLimit;
683 res = mono_native_tls_alloc (&thread_info_key, NULL);
684 res = mono_native_tls_alloc (&thread_exited_key, NULL);
686 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
687 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
692 #ifndef HAVE_KW_THREAD
693 res = mono_native_tls_alloc (&small_id_key, NULL);
697 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
699 long threshold = strtol(sleepLimit, NULL, 10);
700 if ((errno == 0) && (threshold >= 40)) {
701 sleepAbortDuration = threshold;
702 sleepWarnDuration = threshold / 20;
704 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
707 mono_os_sem_init (&global_suspend_semaphore, 1);
708 mono_os_sem_init (&suspend_semaphore, 0);
710 mono_lls_init (&thread_list, NULL);
711 mono_thread_smr_init ();
712 mono_threads_suspend_init ();
713 mono_threads_coop_init ();
714 mono_threads_platform_init ();
716 #if defined(__MACH__)
717 mono_mach_init (thread_info_key);
720 mono_threads_inited = TRUE;
722 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
726 mono_threads_signals_init (void)
728 mono_threads_suspend_init_signals ();
732 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
734 runtime_callbacks = *callbacks;
737 MonoThreadInfoRuntimeCallbacks *
738 mono_threads_get_runtime_callbacks (void)
740 return &runtime_callbacks;
744 mono_thread_info_core_resume (MonoThreadInfo *info)
746 gboolean res = FALSE;
748 switch (mono_threads_transition_request_resume (info)) {
755 case ResumeInitSelfResume:
756 resume_self_suspended (info);
759 case ResumeInitAsyncResume:
760 resume_async_suspended (info);
763 case ResumeInitBlockingResume:
764 resume_blocking_suspended (info);
773 mono_thread_info_resume (MonoNativeThreadId tid)
775 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
776 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
777 MonoThreadInfo *info;
779 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
781 mono_thread_info_suspend_lock ();
783 info = mono_thread_info_lookup (tid); /*info on HP1*/
789 result = mono_thread_info_core_resume (info);
791 //Wait for the pending resume to finish
792 mono_threads_wait_pending_operations ();
795 mono_thread_info_suspend_unlock ();
796 mono_hazard_pointer_clear (hp, 1);
801 mono_thread_info_begin_suspend (MonoThreadInfo *info)
803 switch (mono_threads_transition_request_async_suspension (info)) {
804 case AsyncSuspendAlreadySuspended:
805 case AsyncSuspendBlocking:
807 case AsyncSuspendWait:
808 mono_threads_add_to_pending_operation_set (info);
810 case AsyncSuspendInitSuspend:
811 return begin_async_suspend (info, FALSE);
813 g_assert_not_reached ();
818 mono_thread_info_begin_resume (MonoThreadInfo *info)
820 return mono_thread_info_core_resume (info);
824 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
825 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
828 is_thread_in_critical_region (MonoThreadInfo *info)
832 gpointer stack_start;
833 MonoThreadUnwindState *state;
835 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
838 /* Are we inside a system critical region? */
839 if (info->inside_critical_region)
842 /* Are we inside a GC critical region? */
843 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
847 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
848 state = mono_thread_info_get_suspend_state (info);
849 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
852 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
853 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
854 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
857 if (threads_callbacks.ip_in_critical_region)
858 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
860 ji = mono_jit_info_table_find (
861 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
862 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
867 method = mono_jit_info_get_method (ji);
869 return threads_callbacks.mono_method_is_critical (method);
873 mono_thread_info_in_critical_location (MonoThreadInfo *info)
875 return is_thread_in_critical_region (info);
879 The return value is only valid until a matching mono_thread_info_resume is called
881 static MonoThreadInfo*
882 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
884 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
885 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
889 switch (mono_threads_transition_request_async_suspension (info)) {
890 case AsyncSuspendAlreadySuspended:
891 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
893 case AsyncSuspendWait:
894 mono_threads_add_to_pending_operation_set (info);
896 case AsyncSuspendInitSuspend:
897 if (!begin_async_suspend (info, interrupt_kernel)) {
898 mono_hazard_pointer_clear (hp, 1);
902 case AsyncSuspendBlocking:
903 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
904 mono_threads_suspend_abort_syscall (info);
908 g_assert_not_reached ();
911 //Wait for the pending suspend to finish
912 mono_threads_wait_pending_operations ();
914 if (!check_async_suspend (info)) {
915 mono_thread_info_core_resume (info);
916 mono_threads_wait_pending_operations ();
917 mono_hazard_pointer_clear (hp, 1);
923 static MonoThreadInfo*
924 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
926 MonoThreadInfo *info = NULL;
927 int sleep_duration = 0;
929 if (!(info = suspend_sync (id, interrupt_kernel))) {
930 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
934 /*WARNING: We now are in interrupt context until we resume the thread. */
935 if (!is_thread_in_critical_region (info))
938 if (!mono_thread_info_core_resume (info)) {
939 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
942 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
944 /* Wait for the pending resume to finish */
945 mono_threads_wait_pending_operations ();
947 if (sleep_duration == 0)
948 mono_thread_info_yield ();
950 g_usleep (sleep_duration);
952 sleep_duration += 10;
958 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
961 MonoThreadInfo *info = NULL;
962 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
964 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
965 /*FIXME: unify this with self-suspend*/
966 g_assert (id != mono_native_thread_id_get ());
968 /* This can block during stw */
969 mono_thread_info_suspend_lock ();
970 mono_threads_begin_global_suspend ();
972 info = suspend_sync_nolock (id, interrupt_kernel);
976 switch (result = callback (info, user_data)) {
977 case MonoResumeThread:
978 mono_hazard_pointer_set (hp, 1, info);
979 mono_thread_info_core_resume (info);
980 mono_threads_wait_pending_operations ();
983 g_assert (!mono_threads_is_coop_enabled ());
986 g_error ("Invalid suspend_and_run callback return value %d", result);
990 mono_hazard_pointer_clear (hp, 1);
991 mono_threads_end_global_suspend ();
992 mono_thread_info_suspend_unlock ();
996 Inject an assynchronous call into the target thread. The target thread must be suspended and
997 only a single async call can be setup for a given suspend cycle.
998 This async call must cause stack unwinding as the current implementation doesn't save enough state
999 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1000 currently used only to deliver exceptions.
1003 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1005 /* An async call can only be setup on an async suspended thread */
1006 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1007 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1008 g_assert (!info->async_target);
1009 info->async_target = target_func;
1010 /* This is not GC tracked */
1011 info->user_data = user_data;
1015 The suspend lock is held during any suspend in progress.
1016 A GC that has safepoints must take this lock as part of its
1017 STW to make sure no unsafe pending suspend is in progress.
1021 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1024 g_assert (mono_thread_info_is_current (info));
1025 g_assert (mono_thread_info_is_live (info));
1027 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1029 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1030 g_assert (res != -1);
1032 MONO_EXIT_GC_SAFE_WITH_INFO;
1036 mono_thread_info_suspend_lock (void)
1038 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1042 mono_thread_info_suspend_unlock (void)
1044 mono_os_sem_post (&global_suspend_semaphore);
1048 * This is a very specific function whose only purpose is to
1049 * break a given thread from socket syscalls.
1051 * This only exists because linux won't fail a call to connect
1052 * if the underlying is closed.
1054 * TODO We should cleanup and unify this with the other syscall abort
1058 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1060 MonoThreadHazardPointers *hp;
1061 MonoThreadInfo *info;
1063 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1066 hp = mono_hazard_pointer_get ();
1067 info = mono_thread_info_lookup (tid);
1071 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1072 mono_hazard_pointer_clear (hp, 1);
1076 mono_thread_info_suspend_lock ();
1077 mono_threads_begin_global_suspend ();
1079 mono_threads_suspend_abort_syscall (info);
1080 mono_threads_wait_pending_operations ();
1082 mono_hazard_pointer_clear (hp, 1);
1084 mono_threads_end_global_suspend ();
1085 mono_thread_info_suspend_unlock ();
1089 * mono_thread_info_set_is_async_context:
1091 * Set whenever the current thread is in an async context. Some runtime functions might behave
1092 * differently while in an async context in order to be async safe.
1095 mono_thread_info_set_is_async_context (gboolean async_context)
1097 MonoThreadInfo *info = mono_thread_info_current ();
1100 info->is_async_context = async_context;
1104 mono_thread_info_is_async_context (void)
1106 MonoThreadInfo *info = mono_thread_info_current ();
1109 return info->is_async_context;
1116 MonoThreadStart start_routine;
1117 gpointer start_routine_arg;
1118 MonoCoopSem registered;
1119 MonoThreadHandle *handle;
1123 create_thread_data_destroy (gpointer data)
1125 CreateThreadData *thread_data;
1127 thread_data = (CreateThreadData*) data;
1129 mono_coop_sem_destroy (&thread_data->registered);
1130 g_free (thread_data);
1134 inner_start_thread (gpointer data)
1136 CreateThreadData *thread_data;
1137 MonoThreadInfo *info;
1138 MonoThreadStart start_routine;
1139 gpointer start_routine_arg;
1140 gsize start_routine_res;
1143 thread_data = (CreateThreadData*) data;
1144 g_assert (thread_data);
1146 start_routine = thread_data->start_routine;
1147 start_routine_arg = thread_data->start_routine_arg;
1149 info = mono_thread_info_attach (&dummy);
1150 info->runtime_thread = TRUE;
1152 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1154 mono_coop_sem_post (&thread_data->registered);
1156 mono_refcount_dec (thread_data);
1158 /* thread_data is not valid anymore */
1161 /* Run the actual main function of the thread */
1162 start_routine_res = start_routine (start_routine_arg);
1164 mono_thread_info_exit (start_routine_res);
1166 g_assert_not_reached ();
1170 * mono_threads_create_thread:
1172 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1173 * Returns: a windows or io-layer handle for the thread.
1176 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1178 CreateThreadData *thread_data;
1180 MonoThreadHandle *ret;
1182 thread_data = g_new0 (CreateThreadData, 1);
1183 mono_refcount_init (thread_data, create_thread_data_destroy);
1184 thread_data->start_routine = start;
1185 thread_data->start_routine_arg = arg;
1186 mono_coop_sem_init (&thread_data->registered, 0);
1188 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
1190 /* ref is not going to be decremented in inner_start_thread */
1191 mono_refcount_dec (thread_data);
1196 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1197 g_assert (res == 0);
1199 ret = thread_data->handle;
1203 mono_refcount_dec (thread_data);
1209 * mono_thread_info_get_stack_bounds:
1211 * Return the address and size of the current threads stack. Return NULL as the
1212 * stack address if the stack address cannot be determined.
1215 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1217 guint8 *current = (guint8 *)&stsize;
1218 mono_threads_platform_get_stack_bounds (staddr, stsize);
1222 /* Sanity check the result */
1223 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1225 /* When running under emacs, sometimes staddr is not aligned to a page size */
1226 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1230 mono_thread_info_yield (void)
1232 return mono_threads_platform_yield ();
1234 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1235 static MonoCoopMutex sleep_mutex;
1236 static MonoCoopCond sleep_cond;
1239 sleep_initialize (void)
1241 mono_coop_mutex_init (&sleep_mutex);
1242 mono_coop_cond_init (&sleep_cond);
1246 sleep_interrupt (gpointer data)
1248 mono_coop_mutex_lock (&sleep_mutex);
1249 mono_coop_cond_broadcast (&sleep_cond);
1250 mono_coop_mutex_unlock (&sleep_mutex);
1253 static inline guint32
1254 sleep_interruptable (guint32 ms, gboolean *alerted)
1258 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1263 if (ms != MONO_INFINITE_WAIT)
1264 end = mono_msec_ticks() + ms;
1266 mono_lazy_initialize (&sleep_init, sleep_initialize);
1268 mono_coop_mutex_lock (&sleep_mutex);
1271 if (ms != MONO_INFINITE_WAIT) {
1272 now = mono_msec_ticks();
1277 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1279 mono_coop_mutex_unlock (&sleep_mutex);
1280 return WAIT_IO_COMPLETION;
1283 if (ms != MONO_INFINITE_WAIT)
1284 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1286 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1288 mono_thread_info_uninstall_interrupt (alerted);
1290 mono_coop_mutex_unlock (&sleep_mutex);
1291 return WAIT_IO_COMPLETION;
1295 mono_coop_mutex_unlock (&sleep_mutex);
1301 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1304 MonoThreadInfo *info;
1306 mono_thread_info_yield ();
1308 info = mono_thread_info_current ();
1309 if (info && mono_thread_info_is_interrupt_state (info))
1310 return WAIT_IO_COMPLETION;
1316 return sleep_interruptable (ms, alerted);
1320 if (ms == MONO_INFINITE_WAIT) {
1323 Sleep (G_MAXUINT32);
1325 sleep (G_MAXUINT32);
1330 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1331 struct timespec start, target;
1333 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1334 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1335 g_assert (ret == 0);
1338 target.tv_sec += ms / 1000;
1339 target.tv_nsec += (ms % 1000) * 1000000;
1340 if (target.tv_nsec > 999999999) {
1341 target.tv_nsec -= 999999999;
1346 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1351 struct timespec req, rem;
1353 req.tv_sec = ms / 1000;
1354 req.tv_nsec = (ms % 1000) * 1000000;
1357 memset (&rem, 0, sizeof (rem));
1358 ret = nanosleep (&req, &rem);
1360 #endif /* __linux__ */
1369 mono_thread_info_usleep (guint64 us)
1378 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1380 return ((MonoThreadInfo*)info)->tls [key];
1384 * mono_threads_info_tls_set:
1386 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1387 * values of TLS variables for threads other than the current thread.
1388 * This should only be used for infrequently changing TLS variables, and it should
1389 * be paired with setting the real TLS variable since this provides no GC tracking.
1392 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1394 ((MonoThreadInfo*)info)->tls [key] = value;
1397 #if defined(__native_client__)
1398 void nacl_shutdown_gc_thread(void);
1402 * mono_thread_info_exit:
1404 * Exit the current thread.
1405 * This function doesn't return.
1408 mono_thread_info_exit (gsize exit_code)
1410 #if defined(__native_client__)
1411 nacl_shutdown_gc_thread();
1414 mono_thread_info_detach ();
1416 mono_threads_platform_exit (0);
1420 * mono_threads_open_thread_handle:
1422 * Duplicate the handle. The handle needs to be closed by calling
1423 * mono_threads_close_thread_handle () when it is no longer needed.
1426 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1428 return mono_refcount_inc (thread_handle);
1432 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1434 mono_refcount_dec (thread_handle);
1438 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1440 mono_os_event_set (&thread_handle->event);
1443 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1445 struct _MonoThreadInfoInterruptToken {
1446 void (*callback) (gpointer data);
1451 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1453 * - @callback: must be able to be called from another thread and always cancel the wait
1454 * - @data: passed to the callback
1455 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1456 * if set to TRUE, it must mean that the thread is in interrupted state
1459 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1461 MonoThreadInfo *info;
1462 MonoThreadInfoInterruptToken *previous_token, *token;
1464 g_assert (callback);
1466 g_assert (interrupted);
1467 *interrupted = FALSE;
1469 info = mono_thread_info_current ();
1472 /* The memory of this token can be freed at 2 places:
1473 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1474 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1475 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1476 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1477 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1478 token->callback = callback;
1481 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1483 if (previous_token) {
1484 if (previous_token != INTERRUPT_STATE)
1485 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1489 *interrupted = TRUE;
1492 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1493 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1497 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1499 MonoThreadInfo *info;
1500 MonoThreadInfoInterruptToken *previous_token;
1502 g_assert (interrupted);
1503 *interrupted = FALSE;
1505 info = mono_thread_info_current ();
1508 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1510 /* only the installer can uninstall the token */
1511 g_assert (previous_token);
1513 if (previous_token == INTERRUPT_STATE) {
1514 /* if it is interrupted, then it is going to be freed in finish interrupt */
1515 *interrupted = TRUE;
1517 g_free (previous_token);
1520 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1521 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1524 static MonoThreadInfoInterruptToken*
1525 set_interrupt_state (MonoThreadInfo *info)
1527 MonoThreadInfoInterruptToken *token, *previous_token;
1531 /* Atomically obtain the token the thread is
1532 * waiting on, and change it to a flag value. */
1535 previous_token = info->interrupt_token;
1537 /* Already interrupted */
1538 if (previous_token == INTERRUPT_STATE) {
1543 token = previous_token;
1544 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1550 * mono_thread_info_prepare_interrupt:
1552 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1553 * - if the thread calls one of the WaitFor functions, the function will return with
1554 * WAIT_IO_COMPLETION instead of waiting
1555 * - if the thread was waiting when this function was called, the wait will be broken
1557 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1558 * didn't receive the interrupt signal yet, in this case it should call the wait function
1559 * again. This essentially means that the target thread will busy wait until it is ready to
1560 * process the interruption.
1562 MonoThreadInfoInterruptToken*
1563 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1565 MonoThreadInfoInterruptToken *token;
1567 token = set_interrupt_state (info);
1569 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1570 mono_thread_info_get_tid (info), token);
1576 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1578 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1583 g_assert (token->callback);
1585 token->callback (token->data);
1591 mono_thread_info_self_interrupt (void)
1593 MonoThreadInfo *info;
1594 MonoThreadInfoInterruptToken *token;
1596 info = mono_thread_info_current ();
1599 token = set_interrupt_state (info);
1602 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1603 mono_thread_info_get_tid (info));
1606 /* Clear the interrupted flag of the current thread, set with
1607 * mono_thread_info_self_interrupt, so it can wait again */
1609 mono_thread_info_clear_self_interrupt ()
1611 MonoThreadInfo *info;
1612 MonoThreadInfoInterruptToken *previous_token;
1614 info = mono_thread_info_current ();
1617 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1618 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1620 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1624 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1627 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1631 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1635 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1636 g_string_append_printf (text, "not waiting");
1637 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1638 g_string_append_printf (text, "interrupted state");
1640 g_string_append_printf (text, "waiting");
1644 mono_thread_info_is_current (MonoThreadInfo *info)
1646 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1649 MonoThreadInfoWaitRet
1650 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1652 MonoOSEventWaitRet res;
1654 res = mono_os_event_wait_one (&thread_handle->event, timeout);
1655 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1656 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1657 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1658 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1659 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1660 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1662 g_error ("%s: unknown res value %d", __func__, res);
1665 MonoThreadInfoWaitRet
1666 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1668 MonoOSEventWaitRet res;
1669 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1672 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1673 if (background_change_event)
1674 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1676 for (i = 0; i < nhandles; ++i)
1677 thread_events [i] = &thread_handles [i]->event;
1679 if (background_change_event)
1680 thread_events [nhandles ++] = background_change_event;
1682 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout);
1683 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1684 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1685 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1686 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1687 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1688 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1690 g_error ("%s: unknown res value %d", __func__, res);