Whittle away at warnings: (#18874)
[mono-project.git] / mono / utils / mono-threads.c
blobfbf561891e352bdcc136d77fc2ef4621d7582f34
1 /**
2 * \file
3 * Low-level threading
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
15 /* enable pthread extensions */
16 #ifdef TARGET_MACH
17 #define _DARWIN_C_SOURCE
18 #endif
20 #include <mono/utils/mono-compiler.h>
21 #include <mono/utils/mono-os-semaphore.h>
22 #include <mono/utils/mono-threads.h>
23 #include <mono/utils/mono-tls.h>
24 #include <mono/utils/hazard-pointer.h>
25 #include <mono/utils/mono-memory-model.h>
26 #include <mono/utils/mono-mmap.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-lazy-init.h>
30 #include <mono/utils/mono-coop-mutex.h>
31 #include <mono/utils/mono-coop-semaphore.h>
32 #include <mono/utils/mono-threads-coop.h>
33 #include <mono/utils/mono-threads-debug.h>
34 #include <mono/utils/os-event.h>
35 #include <mono/utils/w32api.h>
37 #include <errno.h>
38 #include <mono/utils/mono-errno.h>
40 #if defined(__MACH__)
41 #include <mono/utils/mach-support.h>
42 #endif
44 #if _MSC_VER
45 #pragma warning(disable:4312) // FIXME pointer cast to different size
46 #endif
49 Mutex that makes sure only a single thread can be suspending others.
50 Suspend is a very racy operation since it requires restarting until
51 the target thread is not on an unsafe region.
53 We could implement this using critical regions, but would be much much
54 harder for an operation that is hardly performance critical.
56 The GC has to acquire this lock before starting a STW to make sure
57 a runtime suspend won't make it wronly see a thread in a safepoint
58 when it is in fact not.
60 This has to be a naked locking primitive, and not a coop aware one, as
61 it needs to be usable when destroying thread_info_key, the TLS key for
62 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
63 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
64 to an assertion error. We then simply switch state manually in
65 mono_thread_info_suspend_lock_with_info.
67 static MonoSemType global_suspend_semaphore;
69 static size_t thread_info_size;
70 static MonoThreadInfoCallbacks threads_callbacks;
71 const MonoThreadInfoRuntimeCallbacks *mono_runtime_callbacks;
72 static MonoNativeTlsKey thread_info_key, thread_exited_key;
73 #ifdef MONO_KEYWORD_THREAD
74 static MONO_KEYWORD_THREAD gint32 tls_small_id = -1;
75 #else
76 static MonoNativeTlsKey small_id_key;
77 #endif
78 static MonoLinkedListSet thread_list;
79 static gboolean mono_threads_inited = FALSE;
81 static MonoSemType suspend_semaphore;
82 static size_t pending_suspends;
84 static mono_mutex_t join_mutex;
86 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state.state)
88 /*warn at 50 ms*/
89 #define SLEEP_DURATION_BEFORE_WARNING (50)
90 /*never aborts */
91 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
93 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
94 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
96 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
98 void
99 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
101 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
102 mono_atomic_inc_i32 (&abort_posts);
103 mono_os_sem_post (&suspend_semaphore);
106 void
107 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
109 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
110 // check that the thread is really in a valid suspended state.
111 g_assert (mono_thread_info_get_suspend_state (info) != NULL);
112 mono_atomic_inc_i32 (&suspend_posts);
113 mono_os_sem_post (&suspend_semaphore);
116 void
117 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
119 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
120 mono_atomic_inc_i32 (&resume_posts);
121 mono_os_sem_post (&suspend_semaphore);
124 typedef enum {
125 BeginSuspendFail = 0,
126 BeginSuspendOkPreemptive = 1,
127 BeginSuspendOkCooperative = 2,
128 BeginSuspendOkNoWait = 3,
129 } BeginSuspendResult;
131 static BeginSuspendResult
132 begin_cooperative_suspend (MonoThreadInfo *info)
134 /* There's nothing else to do after we async request the thread to suspend */
135 mono_threads_add_to_pending_operation_set (info);
136 return BeginSuspendOkCooperative;
139 static BeginSuspendResult
140 begin_preemptive_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
142 if (mono_threads_suspend_begin_async_suspend (info, interrupt_kernel))
143 return BeginSuspendOkPreemptive;
144 else
145 return BeginSuspendFail;
148 static BeginSuspendResult
149 begin_suspend_for_running_thread (MonoThreadInfo *info, gboolean interrupt_kernel)
151 /* If we're using full cooperative suspend or hybrid suspend,
152 * cooperatively suspend RUNNING threads */
153 if (mono_threads_are_safepoints_enabled ())
154 return begin_cooperative_suspend (info);
155 else
156 return begin_preemptive_suspend (info, interrupt_kernel);
159 static gboolean
160 thread_is_cooperative_suspend_aware (MonoThreadInfo *info)
162 return (mono_threads_is_cooperative_suspension_enabled () || mono_atomic_load_i32 (&(info->coop_aware_thread)));
165 static BeginSuspendResult
166 begin_suspend_for_blocking_thread (MonoThreadInfo *info, gboolean interrupt_kernel, MonoThreadSuspendPhase phase, gboolean coop_aware_thread, gboolean *did_interrupt)
168 // if a thread can't transition to blocking, we certainly shouldn't be
169 // trying to suspend it like it's blocking.
170 g_assert (mono_threads_is_blocking_transition_enabled ());
171 // with hybrid suspend, preemptively suspend blocking threads (if thread is not coop aware),
172 // otherwise blocking already counts as suspended.
173 if (mono_threads_is_hybrid_suspension_enabled () && !coop_aware_thread) {
174 if (did_interrupt) {
175 *did_interrupt = interrupt_kernel;
177 switch (phase) {
178 case MONO_THREAD_SUSPEND_PHASE_INITIAL:
179 /* In hybrid suspend, in the first phase, a thread in
180 * blocking can continue running (and possibly
181 * self-suspend). We'll preemptively suspend it in the
182 * second phase, if thread is not coop aware. */
183 return BeginSuspendOkNoWait;
184 case MONO_THREAD_SUSPEND_PHASE_MOPUP:
185 return begin_preemptive_suspend (info, interrupt_kernel);
186 default:
187 g_assert_not_reached ();
189 } else {
190 if (did_interrupt)
191 *did_interrupt = FALSE;
192 // In full cooperative suspend, treat a thread in BLOCKING as
193 // already suspended and don't wait for it.
194 return BeginSuspendOkNoWait;
198 static gboolean
199 check_async_suspend (MonoThreadInfo *info, BeginSuspendResult result)
201 switch (result) {
202 case BeginSuspendOkCooperative:
203 return TRUE;
204 case BeginSuspendOkPreemptive:
205 return mono_threads_suspend_check_suspend_result (info);
206 case BeginSuspendFail:
207 return FALSE;
208 case BeginSuspendOkNoWait:
209 return TRUE;
210 default:
211 g_assert_not_reached ();
215 static void
216 resume_async_suspended (MonoThreadInfo *info)
218 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
219 g_assert_not_reached ();
221 g_assert (mono_threads_suspend_begin_async_resume (info));
224 static void
225 resume_self_suspended (MonoThreadInfo* info)
227 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
228 mono_os_sem_post (&info->resume_semaphore);
231 void
232 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
234 int res;
235 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
236 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
237 g_assert (res != -1);
240 static void
241 resume_blocking_suspended (MonoThreadInfo* info)
243 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
244 mono_os_sem_post (&info->resume_semaphore);
247 void
248 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
250 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
251 ++pending_suspends;
252 mono_atomic_inc_i32 (&pending_ops);
255 void
256 mono_threads_begin_global_suspend (void)
258 size_t ps = pending_suspends;
259 if (G_UNLIKELY (ps != 0))
260 g_error ("pending_suspends = %d, but must be 0", ps);
261 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,
262 abort_posts, waits_done, pending_ops);
263 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
264 mono_threads_coop_begin_global_suspend ();
267 void
268 mono_threads_end_global_suspend (void)
270 size_t ps = pending_suspends;
271 if (G_UNLIKELY (ps != 0))
272 g_error ("pending_suspends = %d, but must be 0", ps);
273 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
274 abort_posts, waits_done, pending_ops);
275 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
276 mono_threads_coop_end_global_suspend ();
279 static void
280 dump_threads (void)
282 MonoThreadInfo *cur = mono_thread_info_current ();
284 g_async_safe_printf ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
285 g_async_safe_printf ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
286 g_async_safe_printf ("\t0x1\t- detached (GOOD, unless the thread is running managed code)\n");
287 g_async_safe_printf ("\t0x2\t- running (BAD, unless it's the gc thread)\n");
288 g_async_safe_printf ("\t0x?03\t- async suspended (GOOD)\n");
289 g_async_safe_printf ("\t0x?04\t- self suspended (GOOD)\n");
290 g_async_safe_printf ("\t0x?05\t- async suspend requested (BAD)\n");
291 g_async_safe_printf ("\t0x6\t- blocking (BAD, unless there's no suspend initiator)\n");
292 g_async_safe_printf ("\t0x?07\t- blocking async suspended (GOOD)\n");
293 g_async_safe_printf ("\t0x?08\t- blocking self suspended (GOOD)\n");
294 g_async_safe_printf ("\t0x?09\t- blocking suspend requested (BAD in coop; GOOD in hybrid)\n");
296 FOREACH_THREAD_SAFE_ALL (info) {
297 #ifdef TARGET_MACH
298 char thread_name [256] = { 0 };
299 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
301 g_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" : "" );
302 #else
303 g_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" : "" );
304 #endif
305 } FOREACH_THREAD_SAFE_END
308 gboolean
309 mono_threads_wait_pending_operations (void)
311 int i;
312 int c = pending_suspends;
314 /* Wait threads to park */
315 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
316 if (pending_suspends) {
317 MonoStopwatch suspension_time;
318 mono_stopwatch_start (&suspension_time);
319 for (i = 0; i < pending_suspends; ++i) {
320 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
321 mono_atomic_inc_i32 (&waits_done);
322 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
323 continue;
324 mono_stopwatch_stop (&suspension_time);
326 dump_threads ();
328 g_async_safe_printf ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
329 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
331 mono_stopwatch_stop (&suspension_time);
332 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
336 pending_suspends = 0;
338 return c > 0;
342 //Thread initialization code
344 static void
345 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
347 if (retain != 0)
348 mono_hazard_pointer_clear (hp, 0);
349 if (retain != 1)
350 mono_hazard_pointer_clear (hp, 1);
351 if (retain != 2)
352 mono_hazard_pointer_clear (hp, 2);
356 If return non null Hazard Pointer 1 holds the return value.
358 MonoThreadInfo*
359 mono_thread_info_lookup (MonoNativeThreadId id)
361 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
363 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
364 mono_hazard_pointer_clear_all (hp, -1);
365 return NULL;
368 mono_hazard_pointer_clear_all (hp, 1);
369 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
372 static gboolean
373 mono_thread_info_insert (MonoThreadInfo *info)
375 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
377 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
378 mono_hazard_pointer_clear_all (hp, -1);
379 return FALSE;
382 mono_hazard_pointer_clear_all (hp, -1);
383 return TRUE;
386 static gboolean
387 mono_thread_info_remove (MonoThreadInfo *info)
389 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
390 gboolean res;
392 THREADS_DEBUG ("removing info %p\n", info);
393 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
394 mono_hazard_pointer_clear_all (hp, -1);
395 return res;
398 static void
399 free_thread_info (gpointer mem)
401 MonoThreadInfo *info = (MonoThreadInfo *) mem;
403 mono_os_sem_destroy (&info->resume_semaphore);
404 mono_threads_suspend_free (info);
406 g_free (info);
410 * mono_thread_info_register_small_id
412 * Registers a small ID for the current thread. This is a 16-bit value uniquely
413 * identifying the current thread. If the current thread already has a small ID
414 * assigned, that small ID will be returned; otherwise, the newly assigned small
415 * ID is returned.
418 mono_thread_info_register_small_id (void)
420 int small_id = mono_thread_info_get_small_id ();
422 if (small_id != -1)
423 return small_id;
425 small_id = mono_thread_small_id_alloc ();
426 #ifdef MONO_KEYWORD_THREAD
427 tls_small_id = small_id;
428 #else
429 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
430 #endif
431 return small_id;
434 static void
435 thread_handle_destroy (gpointer data)
437 MonoThreadHandle *thread_handle;
439 thread_handle = (MonoThreadHandle*) data;
441 mono_os_event_destroy (&thread_handle->event);
442 g_free (thread_handle);
445 static gboolean
446 register_thread (MonoThreadInfo *info)
448 size_t stsize = 0;
449 guint8 *staddr = NULL;
450 gboolean result;
452 info->small_id = mono_thread_info_register_small_id ();
453 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
455 info->handle = g_new0 (MonoThreadHandle, 1);
456 mono_refcount_init (info->handle, thread_handle_destroy);
457 mono_os_event_init (&info->handle->event, FALSE);
459 mono_os_sem_init (&info->resume_semaphore, 0);
461 /*set TLS early so SMR works */
462 mono_native_tls_set_value (thread_info_key, info);
464 mono_thread_info_get_stack_bounds (&staddr, &stsize);
465 g_assert (staddr);
466 g_assert (stsize);
467 info->stack_start_limit = staddr;
468 info->stack_end = staddr + stsize;
469 info->stackdata = g_byte_array_new ();
471 info->internal_thread_gchandle = G_MAXUINT32;
473 info->profiler_signal_ack = 1;
475 #ifdef USE_WINDOWS_BACKEND
476 info->windows_tib = (PNT_TIB)NtCurrentTeb ();
477 info->win32_apc_info = 0;
478 info->win32_apc_info_io_handle = INVALID_HANDLE_VALUE;
479 #endif
481 mono_threads_suspend_register (info);
483 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
485 if (threads_callbacks.thread_attach) {
486 if (!threads_callbacks.thread_attach (info)) {
487 // g_warning ("thread registation failed\n");
488 mono_native_tls_set_value (thread_info_key, NULL);
489 return FALSE;
494 Transition it before taking any locks or publishing itself to reduce the chance
495 of others witnessing a detached thread.
496 We can reasonably expect that until this thread gets published, no other thread will
497 try to manipulate it.
499 mono_threads_transition_attach (info);
500 mono_thread_info_suspend_lock ();
501 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
502 result = mono_thread_info_insert (info);
503 g_assert (result);
504 mono_thread_info_suspend_unlock ();
506 return TRUE;
509 static void
510 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
512 static void
513 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
515 static void
516 unregister_thread (void *arg)
518 MONO_STACKDATA (gc_unsafe_stackdata);
519 MonoThreadInfo *info;
520 int small_id;
521 gboolean result;
522 MonoThreadHandle* handle;
524 info = (MonoThreadInfo *) arg;
525 g_assertf (info, ""); // f includes __func__
526 g_assert (mono_thread_info_is_current (info));
527 g_assert (mono_thread_info_is_live (info));
529 /* We only enter the GC unsafe region, as when exiting this function, the thread
530 * will be detached, and the current MonoThreadInfo* will be destroyed. */
531 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
533 /* Need to be in GC Unsafe to pump the HP queue - some of the cleanup
534 * methods need to use coop-aware locks. For example: jit_info_table_free_duplicate.
537 /* Pump the HP queue while the thread is alive.*/
538 mono_thread_hazardous_try_free_some ();
540 small_id = info->small_id;
542 THREADS_DEBUG ("unregistering info %p\n", info);
544 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
547 * TLS destruction order is not reliable so small_id might be cleaned up
548 * before us.
550 #ifndef MONO_KEYWORD_THREAD
551 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
552 #endif
554 /* we need to duplicate it, as the info->handle is going
555 * to be closed when unregistering from the platform */
556 handle = mono_threads_open_thread_handle (info->handle);
559 First perform the callback that requires no locks.
560 This callback has the potential of taking other locks, so we do it before.
561 After it completes, the thread remains functional.
563 if (threads_callbacks.thread_detach)
564 threads_callbacks.thread_detach (info);
566 mono_thread_info_suspend_lock_with_info (info);
569 Now perform the callback that must be done under locks.
570 This will render the thread useless and non-suspendable, so it must
571 be done while holding the suspend lock to give no other thread chance
572 to suspend it.
574 if (threads_callbacks.thread_detach_with_lock)
575 threads_callbacks.thread_detach_with_lock (info);
577 /* The thread is no longer active, so unref its handle */
578 mono_threads_close_thread_handle (info->handle);
579 info->handle = NULL;
581 result = mono_thread_info_remove (info);
582 g_assert (result);
583 mono_threads_transition_detach (info);
585 mono_thread_info_suspend_unlock ();
587 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
589 /*now it's safe to free the thread info.*/
590 mono_thread_hazardous_try_free (info, free_thread_info);
592 mono_thread_small_id_free (small_id);
593 // clear the small_id thread local, in case this thread so that if it is reattached while running other TLS key dtors it will get a new small id
594 #ifdef MONO_KEYWORD_THREAD
595 tls_small_id = -1;
596 #else
597 mono_native_tls_set_value (small_id_key, NULL);
598 #endif
600 mono_threads_signal_thread_handle (handle);
602 mono_threads_close_thread_handle (handle);
604 mono_native_tls_set_value (thread_info_key, NULL);
607 static void
608 thread_exited_dtor (void *arg)
610 #if defined(__MACH__)
612 * Since we use pthread dtors to clean up thread data, if a thread
613 * is attached to the runtime by another pthread dtor after our dtor
614 * has ran, it will never be detached, leading to various problems
615 * since the thread ids etc. will be reused while they are still in
616 * the threads hashtables etc.
617 * Dtors are called in a loop until all user tls entries are 0,
618 * but the loop has a maximum count (4), so if we set the tls
619 * variable every time, it will remain set when system tls dtors
620 * are ran. This allows mono_thread_info_is_exiting () to detect
621 * whenever the thread is exiting, even if it is executed from a
622 * system tls dtor (i.e. obj-c dealloc methods).
624 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
625 #endif
628 MonoThreadInfo*
629 mono_thread_info_current_unchecked (void)
631 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
635 MonoThreadInfo*
636 mono_thread_info_current (void)
638 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
639 if (info)
640 return info;
642 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
645 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
646 The way to distinguish between before, during and after cleanup is the following:
648 -If the TLS key is set, cleanup has not begun;
649 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
650 -If the thread is nowhere to be found, cleanup has finished.
652 We cannot function after cleanup since there's no way to ensure what will happen.
654 g_assertf (info, ""); // f includes __func__
656 /*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 */
657 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
659 return info;
663 * mono_thread_info_get_small_id
665 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
666 * identifying the current thread. Returns -1 if the current thread doesn't have
667 * a small ID assigned.
669 * To ensure that the calling thread has a small ID assigned, call either
670 * mono_thread_info_attach or mono_thread_info_register_small_id.
673 mono_thread_info_get_small_id (void)
675 #ifdef MONO_KEYWORD_THREAD
676 return tls_small_id;
677 #else
678 gpointer val = mono_native_tls_get_value (small_id_key);
679 if (!val)
680 return -1;
681 return GPOINTER_TO_INT (val) - 1;
682 #endif
685 MonoLinkedListSet*
686 mono_thread_info_list_head (void)
688 return &thread_list;
691 MonoThreadInfo *
692 mono_thread_info_attach (void)
694 MonoThreadInfo *info;
696 #ifdef HOST_WIN32
697 if (!mono_threads_inited)
699 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
700 * thread is created before an embedding API user initialized Mono. */
701 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
702 return NULL;
704 #endif
706 g_assert (mono_threads_inited);
708 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
709 if (!info) {
710 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
711 THREADS_DEBUG ("attaching %p\n", info);
712 if (!register_thread (info)) {
713 g_free (info);
714 return NULL;
718 return info;
721 void
722 mono_thread_info_detach (void)
724 MonoThreadInfo *info;
726 #ifdef HOST_WIN32
727 if (!mono_threads_inited)
729 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
730 * is created before an embedding API user initialized Mono. */
731 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
732 return;
734 #endif
736 g_assert (mono_threads_inited);
738 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
739 if (info) {
740 THREADS_DEBUG ("detaching %p\n", info);
741 unregister_thread (info);
745 gboolean
746 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
748 g_assertf (info, ""); // f includes __func__
749 g_assert (mono_thread_info_is_current (info));
751 if (info->internal_thread_gchandle == G_MAXUINT32)
752 return FALSE;
754 *gchandle = info->internal_thread_gchandle;
755 return TRUE;
758 void
759 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
761 g_assertf (info, ""); // f includes __func__
762 g_assert (mono_thread_info_is_current (info));
763 g_assert (gchandle != G_MAXUINT32);
764 info->internal_thread_gchandle = gchandle;
767 void
768 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
770 g_assertf (info, ""); // f includes __func__
771 g_assert (mono_thread_info_is_current (info));
772 info->internal_thread_gchandle = G_MAXUINT32;
776 * mono_thread_info_is_exiting:
778 * Return whenever the current thread is exiting, i.e. it is running pthread
779 * dtors.
781 gboolean
782 mono_thread_info_is_exiting (void)
784 #if defined(__MACH__)
785 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
786 return TRUE;
787 #endif
788 return FALSE;
791 #ifndef HOST_WIN32
792 static void
793 thread_info_key_dtor (void *arg)
795 /* Put the MonoThreadInfo back for the duration of the
796 * unregister code. In some circumstances the thread needs to
797 * take the GC lock which may block which requires a coop
798 * state transition. */
799 mono_native_tls_set_value (thread_info_key, arg);
800 unregister_thread (arg);
801 mono_native_tls_set_value (thread_info_key, NULL);
803 #endif
805 MonoThreadInfoFlags
806 mono_thread_info_get_flags (MonoThreadInfo *info)
808 return (MonoThreadInfoFlags)mono_atomic_load_i32 (&info->flags);
811 void
812 mono_thread_info_set_flags (MonoThreadInfoFlags flags)
814 MonoThreadInfo *info = mono_thread_info_current ();
815 MonoThreadInfoFlags old = (MonoThreadInfoFlags)mono_atomic_load_i32 (&info->flags);
817 if (threads_callbacks.thread_flags_changing)
818 threads_callbacks.thread_flags_changing (old, flags);
820 mono_atomic_store_i32 (&info->flags, flags);
822 if (threads_callbacks.thread_flags_changed)
823 threads_callbacks.thread_flags_changed (old, flags);
826 #define MONO_END_INIT_CB GINT_TO_POINTER(-1)
827 static GSList *init_callbacks;
829 void
830 mono_thread_info_wait_inited (void)
832 MonoSemType cb;
833 mono_os_sem_init (&cb, 0);
834 GSList *old = init_callbacks;
836 GSList wait_request;
837 wait_request.data = &cb;
838 wait_request.next = old;
840 while (mono_threads_inited != TRUE) {
841 GSList *old_read = (GSList*)mono_atomic_cas_ptr ((gpointer *) &init_callbacks, &wait_request, old);
843 // Queued up waiter, need to be unstuck
844 if (old_read == old) {
845 break;
846 } else if (old_read == GINT_TO_POINTER (MONO_END_INIT_CB)) {
847 // Is inited
848 return;
849 } else {
850 // We raced with another writer
851 wait_request.next = (GSList *) old_read;
852 old = old_read;
856 while (mono_threads_inited != TRUE) {
857 gboolean timedout = mono_os_sem_timedwait (&cb, 1000, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
858 if (!timedout)
859 break;
862 g_assert (mono_threads_inited);
863 return;
866 static void
867 mono_thread_info_set_inited (void)
869 mono_threads_inited = TRUE;
870 mono_memory_barrier ();
872 GSList *old = init_callbacks;
874 while (TRUE) {
875 GSList* old_read = (GSList*)mono_atomic_cas_ptr ((gpointer *) &init_callbacks, MONO_END_INIT_CB, (gpointer) old);
876 if (old == old_read)
877 break;
878 else
879 old = old_read;
881 if (old == MONO_END_INIT_CB) {
882 // Try not to use g_error / g_warning because this machinery used by logging
883 // Don't want to loop back into it.
884 fprintf (stderr, "Global threads inited twice");
885 exit (1);
886 return;
889 while (old != NULL) {
890 GSList *curr = (GSList *) old;
891 GSList *next = old->next;
893 mono_os_sem_post ((MonoSemType*)curr->data);
894 old = next;
897 return;
900 void
901 mono_thread_info_cleanup ()
903 mono_native_tls_free (thread_info_key);
904 mono_native_tls_free (thread_exited_key);
907 void
908 mono_thread_info_init (size_t info_size)
910 gboolean res;
911 thread_info_size = info_size;
912 char *sleepLimit;
914 mono_threads_suspend_policy_init ();
916 #ifdef HOST_WIN32
917 res = mono_native_tls_alloc (&thread_info_key, NULL);
918 res = mono_native_tls_alloc (&thread_exited_key, NULL);
919 #else
920 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
921 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
922 #endif
924 g_assert (res);
926 #ifndef MONO_KEYWORD_THREAD
927 res = mono_native_tls_alloc (&small_id_key, NULL);
928 #endif
929 g_assert (res);
931 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
932 mono_set_errno (0);
933 long threshold = strtol(sleepLimit, NULL, 10);
934 if ((errno == 0) && (threshold >= 40)) {
935 sleepAbortDuration = threshold;
936 sleepWarnDuration = threshold / 20;
937 } else
938 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
939 g_free (sleepLimit);
942 mono_os_sem_init (&global_suspend_semaphore, 1);
943 mono_os_sem_init (&suspend_semaphore, 0);
944 mono_os_mutex_init (&join_mutex);
946 mono_lls_init (&thread_list, NULL);
947 mono_thread_smr_init ();
948 mono_threads_suspend_init ();
949 mono_threads_coop_init ();
950 mono_threads_platform_init ();
952 mono_thread_info_set_inited ();
954 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
957 void
958 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
960 threads_callbacks = *callbacks;
963 void
964 mono_thread_info_signals_init (void)
966 mono_threads_suspend_init_signals ();
969 void
970 mono_thread_info_runtime_init (const MonoThreadInfoRuntimeCallbacks *callbacks)
972 mono_runtime_callbacks = callbacks;
975 static gboolean
976 mono_thread_info_core_resume (MonoThreadInfo *info)
978 gboolean res = FALSE;
980 switch (mono_threads_transition_request_resume (info)) {
981 case ResumeError:
982 res = FALSE;
983 break;
984 case ResumeOk:
985 res = TRUE;
986 break;
987 case ResumeInitSelfResume:
988 resume_self_suspended (info);
989 res = TRUE;
990 break;
991 case ResumeInitAsyncResume:
992 resume_async_suspended (info);
993 res = TRUE;
994 break;
995 case ResumeInitBlockingResume:
996 resume_blocking_suspended (info);
997 res = TRUE;
998 break;
1001 return res;
1005 * Current thread must hold the global_suspend_semaphore.
1006 * The given MonoThreadInfo* is a suspended thread.
1007 * Must be using hybrid suspend.
1009 static gboolean
1010 mono_thread_info_core_pulse (MonoThreadInfo *info)
1012 gboolean res = FALSE;
1014 switch (mono_threads_transition_request_pulse (info)) {
1015 case PulseInitAsyncPulse:
1016 resume_async_suspended (info);
1017 res = TRUE;
1018 break;
1020 return res;
1023 gboolean
1024 mono_thread_info_resume (MonoNativeThreadId tid)
1026 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
1027 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1028 MonoThreadInfo *info;
1030 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
1032 mono_thread_info_suspend_lock ();
1034 info = mono_thread_info_lookup (tid); /*info on HP1*/
1035 if (!info) {
1036 result = FALSE;
1037 goto cleanup;
1040 result = mono_thread_info_core_resume (info);
1042 //Wait for the pending resume to finish
1043 mono_threads_wait_pending_operations ();
1045 cleanup:
1046 mono_thread_info_suspend_unlock ();
1047 mono_hazard_pointer_clear (hp, 1);
1048 return result;
1051 static MonoThreadBeginSuspendResult
1052 begin_suspend_request_suspension_cordially (MonoThreadInfo *info);
1054 static MonoThreadBeginSuspendResult
1055 begin_suspend_peek_and_preempt (MonoThreadInfo *info);
1058 MonoThreadBeginSuspendResult
1059 mono_thread_info_begin_suspend (MonoThreadInfo *info, MonoThreadSuspendPhase phase)
1061 if (phase == MONO_THREAD_SUSPEND_PHASE_MOPUP && mono_threads_is_hybrid_suspension_enabled ())
1062 return begin_suspend_peek_and_preempt (info);
1063 else
1064 return begin_suspend_request_suspension_cordially (info);
1067 MonoThreadBeginSuspendResult
1068 begin_suspend_request_suspension_cordially (MonoThreadInfo *info)
1070 gboolean coop_aware_thread = FALSE;
1072 /* Ask the thread nicely to suspend. In hybrid suspend, blocking
1073 * threads are transitioned to blocking_suspend_requested, but not
1074 * preemptively suspend in the current phase.
1076 switch (mono_threads_transition_request_suspension (info)) {
1077 case ReqSuspendAlreadySuspended:
1078 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1079 case ReqSuspendAlreadySuspendedBlocking:
1080 if (mono_threads_is_hybrid_suspension_enabled ()) {
1081 /* This should only happen in the second phase of
1082 * hybrid suspend. It means that the first phase asked
1083 * the thread to suspend but did not signal it to
1084 * suspend preemptively. */
1085 g_assert_not_reached ();
1086 } else {
1087 // This state should not be possible if we're using preemptive
1088 // suspend on a blocking thread - there can only be a single
1089 // suspend initiator at a time (guarded by
1090 // mono_thread_info_suspend_lock), and we expect the victim
1091 // thread to finish the two-phase preemptive suspension
1092 // procedure (and reach the ReqSuspendAlreadySuspended stage)
1093 // before the next suspend initiator can begin.
1094 g_assert (mono_threads_is_blocking_transition_enabled ());
1096 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1098 case ReqSuspendInitSuspendBlocking:
1099 // in full cooperative mode just leave BLOCKING
1100 // threads running until they try to return to RUNNING, so
1101 // nothing to do, in hybrid coop preempt the thread. If thread is coop aware,
1102 // handle its as normal cooperative mode.
1103 if (mono_threads_is_blocking_transition_enabled ())
1104 coop_aware_thread = thread_is_cooperative_suspend_aware (info);
1106 switch (begin_suspend_for_blocking_thread (info, FALSE, MONO_THREAD_SUSPEND_PHASE_INITIAL, coop_aware_thread, NULL)) {
1107 case BeginSuspendFail:
1108 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1109 case BeginSuspendOkNoWait:
1110 if (mono_threads_is_hybrid_suspension_enabled () && !coop_aware_thread)
1111 return MONO_THREAD_BEGIN_SUSPEND_NEXT_PHASE;
1112 else {
1113 g_assert (thread_is_cooperative_suspend_aware (info));
1114 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1116 case BeginSuspendOkPreemptive:
1117 case BeginSuspendOkCooperative:
1118 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1119 default:
1120 g_assert_not_reached ();
1122 case ReqSuspendInitSuspendRunning:
1123 // in full preemptive mode this should be a preemptive suspend
1124 // in full and hybrid cooperative modes this should be a coop suspend
1125 if (begin_suspend_for_running_thread (info, FALSE) != BeginSuspendFail)
1126 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1127 else
1128 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1129 default:
1130 g_assert_not_reached ();
1134 MonoThreadBeginSuspendResult
1135 begin_suspend_peek_and_preempt (MonoThreadInfo *info)
1137 /* This only makes sense for two-phase hybrid suspension:
1138 * requires that a suspension request transition was already performed for 'info',
1139 * and if it is still in blocking_suspend_requested, preemptively suspends it.
1141 g_assert (mono_threads_is_hybrid_suspension_enabled ());
1142 if (mono_threads_transition_peek_blocking_suspend_requested (info)) {
1143 // in full cooperative mode just leave BLOCKING
1144 // threads running until they try to return to RUNNING, so
1145 // nothing to do, in hybrid coop preempt the thread.
1146 switch (begin_suspend_for_blocking_thread (info, FALSE, MONO_THREAD_SUSPEND_PHASE_MOPUP, FALSE, NULL)) {
1147 case BeginSuspendFail:
1148 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1149 case BeginSuspendOkNoWait:
1150 case BeginSuspendOkCooperative:
1151 /* can't happen - should've suspended in the previous phase */
1152 g_assert_not_reached ();
1153 case BeginSuspendOkPreemptive:
1154 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1155 default:
1156 g_assert_not_reached ();
1158 } else
1159 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1162 gboolean
1163 mono_thread_info_begin_resume (MonoThreadInfo *info)
1165 return mono_thread_info_core_resume (info);
1168 gboolean
1169 mono_thread_info_begin_pulse_resume_and_request_suspension (MonoThreadInfo *info)
1171 /* For two-phase suspend, we want to atomically resume the thread and
1172 * request that it try to cooperatively suspend again. Specifically,
1173 * we really don't want it to transition from GC Safe to GC Unsafe
1174 * because we then it could (in GC Unsafe) try to take a lock that's
1175 * held by another preemptively-suspended thread, essentially
1176 * recreating the same problem that two-phase suspend intends to
1177 * fix. */
1178 if (mono_threads_is_multiphase_stw_enabled ())
1179 return mono_thread_info_core_pulse (info);
1180 else
1181 return mono_thread_info_core_resume (info);
1184 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
1185 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
1187 static gboolean
1188 is_thread_in_critical_region (MonoThreadInfo *info)
1190 gpointer stack_start;
1191 MonoThreadUnwindState *state;
1193 if (mono_threads_platform_in_critical_region (info))
1194 return TRUE;
1196 /* Are we inside a system critical region? */
1197 if (info->inside_critical_region)
1198 return TRUE;
1200 /* Are we inside a GC critical region? */
1201 if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
1202 return TRUE;
1205 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
1206 state = mono_thread_info_get_suspend_state (info);
1207 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
1208 return FALSE;
1210 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
1211 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
1212 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
1213 return TRUE;
1215 if (threads_callbacks.ip_in_critical_region)
1216 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
1218 return FALSE;
1221 gboolean
1222 mono_thread_info_in_critical_location (MonoThreadInfo *info)
1224 return is_thread_in_critical_region (info);
1228 The return value is only valid until a matching mono_thread_info_resume is called
1230 static MonoThreadInfo*
1231 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
1233 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1234 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
1235 if (!info)
1236 return NULL;
1238 BeginSuspendResult suspend_result = BeginSuspendFail;
1239 switch (mono_threads_transition_request_suspension (info)) {
1240 case ReqSuspendAlreadySuspended:
1241 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
1242 return info;
1243 case ReqSuspendInitSuspendRunning:
1244 suspend_result = begin_suspend_for_running_thread (info, interrupt_kernel);
1245 if (suspend_result == BeginSuspendFail) {
1246 mono_hazard_pointer_clear (hp, 1);
1247 return NULL;
1249 //Wait for the pending suspend to finish
1250 g_assert (suspend_result != BeginSuspendOkNoWait);
1251 mono_threads_wait_pending_operations ();
1253 if (!check_async_suspend (info, suspend_result)) {
1254 mono_thread_info_core_resume (info);
1255 mono_threads_wait_pending_operations ();
1256 mono_hazard_pointer_clear (hp, 1);
1257 return NULL;
1259 return info;
1260 case ReqSuspendAlreadySuspendedBlocking:
1261 // ReqSuspendAlreadySuspendedBlocking should not be possible if
1262 // we're using preemptive suspend on a blocking thread - a
1263 // suspend initiator holds the mono_thread_info_suspend_lock
1264 // (there is a single suspend initiator at a time), and we
1265 // expect the victim thread to finish the two-phase preemptive
1266 // suspension procedure (and reach the
1267 // ReqSuspendAlreadySuspended stage) before the next suspend
1268 // initiator can begin.
1269 g_assert (mono_threads_is_blocking_transition_enabled () && !mono_threads_is_hybrid_suspension_enabled ());
1271 // if we tried to preempt the thread already, do nothing.
1272 // otherwise (if it's running in blocking mode) try to abort the syscall.
1273 if (interrupt_kernel)
1274 mono_threads_suspend_abort_syscall (info);
1276 return info;
1277 case ReqSuspendInitSuspendBlocking: {
1278 gboolean did_interrupt = FALSE;
1279 suspend_result = begin_suspend_for_blocking_thread (info, interrupt_kernel, MONO_THREAD_SUSPEND_PHASE_MOPUP, FALSE, &did_interrupt);
1280 if (suspend_result == BeginSuspendFail) {
1281 mono_hazard_pointer_clear (hp, 1);
1282 return NULL;
1285 if (suspend_result != BeginSuspendOkNoWait)
1286 mono_threads_wait_pending_operations ();
1288 if (!check_async_suspend (info, suspend_result)) {
1289 mono_thread_info_core_resume (info);
1290 mono_threads_wait_pending_operations ();
1291 mono_hazard_pointer_clear (hp, 1);
1292 return NULL;
1295 // if we tried to preempt the thread already, do nothing.
1296 // otherwise (if it's running in blocking mode) try to abort the syscall.
1297 if (interrupt_kernel && !did_interrupt)
1298 mono_threads_suspend_abort_syscall (info);
1300 return info;
1302 default:
1303 g_assert_not_reached ();
1305 g_assert_not_reached ();
1308 static MonoThreadInfo*
1309 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
1311 MonoThreadInfo *info = NULL;
1312 int sleep_duration = 0;
1313 for (;;) {
1314 if (!(info = suspend_sync (id, interrupt_kernel))) {
1315 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1316 return NULL;
1319 /*WARNING: We now are in interrupt context until we resume the thread. */
1320 if (!is_thread_in_critical_region (info))
1321 break;
1323 if (!mono_thread_info_core_resume (info)) {
1324 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1325 return NULL;
1327 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
1329 /* Wait for the pending resume to finish */
1330 mono_threads_wait_pending_operations ();
1332 if (sleep_duration == 0)
1333 mono_thread_info_yield ();
1334 else
1335 g_usleep (sleep_duration);
1337 sleep_duration += 10;
1339 return info;
1342 void
1343 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
1345 int result;
1346 MonoThreadInfo *info = NULL;
1347 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1349 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s)\n", (void*)id, interrupt_kernel ? "int" : "");
1350 /*FIXME: unify this with self-suspend*/
1351 g_assert (id != mono_native_thread_id_get ());
1353 /* This can block during stw */
1354 mono_thread_info_suspend_lock ();
1355 mono_threads_begin_global_suspend ();
1357 info = suspend_sync_nolock (id, interrupt_kernel);
1358 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s): info %p\n", (void*)id, interrupt_kernel ? "int" : "", info);
1359 if (!info)
1360 goto done;
1362 switch (result = callback (info, user_data)) {
1363 case MonoResumeThread:
1364 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): MonoResumeThread\n", (void*)id, interrupt_kernel ? "int" : "");
1365 mono_hazard_pointer_set (hp, 1, info);
1366 mono_thread_info_core_resume (info);
1367 mono_threads_wait_pending_operations ();
1368 #ifdef USE_WINDOWS_BACKEND
1369 // If we interrupt kernel but have blocking sync IO requests preventing the thread from running APC's
1370 // try to abort all sync blocking IO request. This must be done after thread has been resumed, but before releasing
1371 // global suspend lock (preventing other threads from supsending the thread).
1372 if (interrupt_kernel)
1373 mono_win32_abort_blocking_io_call (info);
1374 #endif
1375 break;
1376 case KeepSuspended:
1377 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): KeepSuspended\n", (void*)id, interrupt_kernel ? "int" : "");
1378 g_assert (!mono_threads_are_safepoints_enabled ());
1379 break;
1380 default:
1381 g_error ("Invalid suspend_and_run callback return value %d", result);
1384 done:
1385 mono_hazard_pointer_clear (hp, 1);
1386 mono_threads_end_global_suspend ();
1387 mono_thread_info_suspend_unlock ();
1391 Inject an assynchronous call into the target thread. The target thread must be suspended and
1392 only a single async call can be setup for a given suspend cycle.
1393 This async call must cause stack unwinding as the current implementation doesn't save enough state
1394 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1395 currently used only to deliver exceptions.
1397 void
1398 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1400 if (!mono_threads_are_safepoints_enabled ()) {
1401 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1402 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1403 * region or entering a gc unsafe region */
1404 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1406 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1407 g_assert (!info->async_target);
1408 info->async_target = target_func;
1409 /* This is not GC tracked */
1410 info->user_data = user_data;
1414 The suspend lock is held during any suspend in progress.
1415 A GC that has safepoints must take this lock as part of its
1416 STW to make sure no unsafe pending suspend is in progress.
1419 static void
1420 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1422 g_assertf (info, ""); // f includes __func__
1423 g_assert (mono_thread_info_is_current (info));
1424 g_assert (mono_thread_info_is_live (info));
1426 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1428 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1429 g_assert (res != -1);
1431 MONO_EXIT_GC_SAFE_WITH_INFO;
1434 void
1435 mono_thread_info_suspend_lock (void)
1437 MonoThreadInfo *info;
1438 gint res;
1440 info = mono_thread_info_current_unchecked ();
1441 if (info && mono_thread_info_is_live (info)) {
1442 mono_thread_info_suspend_lock_with_info (info);
1443 return;
1446 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1447 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1449 res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1450 g_assert (res != -1);
1453 void
1454 mono_thread_info_suspend_unlock (void)
1456 mono_os_sem_post (&global_suspend_semaphore);
1459 /* Return the suspend state for the current thread. Note: the thread must be
1460 * already suspended in order for this function to be callable.
1462 MonoThreadUnwindState*
1463 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
1465 int cur_state = mono_thread_info_current_state (info);
1466 switch (cur_state) {
1467 case STATE_ASYNC_SUSPENDED:
1468 case STATE_BLOCKING_ASYNC_SUSPENDED:
1469 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
1470 case STATE_SELF_SUSPENDED:
1471 case STATE_BLOCKING_SELF_SUSPENDED:
1472 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
1473 case STATE_BLOCKING_SUSPEND_REQUESTED:
1474 // This state is only valid for full cooperative suspend or cooparative suspend
1475 // aware threads. If we're preemptively suspending blocking threads,
1476 // this is not a valid suspend state.
1477 if ((mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ()) || thread_is_cooperative_suspend_aware (info))
1478 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
1479 break;
1480 default:
1481 break;
1484 STATE_RUNNING
1485 STATE_ASYNC_SUSPEND_REQUESTED
1486 STATE_BLOCKING: All those are invalid suspend states.
1487 STATE_BLOCKING_SUSPEND_REQUESTED: Invalid if we're preemptively suspending blocking threads.
1489 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), mono_thread_state_name (cur_state));
1493 * This is a very specific function whose only purpose is to
1494 * break a given thread from socket syscalls.
1496 * This only exists because linux won't fail a call to connect
1497 * if the underlying is closed.
1499 * TODO We should cleanup and unify this with the other syscall abort
1500 * facility.
1502 void
1503 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1505 MonoThreadHazardPointers *hp;
1506 MonoThreadInfo *info;
1508 if (tid == mono_native_thread_id_get ())
1509 return;
1511 mono_thread_info_suspend_lock ();
1512 hp = mono_hazard_pointer_get ();
1513 info = mono_thread_info_lookup (tid);
1514 if (!info) {
1515 mono_thread_info_suspend_unlock ();
1516 return;
1518 mono_threads_begin_global_suspend ();
1520 mono_threads_suspend_abort_syscall (info);
1521 mono_threads_wait_pending_operations ();
1523 mono_hazard_pointer_clear (hp, 1);
1525 mono_threads_end_global_suspend ();
1526 mono_thread_info_suspend_unlock ();
1530 * mono_thread_info_set_is_async_context:
1532 * Set whenever the current thread is in an async context. Some runtime functions might behave
1533 * differently while in an async context in order to be async safe.
1535 void
1536 mono_thread_info_set_is_async_context (gboolean async_context)
1538 MonoThreadInfo *info = mono_thread_info_current ();
1540 if (info) {
1541 // If this assert fails, that means there is recursion and/or
1542 // concurrency, such that setting async_context to FALSE
1543 // that all the callers do after this, is incorrect,
1544 // and this should instead be incremented/decremented.
1546 // As the value is only accessed via current(), that
1547 // limits the case to recursion, but increment/decrement
1548 // is still fast and correct and simple.
1549 g_assert (!async_context || !info->is_async_context);
1550 info->is_async_context = async_context;
1554 gboolean
1555 mono_thread_info_is_async_context (void)
1557 MonoThreadInfo *info = mono_thread_info_current ();
1559 if (info)
1560 return info->is_async_context;
1561 else
1562 return FALSE;
1566 * mono_thread_info_get_stack_bounds:
1568 * Return the address and size of the current threads stack. Return NULL as the
1569 * stack address if the stack address cannot be determined.
1571 void
1572 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1574 guint8 *current = (guint8 *)&stsize;
1575 mono_threads_platform_get_stack_bounds (staddr, stsize);
1576 if (!*staddr)
1577 return;
1579 /* Sanity check the result */
1580 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1582 #ifndef TARGET_WASM
1583 /* When running under emacs, sometimes staddr is not aligned to a page size */
1584 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1585 #endif
1588 gboolean
1589 mono_thread_info_yield (void)
1591 return mono_threads_platform_yield ();
1594 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1595 static MonoCoopMutex sleep_mutex;
1596 static MonoCoopCond sleep_cond;
1598 static void
1599 sleep_initialize (void)
1601 mono_coop_mutex_init (&sleep_mutex);
1602 mono_coop_cond_init (&sleep_cond);
1605 static void
1606 sleep_interrupt (gpointer data)
1608 mono_coop_mutex_lock (&sleep_mutex);
1609 mono_coop_cond_broadcast (&sleep_cond);
1610 mono_coop_mutex_unlock (&sleep_mutex);
1613 static guint32
1614 sleep_interruptable (guint32 ms, gboolean *alerted)
1616 gint64 now, end;
1618 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1620 g_assert (alerted);
1621 *alerted = FALSE;
1623 if (ms != MONO_INFINITE_WAIT)
1624 end = mono_msec_ticks() + ms;
1626 mono_lazy_initialize (&sleep_init, sleep_initialize);
1628 mono_coop_mutex_lock (&sleep_mutex);
1630 for (;;) {
1631 if (ms != MONO_INFINITE_WAIT) {
1632 now = mono_msec_ticks();
1633 if (now >= end)
1634 break;
1637 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1638 if (*alerted) {
1639 mono_coop_mutex_unlock (&sleep_mutex);
1640 return WAIT_IO_COMPLETION;
1643 if (ms != MONO_INFINITE_WAIT)
1644 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1645 else
1646 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1648 mono_thread_info_uninstall_interrupt (alerted);
1649 if (*alerted) {
1650 mono_coop_mutex_unlock (&sleep_mutex);
1651 return WAIT_IO_COMPLETION;
1655 mono_coop_mutex_unlock (&sleep_mutex);
1657 return 0;
1660 gint
1661 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1663 if (ms == 0) {
1664 MonoThreadInfo *info;
1666 mono_thread_info_yield ();
1668 info = mono_thread_info_current ();
1669 if (info && mono_thread_info_is_interrupt_state (info))
1670 return WAIT_IO_COMPLETION;
1672 return 0;
1675 if (alerted)
1676 return sleep_interruptable (ms, alerted);
1678 MONO_ENTER_GC_SAFE;
1680 if (ms == MONO_INFINITE_WAIT) {
1681 do {
1682 #ifdef HOST_WIN32
1683 Sleep (G_MAXUINT32);
1684 #else
1685 sleep (G_MAXUINT32);
1686 #endif
1687 } while (1);
1688 } else {
1689 #if defined (HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__)
1690 int ret;
1691 struct timespec start, target;
1693 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1694 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1695 g_assert (ret == 0);
1697 target = start;
1698 target.tv_sec += ms / 1000;
1699 target.tv_nsec += (ms % 1000) * 1000000;
1700 if (target.tv_nsec > 999999999) {
1701 target.tv_nsec -= 999999999;
1702 target.tv_sec ++;
1705 do {
1706 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1707 } while (ret != 0);
1708 #elif HOST_WIN32
1709 Sleep (ms);
1710 #else
1711 int ret;
1712 struct timespec req, rem;
1714 req.tv_sec = ms / 1000;
1715 req.tv_nsec = (ms % 1000) * 1000000;
1717 do {
1718 memset (&rem, 0, sizeof (rem));
1719 ret = nanosleep (&req, &rem);
1720 } while (ret != 0);
1721 #endif /* __linux__ */
1724 MONO_EXIT_GC_SAFE;
1726 return 0;
1729 gint
1730 mono_thread_info_usleep (guint64 us)
1732 MONO_ENTER_GC_SAFE;
1733 g_usleep (us);
1734 MONO_EXIT_GC_SAFE;
1735 return 0;
1738 gpointer
1739 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1741 return ((MonoThreadInfo*)info)->tls [key];
1745 * mono_threads_info_tls_set:
1747 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1748 * values of TLS variables for threads other than the current thread.
1749 * This should only be used for infrequently changing TLS variables, and it should
1750 * be paired with setting the real TLS variable since this provides no GC tracking.
1752 void
1753 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1755 ((MonoThreadInfo*)info)->tls [key] = value;
1759 * mono_thread_info_exit:
1761 * Exit the current thread.
1762 * This function doesn't return.
1764 void
1765 mono_thread_info_exit (gsize exit_code)
1767 mono_thread_info_detach ();
1769 mono_threads_platform_exit (0);
1773 * mono_threads_open_thread_handle:
1775 * Duplicate the handle. The handle needs to be closed by calling
1776 * mono_threads_close_thread_handle () when it is no longer needed.
1778 MonoThreadHandle*
1779 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1781 return mono_refcount_inc (thread_handle);
1784 void
1785 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1787 if (!thread_handle)
1788 return;
1789 mono_refcount_dec (thread_handle);
1793 * mono_threads_open_native_thread_handle:
1795 * Duplicate the handle. The handle needs to be closed by calling
1796 * mono_threads_close_native_thread_handle () when it is no longer needed.
1798 MonoNativeThreadHandle
1799 mono_threads_open_native_thread_handle (MonoNativeThreadHandle thread_handle)
1801 #ifdef HOST_WIN32
1802 BOOL success = FALSE;
1803 HANDLE new_thread_handle = NULL;
1805 g_assert (thread_handle && thread_handle != INVALID_HANDLE_VALUE);
1806 return DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &new_thread_handle, 0, FALSE, DUPLICATE_SAME_ACCESS) ? new_thread_handle : NULL;
1807 #else
1808 return MONO_GPOINTER_TO_NATIVE_THREAD_HANDLE (NULL);
1809 #endif
1812 void
1813 mono_threads_close_native_thread_handle (MonoNativeThreadHandle thread_handle)
1815 #ifdef HOST_WIN32
1816 g_assert (thread_handle != INVALID_HANDLE_VALUE);
1817 if (thread_handle)
1818 CloseHandle (thread_handle);
1819 #endif
1822 static void
1823 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1825 mono_os_event_set (&thread_handle->event);
1828 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1830 struct _MonoThreadInfoInterruptToken {
1831 void (*callback) (gpointer data);
1832 gpointer data;
1836 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1838 * - @callback: must be able to be called from another thread and always cancel the wait
1839 * - @data: passed to the callback
1840 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1841 * if set to TRUE, it must mean that the thread is in interrupted state
1843 void
1844 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1846 MonoThreadInfo *info;
1847 MonoThreadInfoInterruptToken *previous_token, *token;
1849 g_assert (callback);
1851 g_assert (interrupted);
1852 *interrupted = FALSE;
1854 info = mono_thread_info_current ();
1855 g_assertf (info, ""); // f includes __func__
1857 /* The memory of this token can be freed at 2 places:
1858 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1859 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1860 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1861 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1862 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1863 token->callback = callback;
1864 token->data = data;
1866 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, token, NULL);
1868 if (previous_token) {
1869 if (previous_token != INTERRUPT_STATE)
1870 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1872 g_free (token);
1874 *interrupted = TRUE;
1877 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1878 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1881 void
1882 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1884 MonoThreadInfo *info;
1885 MonoThreadInfoInterruptToken *previous_token;
1887 /* Common to uninstall interrupt handler around OS API's affecting last error. */
1888 /* This method could call OS API's on some platforms that will reset last error so make sure to restore */
1889 /* last error before exit. */
1890 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
1892 g_assert (interrupted);
1893 *interrupted = FALSE;
1895 info = mono_thread_info_current ();
1896 g_assertf (info, ""); // f includes __func__
1898 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_xchg_ptr ((gpointer*) &info->interrupt_token, NULL);
1900 /* only the installer can uninstall the token */
1901 g_assert (previous_token);
1903 if (previous_token == INTERRUPT_STATE) {
1904 /* if it is interrupted, then it is going to be freed in finish interrupt */
1905 *interrupted = TRUE;
1906 } else {
1907 g_free (previous_token);
1910 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1911 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1913 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
1916 static MonoThreadInfoInterruptToken*
1917 set_interrupt_state (MonoThreadInfo *info)
1919 MonoThreadInfoInterruptToken *token, *previous_token;
1921 g_assertf (info, ""); // f includes __func__
1923 /* Atomically obtain the token the thread is
1924 * waiting on, and change it to a flag value. */
1926 do {
1927 previous_token = info->interrupt_token;
1929 /* Already interrupted */
1930 if (previous_token == INTERRUPT_STATE) {
1931 token = NULL;
1932 break;
1935 token = previous_token;
1936 } while (mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1938 return token;
1942 * mono_thread_info_prepare_interrupt:
1944 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1945 * - if the thread calls one of the WaitFor functions, the function will return with
1946 * WAIT_IO_COMPLETION instead of waiting
1947 * - if the thread was waiting when this function was called, the wait will be broken
1949 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1950 * didn't receive the interrupt signal yet, in this case it should call the wait function
1951 * again. This essentially means that the target thread will busy wait until it is ready to
1952 * process the interruption.
1954 MonoThreadInfoInterruptToken*
1955 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1957 MonoThreadInfoInterruptToken *token;
1959 token = set_interrupt_state (info);
1961 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1962 mono_thread_info_get_tid (info), token);
1964 return token;
1967 void
1968 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1970 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1972 if (token == NULL)
1973 return;
1975 g_assert (token->callback);
1977 token->callback (token->data);
1979 g_free (token);
1982 void
1983 mono_thread_info_self_interrupt (void)
1985 MonoThreadInfo *info;
1986 MonoThreadInfoInterruptToken *token;
1988 info = mono_thread_info_current ();
1989 g_assertf (info, ""); // f includes __func__
1991 token = set_interrupt_state (info);
1992 g_assert (!token);
1994 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1995 mono_thread_info_get_tid (info));
1998 /* Clear the interrupted flag of the current thread, set with
1999 * mono_thread_info_self_interrupt, so it can wait again */
2000 void
2001 mono_thread_info_clear_self_interrupt (void)
2003 MonoThreadInfo *info;
2004 MonoThreadInfoInterruptToken *previous_token;
2006 info = mono_thread_info_current ();
2007 g_assertf (info, ""); // f includes __func__
2009 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
2010 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
2012 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
2015 gboolean
2016 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
2018 g_assertf (info, ""); // f includes __func__
2019 return mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
2022 void
2023 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
2025 g_assertf (info, ""); // f includes __func__
2027 if (!mono_atomic_load_ptr ((gpointer*) &info->interrupt_token))
2028 g_string_append_printf (text, "not waiting");
2029 else if (mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
2030 g_string_append_printf (text, "interrupted state");
2031 else
2032 g_string_append_printf (text, "waiting");
2035 gboolean
2036 mono_thread_info_is_current (MonoThreadInfo *info)
2038 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
2041 MonoThreadInfoWaitRet
2042 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
2044 MonoOSEventWaitRet res;
2046 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
2047 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
2048 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
2049 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
2050 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
2051 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
2052 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2053 else
2054 g_error ("%s: unknown res value %d", __func__, res);
2057 MonoThreadInfoWaitRet
2058 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
2060 MonoOSEventWaitRet res;
2061 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
2062 gint i;
2064 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
2065 if (background_change_event)
2066 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
2068 for (i = 0; i < nhandles; ++i)
2069 thread_events [i] = &thread_handles [i]->event;
2071 if (background_change_event)
2072 thread_events [nhandles ++] = background_change_event;
2074 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
2075 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
2076 return (MonoThreadInfoWaitRet)(MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0));
2077 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
2078 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
2079 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
2080 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2081 else
2082 g_error ("%s: unknown res value %d", __func__, res);
2086 * mono_threads_join_mutex:
2088 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
2089 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
2090 * The code inside the lock should not block.
2092 void
2093 mono_threads_join_lock (void)
2095 #ifdef TARGET_OSX
2096 mono_os_mutex_lock (&join_mutex);
2097 #endif
2100 void
2101 mono_threads_join_unlock (void)
2103 #ifdef TARGET_OSX
2104 mono_os_mutex_unlock (&join_mutex);
2105 #endif
2109 gboolean
2110 mono_thread_info_set_tools_data (void *data)
2112 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
2113 if (!info)
2114 return FALSE;
2115 if (info->tools_data)
2116 return FALSE;
2117 info->tools_data = data;
2118 return TRUE;
2121 void*
2122 mono_thread_info_get_tools_data (void)
2124 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
2126 return info ? info->tools_data : NULL;