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 /* Pump the HP queue while the thread is alive.*/
430 mono_thread_hazardous_try_free_some ();
432 small_id = info->small_id;
434 /* We only enter the GC unsafe region, as when exiting this function, the thread
435 * will be detached, and the current MonoThreadInfo* will be destroyed. */
436 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
438 THREADS_DEBUG ("unregistering info %p\n", info);
440 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
443 * TLS destruction order is not reliable so small_id might be cleaned up
446 #ifndef HAVE_KW_THREAD
447 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
450 /* we need to duplicate it, as the info->handle is going
451 * to be closed when unregistering from the platform */
452 handle = mono_threads_open_thread_handle (info->handle);
455 First perform the callback that requires no locks.
456 This callback has the potential of taking other locks, so we do it before.
457 After it completes, the thread remains functional.
459 if (threads_callbacks.thread_detach)
460 threads_callbacks.thread_detach (info);
462 mono_thread_info_suspend_lock_with_info (info);
465 Now perform the callback that must be done under locks.
466 This will render the thread useless and non-suspendable, so it must
467 be done while holding the suspend lock to give no other thread chance
470 if (threads_callbacks.thread_unregister)
471 threads_callbacks.thread_unregister (info);
473 /* The thread is no longer active, so unref its handle */
474 mono_threads_close_thread_handle (info->handle);
477 result = mono_thread_info_remove (info);
479 mono_threads_transition_detach (info);
481 mono_thread_info_suspend_unlock ();
483 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
485 /*now it's safe to free the thread info.*/
486 mono_thread_hazardous_try_free (info, free_thread_info);
488 mono_thread_small_id_free (small_id);
490 mono_threads_signal_thread_handle (handle);
492 mono_threads_close_thread_handle (handle);
496 thread_exited_dtor (void *arg)
498 #if defined(__MACH__)
500 * Since we use pthread dtors to clean up thread data, if a thread
501 * is attached to the runtime by another pthread dtor after our dtor
502 * has ran, it will never be detached, leading to various problems
503 * since the thread ids etc. will be reused while they are still in
504 * the threads hashtables etc.
505 * Dtors are called in a loop until all user tls entries are 0,
506 * but the loop has a maximum count (4), so if we set the tls
507 * variable every time, it will remain set when system tls dtors
508 * are ran. This allows mono_thread_info_is_exiting () to detect
509 * whenever the thread is exiting, even if it is executed from a
510 * system tls dtor (i.e. obj-c dealloc methods).
512 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
517 mono_thread_info_current_unchecked (void)
519 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
524 mono_thread_info_current (void)
526 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
530 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
533 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
534 The way to distinguish between before, during and after cleanup is the following:
536 -If the TLS key is set, cleanup has not begun;
537 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
538 -If the thread is nowhere to be found, cleanup has finished.
540 We cannot function after cleanup since there's no way to ensure what will happen.
544 /*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 */
545 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
551 mono_thread_info_get_small_id (void)
553 #ifdef HAVE_KW_THREAD
556 gpointer val = mono_native_tls_get_value (small_id_key);
559 return GPOINTER_TO_INT (val) - 1;
564 mono_thread_info_list_head (void)
570 * mono_threads_attach_tools_thread
572 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
574 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
575 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
578 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
579 * doing things like resolving backtraces in their background processing thread.
582 mono_threads_attach_tools_thread (void)
585 MonoThreadInfo *info;
587 /* Must only be called once */
588 g_assert (!mono_native_tls_get_value (thread_info_key));
590 while (!mono_threads_inited) {
591 mono_thread_info_usleep (10);
594 info = mono_thread_info_attach (&dummy);
597 info->tools_thread = TRUE;
601 mono_thread_info_attach (void *baseptr)
603 MonoThreadInfo *info;
604 if (!mono_threads_inited)
607 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
608 * thread is created before an embedding API user initialized Mono. */
609 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
612 g_assert (mono_threads_inited);
615 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
617 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
618 THREADS_DEBUG ("attaching %p\n", info);
619 if (!register_thread (info, baseptr))
621 } else if (threads_callbacks.thread_attach) {
622 threads_callbacks.thread_attach (info);
628 mono_thread_info_detach (void)
630 MonoThreadInfo *info;
631 if (!mono_threads_inited)
633 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
634 * is created before an embedding API user initialized Mono. */
635 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
638 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
640 THREADS_DEBUG ("detaching %p\n", info);
641 unregister_thread (info);
642 mono_native_tls_set_value (thread_info_key, NULL);
647 * mono_thread_info_is_exiting:
649 * Return whenever the current thread is exiting, i.e. it is running pthread
653 mono_thread_info_is_exiting (void)
655 #if defined(__MACH__)
656 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
664 thread_info_key_dtor (void *arg)
666 /* Put the MonoThreadInfo back for the duration of the
667 * unregister code. In some circumstances the thread needs to
668 * take the GC lock which may block which requires a coop
669 * state transition. */
670 mono_native_tls_set_value (thread_info_key, arg);
671 unregister_thread (arg);
672 mono_native_tls_set_value (thread_info_key, NULL);
677 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
680 threads_callbacks = *callbacks;
681 thread_info_size = info_size;
682 const char *sleepLimit;
684 res = mono_native_tls_alloc (&thread_info_key, NULL);
685 res = mono_native_tls_alloc (&thread_exited_key, NULL);
687 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
688 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
693 #ifndef HAVE_KW_THREAD
694 res = mono_native_tls_alloc (&small_id_key, NULL);
698 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
700 long threshold = strtol(sleepLimit, NULL, 10);
701 if ((errno == 0) && (threshold >= 40)) {
702 sleepAbortDuration = threshold;
703 sleepWarnDuration = threshold / 20;
705 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
708 mono_os_sem_init (&global_suspend_semaphore, 1);
709 mono_os_sem_init (&suspend_semaphore, 0);
711 mono_lls_init (&thread_list, NULL);
712 mono_thread_smr_init ();
713 mono_threads_suspend_init ();
714 mono_threads_coop_init ();
715 mono_threads_platform_init ();
717 #if defined(__MACH__)
718 mono_mach_init (thread_info_key);
721 mono_threads_inited = TRUE;
723 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
727 mono_threads_signals_init (void)
729 mono_threads_suspend_init_signals ();
733 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
735 runtime_callbacks = *callbacks;
738 MonoThreadInfoRuntimeCallbacks *
739 mono_threads_get_runtime_callbacks (void)
741 return &runtime_callbacks;
745 mono_thread_info_core_resume (MonoThreadInfo *info)
747 gboolean res = FALSE;
749 switch (mono_threads_transition_request_resume (info)) {
756 case ResumeInitSelfResume:
757 resume_self_suspended (info);
760 case ResumeInitAsyncResume:
761 resume_async_suspended (info);
764 case ResumeInitBlockingResume:
765 resume_blocking_suspended (info);
774 mono_thread_info_resume (MonoNativeThreadId tid)
776 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
777 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
778 MonoThreadInfo *info;
780 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
782 mono_thread_info_suspend_lock ();
784 info = mono_thread_info_lookup (tid); /*info on HP1*/
790 result = mono_thread_info_core_resume (info);
792 //Wait for the pending resume to finish
793 mono_threads_wait_pending_operations ();
796 mono_thread_info_suspend_unlock ();
797 mono_hazard_pointer_clear (hp, 1);
802 mono_thread_info_begin_suspend (MonoThreadInfo *info)
804 switch (mono_threads_transition_request_async_suspension (info)) {
805 case AsyncSuspendAlreadySuspended:
806 case AsyncSuspendBlocking:
808 case AsyncSuspendWait:
809 mono_threads_add_to_pending_operation_set (info);
811 case AsyncSuspendInitSuspend:
812 return begin_async_suspend (info, FALSE);
814 g_assert_not_reached ();
819 mono_thread_info_begin_resume (MonoThreadInfo *info)
821 return mono_thread_info_core_resume (info);
825 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
826 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
829 is_thread_in_critical_region (MonoThreadInfo *info)
833 gpointer stack_start;
834 MonoThreadUnwindState *state;
836 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
839 /* Are we inside a system critical region? */
840 if (info->inside_critical_region)
843 /* Are we inside a GC critical region? */
844 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
848 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
849 state = mono_thread_info_get_suspend_state (info);
850 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
853 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
854 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
855 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
858 if (threads_callbacks.ip_in_critical_region)
859 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
861 ji = mono_jit_info_table_find (
862 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
863 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
868 method = mono_jit_info_get_method (ji);
870 return threads_callbacks.mono_method_is_critical (method);
874 mono_thread_info_in_critical_location (MonoThreadInfo *info)
876 return is_thread_in_critical_region (info);
880 The return value is only valid until a matching mono_thread_info_resume is called
882 static MonoThreadInfo*
883 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
885 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
886 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
890 switch (mono_threads_transition_request_async_suspension (info)) {
891 case AsyncSuspendAlreadySuspended:
892 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
894 case AsyncSuspendWait:
895 mono_threads_add_to_pending_operation_set (info);
897 case AsyncSuspendInitSuspend:
898 if (!begin_async_suspend (info, interrupt_kernel)) {
899 mono_hazard_pointer_clear (hp, 1);
903 case AsyncSuspendBlocking:
904 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
905 mono_threads_suspend_abort_syscall (info);
909 g_assert_not_reached ();
912 //Wait for the pending suspend to finish
913 mono_threads_wait_pending_operations ();
915 if (!check_async_suspend (info)) {
916 mono_thread_info_core_resume (info);
917 mono_threads_wait_pending_operations ();
918 mono_hazard_pointer_clear (hp, 1);
924 static MonoThreadInfo*
925 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
927 MonoThreadInfo *info = NULL;
928 int sleep_duration = 0;
930 if (!(info = suspend_sync (id, interrupt_kernel))) {
931 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
935 /*WARNING: We now are in interrupt context until we resume the thread. */
936 if (!is_thread_in_critical_region (info))
939 if (!mono_thread_info_core_resume (info)) {
940 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
943 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
945 /* Wait for the pending resume to finish */
946 mono_threads_wait_pending_operations ();
948 if (sleep_duration == 0)
949 mono_thread_info_yield ();
951 g_usleep (sleep_duration);
953 sleep_duration += 10;
959 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
962 MonoThreadInfo *info = NULL;
963 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
965 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
966 /*FIXME: unify this with self-suspend*/
967 g_assert (id != mono_native_thread_id_get ());
969 /* This can block during stw */
970 mono_thread_info_suspend_lock ();
971 mono_threads_begin_global_suspend ();
973 info = suspend_sync_nolock (id, interrupt_kernel);
977 switch (result = callback (info, user_data)) {
978 case MonoResumeThread:
979 mono_hazard_pointer_set (hp, 1, info);
980 mono_thread_info_core_resume (info);
981 mono_threads_wait_pending_operations ();
984 g_assert (!mono_threads_is_coop_enabled ());
987 g_error ("Invalid suspend_and_run callback return value %d", result);
991 mono_hazard_pointer_clear (hp, 1);
992 mono_threads_end_global_suspend ();
993 mono_thread_info_suspend_unlock ();
997 Inject an assynchronous call into the target thread. The target thread must be suspended and
998 only a single async call can be setup for a given suspend cycle.
999 This async call must cause stack unwinding as the current implementation doesn't save enough state
1000 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1001 currently used only to deliver exceptions.
1004 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1006 /* An async call can only be setup on an async suspended thread */
1007 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1008 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1009 g_assert (!info->async_target);
1010 info->async_target = target_func;
1011 /* This is not GC tracked */
1012 info->user_data = user_data;
1016 The suspend lock is held during any suspend in progress.
1017 A GC that has safepoints must take this lock as part of its
1018 STW to make sure no unsafe pending suspend is in progress.
1022 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1025 g_assert (mono_thread_info_is_current (info));
1026 g_assert (mono_thread_info_is_live (info));
1028 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1030 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1031 g_assert (res != -1);
1033 MONO_EXIT_GC_SAFE_WITH_INFO;
1037 mono_thread_info_suspend_lock (void)
1039 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1043 mono_thread_info_suspend_unlock (void)
1045 mono_os_sem_post (&global_suspend_semaphore);
1049 * This is a very specific function whose only purpose is to
1050 * break a given thread from socket syscalls.
1052 * This only exists because linux won't fail a call to connect
1053 * if the underlying is closed.
1055 * TODO We should cleanup and unify this with the other syscall abort
1059 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1061 MonoThreadHazardPointers *hp;
1062 MonoThreadInfo *info;
1064 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1067 hp = mono_hazard_pointer_get ();
1068 info = mono_thread_info_lookup (tid);
1072 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1073 mono_hazard_pointer_clear (hp, 1);
1077 mono_thread_info_suspend_lock ();
1078 mono_threads_begin_global_suspend ();
1080 mono_threads_suspend_abort_syscall (info);
1081 mono_threads_wait_pending_operations ();
1083 mono_hazard_pointer_clear (hp, 1);
1085 mono_threads_end_global_suspend ();
1086 mono_thread_info_suspend_unlock ();
1090 * mono_thread_info_set_is_async_context:
1092 * Set whenever the current thread is in an async context. Some runtime functions might behave
1093 * differently while in an async context in order to be async safe.
1096 mono_thread_info_set_is_async_context (gboolean async_context)
1098 MonoThreadInfo *info = mono_thread_info_current ();
1101 info->is_async_context = async_context;
1105 mono_thread_info_is_async_context (void)
1107 MonoThreadInfo *info = mono_thread_info_current ();
1110 return info->is_async_context;
1117 MonoThreadStart start_routine;
1118 gpointer start_routine_arg;
1119 MonoCoopSem registered;
1120 MonoThreadHandle *handle;
1124 create_thread_data_destroy (gpointer data)
1126 CreateThreadData *thread_data;
1128 thread_data = (CreateThreadData*) data;
1130 mono_coop_sem_destroy (&thread_data->registered);
1131 g_free (thread_data);
1135 inner_start_thread (gpointer data)
1137 CreateThreadData *thread_data;
1138 MonoThreadInfo *info;
1139 MonoThreadStart start_routine;
1140 gpointer start_routine_arg;
1141 gsize start_routine_res;
1144 thread_data = (CreateThreadData*) data;
1145 g_assert (thread_data);
1147 start_routine = thread_data->start_routine;
1148 start_routine_arg = thread_data->start_routine_arg;
1150 info = mono_thread_info_attach (&dummy);
1151 info->runtime_thread = TRUE;
1153 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1155 mono_coop_sem_post (&thread_data->registered);
1157 mono_refcount_dec (thread_data);
1159 /* thread_data is not valid anymore */
1162 /* Run the actual main function of the thread */
1163 start_routine_res = start_routine (start_routine_arg);
1165 mono_thread_info_exit (start_routine_res);
1167 g_assert_not_reached ();
1171 * mono_threads_create_thread:
1173 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1174 * Returns: a windows or io-layer handle for the thread.
1177 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1179 CreateThreadData *thread_data;
1181 MonoThreadHandle *ret;
1183 thread_data = g_new0 (CreateThreadData, 1);
1184 mono_refcount_init (thread_data, create_thread_data_destroy);
1185 thread_data->start_routine = start;
1186 thread_data->start_routine_arg = arg;
1187 mono_coop_sem_init (&thread_data->registered, 0);
1189 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
1191 /* ref is not going to be decremented in inner_start_thread */
1192 mono_refcount_dec (thread_data);
1197 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1198 g_assert (res == 0);
1200 ret = thread_data->handle;
1204 mono_refcount_dec (thread_data);
1210 * mono_thread_info_get_stack_bounds:
1212 * Return the address and size of the current threads stack. Return NULL as the
1213 * stack address if the stack address cannot be determined.
1216 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1218 guint8 *current = (guint8 *)&stsize;
1219 mono_threads_platform_get_stack_bounds (staddr, stsize);
1223 /* Sanity check the result */
1224 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1226 /* When running under emacs, sometimes staddr is not aligned to a page size */
1227 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1231 mono_thread_info_yield (void)
1233 return mono_threads_platform_yield ();
1235 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1236 static MonoCoopMutex sleep_mutex;
1237 static MonoCoopCond sleep_cond;
1240 sleep_initialize (void)
1242 mono_coop_mutex_init (&sleep_mutex);
1243 mono_coop_cond_init (&sleep_cond);
1247 sleep_interrupt (gpointer data)
1249 mono_coop_mutex_lock (&sleep_mutex);
1250 mono_coop_cond_broadcast (&sleep_cond);
1251 mono_coop_mutex_unlock (&sleep_mutex);
1254 static inline guint32
1255 sleep_interruptable (guint32 ms, gboolean *alerted)
1259 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1264 if (ms != MONO_INFINITE_WAIT)
1265 end = mono_msec_ticks() + ms;
1267 mono_lazy_initialize (&sleep_init, sleep_initialize);
1269 mono_coop_mutex_lock (&sleep_mutex);
1272 if (ms != MONO_INFINITE_WAIT) {
1273 now = mono_msec_ticks();
1278 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1280 mono_coop_mutex_unlock (&sleep_mutex);
1281 return WAIT_IO_COMPLETION;
1284 if (ms != MONO_INFINITE_WAIT)
1285 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1287 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1289 mono_thread_info_uninstall_interrupt (alerted);
1291 mono_coop_mutex_unlock (&sleep_mutex);
1292 return WAIT_IO_COMPLETION;
1296 mono_coop_mutex_unlock (&sleep_mutex);
1302 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1305 MonoThreadInfo *info;
1307 mono_thread_info_yield ();
1309 info = mono_thread_info_current ();
1310 if (info && mono_thread_info_is_interrupt_state (info))
1311 return WAIT_IO_COMPLETION;
1317 return sleep_interruptable (ms, alerted);
1321 if (ms == MONO_INFINITE_WAIT) {
1324 Sleep (G_MAXUINT32);
1326 sleep (G_MAXUINT32);
1331 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1332 struct timespec start, target;
1334 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1335 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1336 g_assert (ret == 0);
1339 target.tv_sec += ms / 1000;
1340 target.tv_nsec += (ms % 1000) * 1000000;
1341 if (target.tv_nsec > 999999999) {
1342 target.tv_nsec -= 999999999;
1347 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1352 struct timespec req, rem;
1354 req.tv_sec = ms / 1000;
1355 req.tv_nsec = (ms % 1000) * 1000000;
1358 memset (&rem, 0, sizeof (rem));
1359 ret = nanosleep (&req, &rem);
1361 #endif /* __linux__ */
1370 mono_thread_info_usleep (guint64 us)
1379 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1381 return ((MonoThreadInfo*)info)->tls [key];
1385 * mono_threads_info_tls_set:
1387 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1388 * values of TLS variables for threads other than the current thread.
1389 * This should only be used for infrequently changing TLS variables, and it should
1390 * be paired with setting the real TLS variable since this provides no GC tracking.
1393 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1395 ((MonoThreadInfo*)info)->tls [key] = value;
1398 #if defined(__native_client__)
1399 void nacl_shutdown_gc_thread(void);
1403 * mono_thread_info_exit:
1405 * Exit the current thread.
1406 * This function doesn't return.
1409 mono_thread_info_exit (gsize exit_code)
1411 #if defined(__native_client__)
1412 nacl_shutdown_gc_thread();
1415 mono_thread_info_detach ();
1417 mono_threads_platform_exit (0);
1421 * mono_threads_open_thread_handle:
1423 * Duplicate the handle. The handle needs to be closed by calling
1424 * mono_threads_close_thread_handle () when it is no longer needed.
1427 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1429 return mono_refcount_inc (thread_handle);
1433 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1435 mono_refcount_dec (thread_handle);
1439 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1441 mono_os_event_set (&thread_handle->event);
1444 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1446 struct _MonoThreadInfoInterruptToken {
1447 void (*callback) (gpointer data);
1452 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1454 * - @callback: must be able to be called from another thread and always cancel the wait
1455 * - @data: passed to the callback
1456 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1457 * if set to TRUE, it must mean that the thread is in interrupted state
1460 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1462 MonoThreadInfo *info;
1463 MonoThreadInfoInterruptToken *previous_token, *token;
1465 g_assert (callback);
1467 g_assert (interrupted);
1468 *interrupted = FALSE;
1470 info = mono_thread_info_current ();
1473 /* The memory of this token can be freed at 2 places:
1474 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1475 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1476 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1477 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1478 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1479 token->callback = callback;
1482 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1484 if (previous_token) {
1485 if (previous_token != INTERRUPT_STATE)
1486 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1490 *interrupted = TRUE;
1493 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1494 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1498 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1500 MonoThreadInfo *info;
1501 MonoThreadInfoInterruptToken *previous_token;
1503 g_assert (interrupted);
1504 *interrupted = FALSE;
1506 info = mono_thread_info_current ();
1509 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1511 /* only the installer can uninstall the token */
1512 g_assert (previous_token);
1514 if (previous_token == INTERRUPT_STATE) {
1515 /* if it is interrupted, then it is going to be freed in finish interrupt */
1516 *interrupted = TRUE;
1518 g_free (previous_token);
1521 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1522 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1525 static MonoThreadInfoInterruptToken*
1526 set_interrupt_state (MonoThreadInfo *info)
1528 MonoThreadInfoInterruptToken *token, *previous_token;
1532 /* Atomically obtain the token the thread is
1533 * waiting on, and change it to a flag value. */
1536 previous_token = info->interrupt_token;
1538 /* Already interrupted */
1539 if (previous_token == INTERRUPT_STATE) {
1544 token = previous_token;
1545 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1551 * mono_thread_info_prepare_interrupt:
1553 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1554 * - if the thread calls one of the WaitFor functions, the function will return with
1555 * WAIT_IO_COMPLETION instead of waiting
1556 * - if the thread was waiting when this function was called, the wait will be broken
1558 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1559 * didn't receive the interrupt signal yet, in this case it should call the wait function
1560 * again. This essentially means that the target thread will busy wait until it is ready to
1561 * process the interruption.
1563 MonoThreadInfoInterruptToken*
1564 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1566 MonoThreadInfoInterruptToken *token;
1568 token = set_interrupt_state (info);
1570 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1571 mono_thread_info_get_tid (info), token);
1577 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1579 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1584 g_assert (token->callback);
1586 token->callback (token->data);
1592 mono_thread_info_self_interrupt (void)
1594 MonoThreadInfo *info;
1595 MonoThreadInfoInterruptToken *token;
1597 info = mono_thread_info_current ();
1600 token = set_interrupt_state (info);
1603 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1604 mono_thread_info_get_tid (info));
1607 /* Clear the interrupted flag of the current thread, set with
1608 * mono_thread_info_self_interrupt, so it can wait again */
1610 mono_thread_info_clear_self_interrupt ()
1612 MonoThreadInfo *info;
1613 MonoThreadInfoInterruptToken *previous_token;
1615 info = mono_thread_info_current ();
1618 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1619 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1621 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1625 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1628 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1632 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1636 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1637 g_string_append_printf (text, "not waiting");
1638 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1639 g_string_append_printf (text, "interrupted state");
1641 g_string_append_printf (text, "waiting");
1645 mono_thread_info_is_current (MonoThreadInfo *info)
1647 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1650 MonoThreadInfoWaitRet
1651 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1653 MonoOSEventWaitRet res;
1655 res = mono_os_event_wait_one (&thread_handle->event, timeout);
1656 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1657 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1658 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1659 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1660 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1661 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1663 g_error ("%s: unknown res value %d", __func__, res);
1666 MonoThreadInfoWaitRet
1667 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1669 MonoOSEventWaitRet res;
1670 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1673 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1674 if (background_change_event)
1675 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1677 for (i = 0; i < nhandles; ++i)
1678 thread_events [i] = &thread_handles [i]->event;
1680 if (background_change_event)
1681 thread_events [nhandles ++] = background_change_event;
1683 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout);
1684 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1685 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1686 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1687 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1688 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1689 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1691 g_error ("%s: unknown res value %d", __func__, res);