update readme (#21797)
[mono-project.git] / mono / utils / mono-threads.c
blob57a7849e6acffd39ae71757d4f3ff0e6f2db7104
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>
36 #include <glib.h>
38 #include <errno.h>
39 #include <mono/utils/mono-errno.h>
41 #if defined(__MACH__)
42 #include <mono/utils/mach-support.h>
43 #endif
45 #if _MSC_VER
46 #pragma warning(disable:4312) // FIXME pointer cast to different size
47 #endif
50 Mutex that makes sure only a single thread can be suspending others.
51 Suspend is a very racy operation since it requires restarting until
52 the target thread is not on an unsafe region.
54 We could implement this using critical regions, but would be much much
55 harder for an operation that is hardly performance critical.
57 The GC has to acquire this lock before starting a STW to make sure
58 a runtime suspend won't make it wronly see a thread in a safepoint
59 when it is in fact not.
61 This has to be a naked locking primitive, and not a coop aware one, as
62 it needs to be usable when destroying thread_info_key, the TLS key for
63 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
64 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
65 to an assertion error. We then simply switch state manually in
66 mono_thread_info_suspend_lock_with_info.
68 static MonoSemType global_suspend_semaphore;
70 static size_t thread_info_size;
71 static MonoThreadInfoCallbacks threads_callbacks;
72 const MonoThreadInfoRuntimeCallbacks *mono_runtime_callbacks;
73 static MonoNativeTlsKey thread_info_key, thread_exited_key;
74 #ifdef MONO_KEYWORD_THREAD
75 static MONO_KEYWORD_THREAD gint32 tls_small_id = -1;
76 #else
77 static MonoNativeTlsKey small_id_key;
78 #endif
79 static MonoLinkedListSet thread_list;
80 static gboolean mono_threads_inited = FALSE;
82 static MonoSemType suspend_semaphore;
83 static size_t pending_suspends;
85 static mono_mutex_t join_mutex;
87 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state.state)
89 /*warn at 50 ms*/
90 #define SLEEP_DURATION_BEFORE_WARNING (50)
91 /*never aborts */
92 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
94 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
95 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
97 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
99 void
100 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
103 mono_atomic_inc_i32 (&abort_posts);
104 mono_os_sem_post (&suspend_semaphore);
107 void
108 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
110 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
111 // check that the thread is really in a valid suspended state.
112 g_assert (mono_thread_info_get_suspend_state (info) != NULL);
113 mono_atomic_inc_i32 (&suspend_posts);
114 mono_os_sem_post (&suspend_semaphore);
117 void
118 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
120 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
121 mono_atomic_inc_i32 (&resume_posts);
122 mono_os_sem_post (&suspend_semaphore);
125 typedef enum {
126 BeginSuspendFail = 0,
127 BeginSuspendOkPreemptive = 1,
128 BeginSuspendOkCooperative = 2,
129 BeginSuspendOkNoWait = 3,
130 } BeginSuspendResult;
132 static BeginSuspendResult
133 begin_cooperative_suspend (MonoThreadInfo *info)
135 /* There's nothing else to do after we async request the thread to suspend */
136 mono_threads_add_to_pending_operation_set (info);
137 return BeginSuspendOkCooperative;
140 static BeginSuspendResult
141 begin_preemptive_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
143 if (mono_threads_suspend_begin_async_suspend (info, interrupt_kernel))
144 return BeginSuspendOkPreemptive;
145 else
146 return BeginSuspendFail;
149 static BeginSuspendResult
150 begin_suspend_for_running_thread (MonoThreadInfo *info, gboolean interrupt_kernel)
152 /* If we're using full cooperative suspend or hybrid suspend,
153 * cooperatively suspend RUNNING threads */
154 if (mono_threads_are_safepoints_enabled ())
155 return begin_cooperative_suspend (info);
156 else
157 return begin_preemptive_suspend (info, interrupt_kernel);
160 static gboolean
161 thread_is_cooperative_suspend_aware (MonoThreadInfo *info)
163 return (mono_threads_is_cooperative_suspension_enabled () || mono_atomic_load_i32 (&(info->coop_aware_thread)));
166 static BeginSuspendResult
167 begin_suspend_for_blocking_thread (MonoThreadInfo *info, gboolean interrupt_kernel, MonoThreadSuspendPhase phase, gboolean coop_aware_thread, gboolean *did_interrupt)
169 // if a thread can't transition to blocking, we certainly shouldn't be
170 // trying to suspend it like it's blocking.
171 g_assert (mono_threads_is_blocking_transition_enabled ());
172 // with hybrid suspend, preemptively suspend blocking threads (if thread is not coop aware),
173 // otherwise blocking already counts as suspended.
174 if (mono_threads_is_hybrid_suspension_enabled () && !coop_aware_thread) {
175 if (did_interrupt) {
176 *did_interrupt = interrupt_kernel;
178 switch (phase) {
179 case MONO_THREAD_SUSPEND_PHASE_INITIAL:
180 /* In hybrid suspend, in the first phase, a thread in
181 * blocking can continue running (and possibly
182 * self-suspend). We'll preemptively suspend it in the
183 * second phase, if thread is not coop aware. */
184 return BeginSuspendOkNoWait;
185 case MONO_THREAD_SUSPEND_PHASE_MOPUP:
186 return begin_preemptive_suspend (info, interrupt_kernel);
187 default:
188 g_assert_not_reached ();
190 } else {
191 if (did_interrupt)
192 *did_interrupt = FALSE;
193 // In full cooperative suspend, treat a thread in BLOCKING as
194 // already suspended and don't wait for it.
195 return BeginSuspendOkNoWait;
199 static gboolean
200 check_async_suspend (MonoThreadInfo *info, BeginSuspendResult result)
202 switch (result) {
203 case BeginSuspendOkCooperative:
204 return TRUE;
205 case BeginSuspendOkPreemptive:
206 return mono_threads_suspend_check_suspend_result (info);
207 case BeginSuspendFail:
208 return FALSE;
209 case BeginSuspendOkNoWait:
210 return TRUE;
211 default:
212 g_assert_not_reached ();
216 static void
217 resume_async_suspended (MonoThreadInfo *info)
219 if (mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ())
220 g_assert_not_reached ();
222 g_assert (mono_threads_suspend_begin_async_resume (info));
225 static void
226 resume_self_suspended (MonoThreadInfo* info)
228 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
229 mono_os_sem_post (&info->resume_semaphore);
232 void
233 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
235 int res;
236 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
237 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
238 g_assert (res != -1);
241 static void
242 resume_blocking_suspended (MonoThreadInfo* info)
244 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
245 mono_os_sem_post (&info->resume_semaphore);
248 void
249 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
251 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
252 ++pending_suspends;
253 mono_atomic_inc_i32 (&pending_ops);
256 void
257 mono_threads_begin_global_suspend (void)
259 size_t ps = pending_suspends;
260 if (G_UNLIKELY (ps != 0))
261 g_error ("pending_suspends = %d, but must be 0", ps);
262 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,
263 abort_posts, waits_done, pending_ops);
264 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
265 mono_threads_coop_begin_global_suspend ();
268 void
269 mono_threads_end_global_suspend (void)
271 size_t ps = pending_suspends;
272 if (G_UNLIKELY (ps != 0))
273 g_error ("pending_suspends = %d, but must be 0", ps);
274 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
275 abort_posts, waits_done, pending_ops);
276 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
277 mono_threads_coop_end_global_suspend ();
280 static void
281 dump_threads (void)
283 MonoThreadInfo *cur = mono_thread_info_current ();
285 g_async_safe_printf ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
286 g_async_safe_printf ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
287 g_async_safe_printf ("\t0x1\t- detached (GOOD, unless the thread is running managed code)\n");
288 g_async_safe_printf ("\t0x2\t- running (BAD, unless it's the gc thread)\n");
289 g_async_safe_printf ("\t0x?03\t- async suspended (GOOD)\n");
290 g_async_safe_printf ("\t0x?04\t- self suspended (GOOD)\n");
291 g_async_safe_printf ("\t0x?05\t- async suspend requested (BAD)\n");
292 g_async_safe_printf ("\t0x6\t- blocking (BAD, unless there's no suspend initiator)\n");
293 g_async_safe_printf ("\t0x?07\t- blocking async suspended (GOOD)\n");
294 g_async_safe_printf ("\t0x?08\t- blocking self suspended (GOOD)\n");
295 g_async_safe_printf ("\t0x?09\t- blocking suspend requested (BAD in coop; GOOD in hybrid)\n");
297 FOREACH_THREAD_SAFE_ALL (info) {
298 #ifdef TARGET_MACH
299 char thread_name [256] = { 0 };
300 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
302 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" : "" );
303 #else
304 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" : "" );
305 #endif
306 } FOREACH_THREAD_SAFE_END
309 gboolean
310 mono_threads_wait_pending_operations (void)
312 int i;
313 int c = pending_suspends;
315 /* Wait threads to park */
316 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
317 if (pending_suspends) {
318 MonoStopwatch suspension_time;
319 mono_stopwatch_start (&suspension_time);
320 for (i = 0; i < pending_suspends; ++i) {
321 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
322 mono_atomic_inc_i32 (&waits_done);
323 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
324 continue;
325 mono_stopwatch_stop (&suspension_time);
327 dump_threads ();
329 g_async_safe_printf ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
330 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
332 mono_stopwatch_stop (&suspension_time);
333 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
337 pending_suspends = 0;
339 return c > 0;
343 //Thread initialization code
345 static void
346 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
348 if (retain != 0)
349 mono_hazard_pointer_clear (hp, 0);
350 if (retain != 1)
351 mono_hazard_pointer_clear (hp, 1);
352 if (retain != 2)
353 mono_hazard_pointer_clear (hp, 2);
357 If return non null Hazard Pointer 1 holds the return value.
359 MonoThreadInfo*
360 mono_thread_info_lookup (MonoNativeThreadId id)
362 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
364 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
365 mono_hazard_pointer_clear_all (hp, -1);
366 return NULL;
369 mono_hazard_pointer_clear_all (hp, 1);
370 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
373 static gboolean
374 mono_thread_info_insert (MonoThreadInfo *info)
376 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
378 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
379 mono_hazard_pointer_clear_all (hp, -1);
380 return FALSE;
383 mono_hazard_pointer_clear_all (hp, -1);
384 return TRUE;
387 static gboolean
388 mono_thread_info_remove (MonoThreadInfo *info)
390 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
391 gboolean res;
393 THREADS_DEBUG ("removing info %p\n", info);
394 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
395 mono_hazard_pointer_clear_all (hp, -1);
396 return res;
399 static void
400 free_thread_info (gpointer mem)
402 MonoThreadInfo *info = (MonoThreadInfo *) mem;
404 mono_os_sem_destroy (&info->resume_semaphore);
405 mono_threads_suspend_free (info);
407 g_free (info);
411 * mono_thread_info_register_small_id
413 * Registers a small ID for the current thread. This is a 16-bit value uniquely
414 * identifying the current thread. If the current thread already has a small ID
415 * assigned, that small ID will be returned; otherwise, the newly assigned small
416 * ID is returned.
419 mono_thread_info_register_small_id (void)
421 int small_id = mono_thread_info_get_small_id ();
423 if (small_id != -1)
424 return small_id;
426 small_id = mono_thread_small_id_alloc ();
427 #ifdef MONO_KEYWORD_THREAD
428 tls_small_id = small_id;
429 #else
430 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
431 #endif
432 return small_id;
435 static void
436 thread_handle_destroy (gpointer data)
438 MonoThreadHandle *thread_handle;
440 thread_handle = (MonoThreadHandle*) data;
442 mono_os_event_destroy (&thread_handle->event);
443 g_free (thread_handle);
446 static gboolean native_thread_id_main_thread_known;
447 static MonoNativeThreadId native_thread_id_main_thread;
450 * mono_native_thread_id_main_thread_known:
452 * If the main thread of the process has interacted with Mono (in the sense
453 * that it has a MonoThreadInfo associated with it), return \c TRUE and write
454 * its MonoNativeThreadId to \c main_thread_tid.
456 * Otherwise return \c FALSE.
458 gboolean
459 mono_native_thread_id_main_thread_known (MonoNativeThreadId *main_thread_tid)
461 if (!native_thread_id_main_thread_known)
462 return FALSE;
463 g_assert (main_thread_tid);
464 *main_thread_tid = native_thread_id_main_thread;
465 return TRUE;
469 * Saves the MonoNativeThreadId (on Linux pthread_t) of the current thread if
470 * it is the main thread.
472 * The main thread is (on Linux) the one whose OS thread id (on Linux pid_t) is
473 * equal to the process id.
475 * We have to do this at thread registration time because in embedding
476 * scenarios we can't count on the main thread to be the one that calls
477 * mono_jit_init, or other runtime initialization functions.
479 static void
480 native_thread_set_main_thread (void)
482 if (native_thread_id_main_thread_known)
483 return;
484 #if defined(__linux__)
485 if (mono_native_thread_os_id_get () == (guint64)getpid ()) {
486 native_thread_id_main_thread = mono_native_thread_id_get ();
487 mono_memory_barrier ();
488 native_thread_id_main_thread_known = TRUE;
490 #endif
493 static gboolean
494 register_thread (MonoThreadInfo *info)
496 size_t stsize = 0;
497 guint8 *staddr = NULL;
498 gboolean result;
500 info->small_id = mono_thread_info_register_small_id ();
501 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
502 native_thread_set_main_thread ();
504 info->handle = g_new0 (MonoThreadHandle, 1);
505 mono_refcount_init (info->handle, thread_handle_destroy);
506 mono_os_event_init (&info->handle->event, FALSE);
508 mono_os_sem_init (&info->resume_semaphore, 0);
510 /*set TLS early so SMR works */
511 mono_native_tls_set_value (thread_info_key, info);
513 mono_thread_info_get_stack_bounds (&staddr, &stsize);
514 g_assert (staddr);
515 g_assert (stsize);
516 info->stack_start_limit = staddr;
517 info->stack_end = staddr + stsize;
518 info->stackdata = g_byte_array_new ();
520 info->internal_thread_gchandle = NULL;
522 info->profiler_signal_ack = 1;
524 #ifdef USE_WINDOWS_BACKEND
525 info->windows_tib = (PNT_TIB)NtCurrentTeb ();
526 info->win32_apc_info = 0;
527 info->win32_apc_info_io_handle = INVALID_HANDLE_VALUE;
528 #endif
530 mono_threads_suspend_register (info);
532 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
534 if (threads_callbacks.thread_attach) {
535 if (!threads_callbacks.thread_attach (info)) {
536 // g_warning ("thread registation failed\n");
537 mono_native_tls_set_value (thread_info_key, NULL);
538 return FALSE;
543 Transition it before taking any locks or publishing itself to reduce the chance
544 of others witnessing a detached thread.
545 We can reasonably expect that until this thread gets published, no other thread will
546 try to manipulate it.
548 mono_threads_transition_attach (info);
549 mono_thread_info_suspend_lock ();
550 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
551 result = mono_thread_info_insert (info);
552 g_assert (result);
553 mono_thread_info_suspend_unlock ();
555 return TRUE;
558 static void
559 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
561 static void
562 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
564 static void
565 unregister_thread (void *arg)
567 MONO_STACKDATA (gc_unsafe_stackdata);
568 MonoThreadInfo *info;
569 int small_id;
570 gboolean result;
571 MonoThreadHandle* handle;
573 info = (MonoThreadInfo *) arg;
574 g_assertf (info, ""); // f includes __func__
575 g_assert (mono_thread_info_is_current (info));
576 g_assert (mono_thread_info_is_live (info));
578 /* We only enter the GC unsafe region, as when exiting this function, the thread
579 * will be detached, and the current MonoThreadInfo* will be destroyed. */
580 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
582 /* Need to be in GC Unsafe to pump the HP queue - some of the cleanup
583 * methods need to use coop-aware locks. For example: jit_info_table_free_duplicate.
586 /* Pump the HP queue while the thread is alive.*/
587 mono_thread_hazardous_try_free_some ();
589 small_id = info->small_id;
591 THREADS_DEBUG ("unregistering info %p\n", info);
593 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
596 * TLS destruction order is not reliable so small_id might be cleaned up
597 * before us.
599 #ifndef MONO_KEYWORD_THREAD
600 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
601 #endif
603 /* we need to duplicate it, as the info->handle is going
604 * to be closed when unregistering from the platform */
605 handle = mono_threads_open_thread_handle (info->handle);
608 First perform the callback that requires no locks.
609 This callback has the potential of taking other locks, so we do it before.
610 After it completes, the thread remains functional.
612 if (threads_callbacks.thread_detach)
613 threads_callbacks.thread_detach (info);
615 mono_thread_info_suspend_lock_with_info (info);
618 Now perform the callback that must be done under locks.
619 This will render the thread useless and non-suspendable, so it must
620 be done while holding the suspend lock to give no other thread chance
621 to suspend it.
623 if (threads_callbacks.thread_detach_with_lock)
624 threads_callbacks.thread_detach_with_lock (info);
626 /* The thread is no longer active, so unref its handle */
627 mono_threads_close_thread_handle (info->handle);
628 info->handle = NULL;
630 result = mono_thread_info_remove (info);
631 g_assert (result);
632 mono_threads_transition_detach (info);
634 mono_thread_info_suspend_unlock ();
636 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
638 /*now it's safe to free the thread info.*/
639 mono_thread_hazardous_try_free (info, free_thread_info);
641 mono_thread_small_id_free (small_id);
642 // 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
643 #ifdef MONO_KEYWORD_THREAD
644 tls_small_id = -1;
645 #else
646 mono_native_tls_set_value (small_id_key, NULL);
647 #endif
649 mono_threads_signal_thread_handle (handle);
651 mono_threads_close_thread_handle (handle);
653 mono_native_tls_set_value (thread_info_key, NULL);
656 static void
657 thread_exited_dtor (void *arg)
659 #if defined(__MACH__)
661 * Since we use pthread dtors to clean up thread data, if a thread
662 * is attached to the runtime by another pthread dtor after our dtor
663 * has ran, it will never be detached, leading to various problems
664 * since the thread ids etc. will be reused while they are still in
665 * the threads hashtables etc.
666 * Dtors are called in a loop until all user tls entries are 0,
667 * but the loop has a maximum count (4), so if we set the tls
668 * variable every time, it will remain set when system tls dtors
669 * are ran. This allows mono_thread_info_is_exiting () to detect
670 * whenever the thread is exiting, even if it is executed from a
671 * system tls dtor (i.e. obj-c dealloc methods).
673 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
674 #endif
677 MonoThreadInfo*
678 mono_thread_info_current_unchecked (void)
680 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
684 MonoThreadInfo*
685 mono_thread_info_current (void)
687 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
688 if (info)
689 return info;
691 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
694 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
695 The way to distinguish between before, during and after cleanup is the following:
697 -If the TLS key is set, cleanup has not begun;
698 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
699 -If the thread is nowhere to be found, cleanup has finished.
701 We cannot function after cleanup since there's no way to ensure what will happen.
703 g_assertf (info, ""); // f includes __func__
705 /*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 */
706 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
708 return info;
712 * mono_thread_info_get_small_id
714 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
715 * identifying the current thread. Returns -1 if the current thread doesn't have
716 * a small ID assigned.
718 * To ensure that the calling thread has a small ID assigned, call either
719 * mono_thread_info_attach or mono_thread_info_register_small_id.
722 mono_thread_info_get_small_id (void)
724 #ifdef MONO_KEYWORD_THREAD
725 return tls_small_id;
726 #else
727 gpointer val = mono_native_tls_get_value (small_id_key);
728 if (!val)
729 return -1;
730 return GPOINTER_TO_INT (val) - 1;
731 #endif
734 MonoLinkedListSet*
735 mono_thread_info_list_head (void)
737 return &thread_list;
740 MonoThreadInfo *
741 mono_thread_info_attach (void)
743 MonoThreadInfo *info;
745 #ifdef HOST_WIN32
746 if (!mono_threads_inited)
748 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
749 * thread is created before an embedding API user initialized Mono. */
750 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
751 return NULL;
753 #endif
755 g_assert (mono_threads_inited);
757 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
758 if (!info) {
759 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
760 THREADS_DEBUG ("attaching %p\n", info);
761 if (!register_thread (info)) {
762 g_free (info);
763 return NULL;
767 return info;
770 void
771 mono_thread_info_detach (void)
773 MonoThreadInfo *info;
775 #ifdef HOST_WIN32
776 if (!mono_threads_inited)
778 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
779 * is created before an embedding API user initialized Mono. */
780 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
781 return;
783 #endif
785 g_assert (mono_threads_inited);
787 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
788 if (info) {
789 THREADS_DEBUG ("detaching %p\n", info);
790 unregister_thread (info);
794 gboolean
795 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, MonoGCHandle *gchandle)
797 g_assertf (info, ""); // f includes __func__
798 g_assert (mono_thread_info_is_current (info));
800 if (info->internal_thread_gchandle == NULL)
801 return FALSE;
803 *gchandle = info->internal_thread_gchandle;
804 return TRUE;
807 void
808 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, MonoGCHandle gchandle)
810 g_assertf (info, ""); // f includes __func__
811 g_assert (mono_thread_info_is_current (info));
812 info->internal_thread_gchandle = gchandle;
815 void
816 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
818 g_assertf (info, ""); // f includes __func__
819 g_assert (mono_thread_info_is_current (info));
820 info->internal_thread_gchandle = NULL;
824 * mono_thread_info_is_exiting:
826 * Return whenever the current thread is exiting, i.e. it is running pthread
827 * dtors.
829 gboolean
830 mono_thread_info_is_exiting (void)
832 #if defined(__MACH__)
833 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
834 return TRUE;
835 #endif
836 return FALSE;
839 #ifndef HOST_WIN32
840 static void
841 thread_info_key_dtor (void *arg)
843 /* Put the MonoThreadInfo back for the duration of the
844 * unregister code. In some circumstances the thread needs to
845 * take the GC lock which may block which requires a coop
846 * state transition. */
847 mono_native_tls_set_value (thread_info_key, arg);
848 unregister_thread (arg);
849 mono_native_tls_set_value (thread_info_key, NULL);
851 #endif
853 MonoThreadInfoFlags
854 mono_thread_info_get_flags (MonoThreadInfo *info)
856 return (MonoThreadInfoFlags)mono_atomic_load_i32 (&info->flags);
859 void
860 mono_thread_info_set_flags (MonoThreadInfoFlags flags)
862 MonoThreadInfo *info = mono_thread_info_current ();
863 MonoThreadInfoFlags old = (MonoThreadInfoFlags)mono_atomic_load_i32 (&info->flags);
865 if (threads_callbacks.thread_flags_changing)
866 threads_callbacks.thread_flags_changing (old, flags);
868 mono_atomic_store_i32 (&info->flags, flags);
870 if (threads_callbacks.thread_flags_changed)
871 threads_callbacks.thread_flags_changed (old, flags);
874 #define MONO_END_INIT_CB GINT_TO_POINTER(-1)
875 static GSList *init_callbacks;
877 void
878 mono_thread_info_wait_inited (void)
880 MonoSemType cb;
881 mono_os_sem_init (&cb, 0);
882 GSList *old = init_callbacks;
884 GSList wait_request;
885 wait_request.data = &cb;
886 wait_request.next = old;
888 while (mono_threads_inited != TRUE) {
889 GSList *old_read = (GSList*)mono_atomic_cas_ptr ((gpointer *) &init_callbacks, &wait_request, old);
891 // Queued up waiter, need to be unstuck
892 if (old_read == old) {
893 break;
894 } else if (old_read == GINT_TO_POINTER (MONO_END_INIT_CB)) {
895 // Is inited
896 return;
897 } else {
898 // We raced with another writer
899 wait_request.next = (GSList *) old_read;
900 old = old_read;
904 while (mono_threads_inited != TRUE) {
905 gboolean timedout = mono_os_sem_timedwait (&cb, 1000, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
906 if (!timedout)
907 break;
910 g_assert (mono_threads_inited);
911 return;
914 static void
915 mono_thread_info_set_inited (void)
917 mono_threads_inited = TRUE;
918 mono_memory_barrier ();
920 GSList *old = init_callbacks;
922 while (TRUE) {
923 GSList* old_read = (GSList*)mono_atomic_cas_ptr ((gpointer *) &init_callbacks, MONO_END_INIT_CB, (gpointer) old);
924 if (old == old_read)
925 break;
926 else
927 old = old_read;
929 if (old == MONO_END_INIT_CB) {
930 // Try not to use g_error / g_warning because this machinery used by logging
931 // Don't want to loop back into it.
932 fprintf (stderr, "Global threads inited twice");
933 exit (1);
934 return;
937 while (old != NULL) {
938 GSList *curr = (GSList *) old;
939 GSList *next = old->next;
941 mono_os_sem_post ((MonoSemType*)curr->data);
942 old = next;
945 return;
948 void
949 mono_thread_info_cleanup ()
951 mono_native_tls_free (thread_info_key);
952 mono_native_tls_free (thread_exited_key);
955 void
956 mono_thread_info_init (size_t info_size)
958 gboolean res;
959 thread_info_size = info_size;
960 char *sleepLimit;
962 mono_threads_suspend_policy_init ();
964 #ifdef HOST_WIN32
965 res = mono_native_tls_alloc (&thread_info_key, NULL);
966 res = mono_native_tls_alloc (&thread_exited_key, NULL);
967 #else
968 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
969 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
970 #endif
972 g_assert (res);
974 #ifndef MONO_KEYWORD_THREAD
975 res = mono_native_tls_alloc (&small_id_key, NULL);
976 #endif
977 g_assert (res);
979 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
980 mono_set_errno (0);
981 long threshold = strtol(sleepLimit, NULL, 10);
982 if ((errno == 0) && (threshold >= 40)) {
983 sleepAbortDuration = threshold;
984 sleepWarnDuration = threshold / 20;
985 } else
986 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
987 g_free (sleepLimit);
990 mono_os_sem_init (&global_suspend_semaphore, 1);
991 mono_os_sem_init (&suspend_semaphore, 0);
992 mono_os_mutex_init (&join_mutex);
994 mono_lls_init (&thread_list, NULL);
995 mono_thread_smr_init ();
996 mono_threads_suspend_init ();
997 mono_threads_coop_init ();
998 mono_threads_platform_init ();
1000 mono_thread_info_set_inited ();
1002 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
1005 void
1006 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
1008 threads_callbacks = *callbacks;
1011 void
1012 mono_thread_info_signals_init (void)
1014 mono_threads_suspend_init_signals ();
1017 void
1018 mono_thread_info_runtime_init (const MonoThreadInfoRuntimeCallbacks *callbacks)
1020 mono_runtime_callbacks = callbacks;
1023 static gboolean
1024 mono_thread_info_core_resume (MonoThreadInfo *info)
1026 gboolean res = FALSE;
1028 switch (mono_threads_transition_request_resume (info)) {
1029 case ResumeError:
1030 res = FALSE;
1031 break;
1032 case ResumeOk:
1033 res = TRUE;
1034 break;
1035 case ResumeInitSelfResume:
1036 resume_self_suspended (info);
1037 res = TRUE;
1038 break;
1039 case ResumeInitAsyncResume:
1040 resume_async_suspended (info);
1041 res = TRUE;
1042 break;
1043 case ResumeInitBlockingResume:
1044 resume_blocking_suspended (info);
1045 res = TRUE;
1046 break;
1049 return res;
1053 * Current thread must hold the global_suspend_semaphore.
1054 * The given MonoThreadInfo* is a suspended thread.
1055 * Must be using hybrid suspend.
1057 static gboolean
1058 mono_thread_info_core_pulse (MonoThreadInfo *info)
1060 gboolean res = FALSE;
1062 switch (mono_threads_transition_request_pulse (info)) {
1063 case PulseInitAsyncPulse:
1064 resume_async_suspended (info);
1065 res = TRUE;
1066 break;
1068 return res;
1071 gboolean
1072 mono_thread_info_resume (MonoNativeThreadId tid)
1074 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
1075 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1076 MonoThreadInfo *info;
1078 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
1080 mono_thread_info_suspend_lock ();
1082 info = mono_thread_info_lookup (tid); /*info on HP1*/
1083 if (!info) {
1084 result = FALSE;
1085 goto cleanup;
1088 result = mono_thread_info_core_resume (info);
1090 //Wait for the pending resume to finish
1091 mono_threads_wait_pending_operations ();
1093 cleanup:
1094 mono_thread_info_suspend_unlock ();
1095 mono_hazard_pointer_clear (hp, 1);
1096 return result;
1099 static MonoThreadBeginSuspendResult
1100 begin_suspend_request_suspension_cordially (MonoThreadInfo *info);
1102 static MonoThreadBeginSuspendResult
1103 begin_suspend_peek_and_preempt (MonoThreadInfo *info);
1106 MonoThreadBeginSuspendResult
1107 mono_thread_info_begin_suspend (MonoThreadInfo *info, MonoThreadSuspendPhase phase)
1109 if (phase == MONO_THREAD_SUSPEND_PHASE_MOPUP && mono_threads_is_hybrid_suspension_enabled ())
1110 return begin_suspend_peek_and_preempt (info);
1111 else
1112 return begin_suspend_request_suspension_cordially (info);
1115 MonoThreadBeginSuspendResult
1116 begin_suspend_request_suspension_cordially (MonoThreadInfo *info)
1118 gboolean coop_aware_thread = FALSE;
1120 /* Ask the thread nicely to suspend. In hybrid suspend, blocking
1121 * threads are transitioned to blocking_suspend_requested, but not
1122 * preemptively suspend in the current phase.
1124 switch (mono_threads_transition_request_suspension (info)) {
1125 case ReqSuspendAlreadySuspended:
1126 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1127 case ReqSuspendAlreadySuspendedBlocking:
1128 if (mono_threads_is_hybrid_suspension_enabled ()) {
1129 /* This should only happen in the second phase of
1130 * hybrid suspend. It means that the first phase asked
1131 * the thread to suspend but did not signal it to
1132 * suspend preemptively. */
1133 g_assert_not_reached ();
1134 } else {
1135 // This state should not be possible if we're using preemptive
1136 // suspend on a blocking thread - there can only be a single
1137 // suspend initiator at a time (guarded by
1138 // mono_thread_info_suspend_lock), and we expect the victim
1139 // thread to finish the two-phase preemptive suspension
1140 // procedure (and reach the ReqSuspendAlreadySuspended stage)
1141 // before the next suspend initiator can begin.
1142 g_assert (mono_threads_is_blocking_transition_enabled ());
1144 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1146 case ReqSuspendInitSuspendBlocking:
1147 // in full cooperative mode just leave BLOCKING
1148 // threads running until they try to return to RUNNING, so
1149 // nothing to do, in hybrid coop preempt the thread. If thread is coop aware,
1150 // handle its as normal cooperative mode.
1151 if (mono_threads_is_blocking_transition_enabled ())
1152 coop_aware_thread = thread_is_cooperative_suspend_aware (info);
1154 switch (begin_suspend_for_blocking_thread (info, FALSE, MONO_THREAD_SUSPEND_PHASE_INITIAL, coop_aware_thread, NULL)) {
1155 case BeginSuspendFail:
1156 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1157 case BeginSuspendOkNoWait:
1158 if (mono_threads_is_hybrid_suspension_enabled () && !coop_aware_thread)
1159 return MONO_THREAD_BEGIN_SUSPEND_NEXT_PHASE;
1160 else {
1161 g_assert (thread_is_cooperative_suspend_aware (info));
1162 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1164 case BeginSuspendOkPreemptive:
1165 case BeginSuspendOkCooperative:
1166 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1167 default:
1168 g_assert_not_reached ();
1170 case ReqSuspendInitSuspendRunning:
1171 // in full preemptive mode this should be a preemptive suspend
1172 // in full and hybrid cooperative modes this should be a coop suspend
1173 if (begin_suspend_for_running_thread (info, FALSE) != BeginSuspendFail)
1174 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1175 else
1176 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1177 default:
1178 g_assert_not_reached ();
1182 MonoThreadBeginSuspendResult
1183 begin_suspend_peek_and_preempt (MonoThreadInfo *info)
1185 /* This only makes sense for two-phase hybrid suspension:
1186 * requires that a suspension request transition was already performed for 'info',
1187 * and if it is still in blocking_suspend_requested, preemptively suspends it.
1189 g_assert (mono_threads_is_hybrid_suspension_enabled ());
1190 if (mono_threads_transition_peek_blocking_suspend_requested (info)) {
1191 // in full cooperative mode just leave BLOCKING
1192 // threads running until they try to return to RUNNING, so
1193 // nothing to do, in hybrid coop preempt the thread.
1194 switch (begin_suspend_for_blocking_thread (info, FALSE, MONO_THREAD_SUSPEND_PHASE_MOPUP, FALSE, NULL)) {
1195 case BeginSuspendFail:
1196 return MONO_THREAD_BEGIN_SUSPEND_SKIP;
1197 case BeginSuspendOkNoWait:
1198 case BeginSuspendOkCooperative:
1199 /* can't happen - should've suspended in the previous phase */
1200 g_assert_not_reached ();
1201 case BeginSuspendOkPreemptive:
1202 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1203 default:
1204 g_assert_not_reached ();
1206 } else
1207 return MONO_THREAD_BEGIN_SUSPEND_SUSPENDED;
1210 gboolean
1211 mono_thread_info_begin_resume (MonoThreadInfo *info)
1213 return mono_thread_info_core_resume (info);
1216 gboolean
1217 mono_thread_info_begin_pulse_resume_and_request_suspension (MonoThreadInfo *info)
1219 /* For two-phase suspend, we want to atomically resume the thread and
1220 * request that it try to cooperatively suspend again. Specifically,
1221 * we really don't want it to transition from GC Safe to GC Unsafe
1222 * because we then it could (in GC Unsafe) try to take a lock that's
1223 * held by another preemptively-suspended thread, essentially
1224 * recreating the same problem that two-phase suspend intends to
1225 * fix. */
1226 if (mono_threads_is_multiphase_stw_enabled ())
1227 return mono_thread_info_core_pulse (info);
1228 else
1229 return mono_thread_info_core_resume (info);
1232 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
1233 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
1235 static gboolean
1236 is_thread_in_critical_region (MonoThreadInfo *info)
1238 gpointer stack_start;
1239 MonoThreadUnwindState *state;
1241 if (mono_threads_platform_in_critical_region (info))
1242 return TRUE;
1244 /* Are we inside a system critical region? */
1245 if (info->inside_critical_region)
1246 return TRUE;
1248 /* Are we inside a GC critical region? */
1249 if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
1250 return TRUE;
1253 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
1254 state = mono_thread_info_get_suspend_state (info);
1255 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
1256 return FALSE;
1258 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
1259 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
1260 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
1261 return TRUE;
1263 if (threads_callbacks.ip_in_critical_region)
1264 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
1266 return FALSE;
1269 gboolean
1270 mono_thread_info_in_critical_location (MonoThreadInfo *info)
1272 return is_thread_in_critical_region (info);
1276 The return value is only valid until a matching mono_thread_info_resume is called
1278 static MonoThreadInfo*
1279 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
1281 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1282 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
1283 if (!info)
1284 return NULL;
1286 BeginSuspendResult suspend_result = BeginSuspendFail;
1287 switch (mono_threads_transition_request_suspension (info)) {
1288 case ReqSuspendAlreadySuspended:
1289 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
1290 return info;
1291 case ReqSuspendInitSuspendRunning:
1292 suspend_result = begin_suspend_for_running_thread (info, interrupt_kernel);
1293 if (suspend_result == BeginSuspendFail) {
1294 mono_hazard_pointer_clear (hp, 1);
1295 return NULL;
1297 //Wait for the pending suspend to finish
1298 g_assert (suspend_result != BeginSuspendOkNoWait);
1299 mono_threads_wait_pending_operations ();
1301 if (!check_async_suspend (info, suspend_result)) {
1302 mono_thread_info_core_resume (info);
1303 mono_threads_wait_pending_operations ();
1304 mono_hazard_pointer_clear (hp, 1);
1305 return NULL;
1307 return info;
1308 case ReqSuspendAlreadySuspendedBlocking:
1309 // ReqSuspendAlreadySuspendedBlocking should not be possible if
1310 // we're using preemptive suspend on a blocking thread - a
1311 // suspend initiator holds the mono_thread_info_suspend_lock
1312 // (there is a single suspend initiator at a time), and we
1313 // expect the victim thread to finish the two-phase preemptive
1314 // suspension procedure (and reach the
1315 // ReqSuspendAlreadySuspended stage) before the next suspend
1316 // initiator can begin.
1317 g_assert (mono_threads_is_blocking_transition_enabled () && !mono_threads_is_hybrid_suspension_enabled ());
1319 // if we tried to preempt the thread already, do nothing.
1320 // otherwise (if it's running in blocking mode) try to abort the syscall.
1321 if (interrupt_kernel)
1322 mono_threads_suspend_abort_syscall (info);
1324 return info;
1325 case ReqSuspendInitSuspendBlocking: {
1326 gboolean did_interrupt = FALSE;
1327 suspend_result = begin_suspend_for_blocking_thread (info, interrupt_kernel, MONO_THREAD_SUSPEND_PHASE_MOPUP, FALSE, &did_interrupt);
1328 if (suspend_result == BeginSuspendFail) {
1329 mono_hazard_pointer_clear (hp, 1);
1330 return NULL;
1333 if (suspend_result != BeginSuspendOkNoWait)
1334 mono_threads_wait_pending_operations ();
1336 if (!check_async_suspend (info, suspend_result)) {
1337 mono_thread_info_core_resume (info);
1338 mono_threads_wait_pending_operations ();
1339 mono_hazard_pointer_clear (hp, 1);
1340 return NULL;
1343 // if we tried to preempt the thread already, do nothing.
1344 // otherwise (if it's running in blocking mode) try to abort the syscall.
1345 if (interrupt_kernel && !did_interrupt)
1346 mono_threads_suspend_abort_syscall (info);
1348 return info;
1350 default:
1351 g_assert_not_reached ();
1353 g_assert_not_reached ();
1356 static MonoThreadInfo*
1357 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
1359 MonoThreadInfo *info = NULL;
1360 int sleep_duration = 0;
1361 for (;;) {
1362 if (!(info = suspend_sync (id, interrupt_kernel))) {
1363 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1364 return NULL;
1367 /*WARNING: We now are in interrupt context until we resume the thread. */
1368 if (!is_thread_in_critical_region (info))
1369 break;
1371 if (!mono_thread_info_core_resume (info)) {
1372 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1373 return NULL;
1375 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
1377 /* Wait for the pending resume to finish */
1378 mono_threads_wait_pending_operations ();
1380 if (sleep_duration == 0)
1381 mono_thread_info_yield ();
1382 else
1383 g_usleep (sleep_duration);
1385 sleep_duration += 10;
1387 return info;
1390 void
1391 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
1393 int result;
1394 MonoThreadInfo *info = NULL;
1395 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1397 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s)\n", (void*)id, interrupt_kernel ? "int" : "");
1398 /*FIXME: unify this with self-suspend*/
1399 g_assert (id != mono_native_thread_id_get ());
1401 /* This can block during stw */
1402 mono_thread_info_suspend_lock ();
1403 mono_threads_begin_global_suspend ();
1405 info = suspend_sync_nolock (id, interrupt_kernel);
1406 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p (%s): info %p\n", (void*)id, interrupt_kernel ? "int" : "", info);
1407 if (!info)
1408 goto done;
1410 switch (result = callback (info, user_data)) {
1411 case MonoResumeThread:
1412 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): MonoResumeThread\n", (void*)id, interrupt_kernel ? "int" : "");
1413 mono_hazard_pointer_set (hp, 1, info);
1414 mono_thread_info_core_resume (info);
1415 mono_threads_wait_pending_operations ();
1416 #ifdef USE_WINDOWS_BACKEND
1417 // If we interrupt kernel but have blocking sync IO requests preventing the thread from running APC's
1418 // try to abort all sync blocking IO request. This must be done after thread has been resumed, but before releasing
1419 // global suspend lock (preventing other threads from supsending the thread).
1420 if (interrupt_kernel)
1421 mono_win32_abort_blocking_io_call (info);
1422 #endif
1423 break;
1424 case KeepSuspended:
1425 THREADS_SUSPEND_DEBUG ("CALLBACK tid %p (%s): KeepSuspended\n", (void*)id, interrupt_kernel ? "int" : "");
1426 g_assert (!mono_threads_are_safepoints_enabled ());
1427 break;
1428 default:
1429 g_error ("Invalid suspend_and_run callback return value %d", result);
1432 done:
1433 mono_hazard_pointer_clear (hp, 1);
1434 mono_threads_end_global_suspend ();
1435 mono_thread_info_suspend_unlock ();
1439 Inject an assynchronous call into the target thread. The target thread must be suspended and
1440 only a single async call can be setup for a given suspend cycle.
1441 This async call must cause stack unwinding as the current implementation doesn't save enough state
1442 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1443 currently used only to deliver exceptions.
1445 void
1446 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1448 if (!mono_threads_are_safepoints_enabled ()) {
1449 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1450 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1451 * region or entering a gc unsafe region */
1452 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1454 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1455 g_assert (!info->async_target);
1456 info->async_target = target_func;
1457 /* This is not GC tracked */
1458 info->user_data = user_data;
1462 The suspend lock is held during any suspend in progress.
1463 A GC that has safepoints must take this lock as part of its
1464 STW to make sure no unsafe pending suspend is in progress.
1467 static void
1468 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1470 g_assertf (info, ""); // f includes __func__
1471 g_assert (mono_thread_info_is_current (info));
1472 g_assert (mono_thread_info_is_live (info));
1474 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1476 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1477 g_assert (res != -1);
1479 MONO_EXIT_GC_SAFE_WITH_INFO;
1482 void
1483 mono_thread_info_suspend_lock (void)
1485 MonoThreadInfo *info;
1486 gint res;
1488 info = mono_thread_info_current_unchecked ();
1489 if (info && mono_thread_info_is_live (info)) {
1490 mono_thread_info_suspend_lock_with_info (info);
1491 return;
1494 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1495 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1497 res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1498 g_assert (res != -1);
1501 void
1502 mono_thread_info_suspend_unlock (void)
1504 mono_os_sem_post (&global_suspend_semaphore);
1507 /* Return the suspend state for the current thread. Note: the thread must be
1508 * already suspended in order for this function to be callable.
1510 MonoThreadUnwindState*
1511 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
1513 int cur_state = mono_thread_info_current_state (info);
1514 switch (cur_state) {
1515 case STATE_ASYNC_SUSPENDED:
1516 case STATE_BLOCKING_ASYNC_SUSPENDED:
1517 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
1518 case STATE_SELF_SUSPENDED:
1519 case STATE_BLOCKING_SELF_SUSPENDED:
1520 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
1521 case STATE_BLOCKING_SUSPEND_REQUESTED:
1522 // This state is only valid for full cooperative suspend or cooparative suspend
1523 // aware threads. If we're preemptively suspending blocking threads,
1524 // this is not a valid suspend state.
1525 if ((mono_threads_is_cooperative_suspension_enabled () && !mono_threads_is_hybrid_suspension_enabled ()) || thread_is_cooperative_suspend_aware (info))
1526 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
1527 break;
1528 default:
1529 break;
1532 STATE_RUNNING
1533 STATE_ASYNC_SUSPEND_REQUESTED
1534 STATE_BLOCKING: All those are invalid suspend states.
1535 STATE_BLOCKING_SUSPEND_REQUESTED: Invalid if we're preemptively suspending blocking threads.
1537 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));
1541 * This is a very specific function whose only purpose is to
1542 * break a given thread from socket syscalls.
1544 * This only exists because linux won't fail a call to connect
1545 * if the underlying is closed.
1547 * TODO We should cleanup and unify this with the other syscall abort
1548 * facility.
1550 void
1551 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1553 MonoThreadHazardPointers *hp;
1554 MonoThreadInfo *info;
1556 if (tid == mono_native_thread_id_get ())
1557 return;
1559 mono_thread_info_suspend_lock ();
1560 hp = mono_hazard_pointer_get ();
1561 info = mono_thread_info_lookup (tid);
1562 if (!info) {
1563 mono_thread_info_suspend_unlock ();
1564 return;
1566 mono_threads_begin_global_suspend ();
1568 mono_threads_suspend_abort_syscall (info);
1569 mono_threads_wait_pending_operations ();
1571 mono_hazard_pointer_clear (hp, 1);
1573 mono_threads_end_global_suspend ();
1574 mono_thread_info_suspend_unlock ();
1578 * mono_thread_info_set_is_async_context:
1580 * Set whenever the current thread is in an async context. Some runtime functions might behave
1581 * differently while in an async context in order to be async safe.
1583 void
1584 mono_thread_info_set_is_async_context (gboolean async_context)
1586 MonoThreadInfo *info = mono_thread_info_current ();
1588 if (info) {
1589 // If this assert fails, that means there is recursion and/or
1590 // concurrency, such that setting async_context to FALSE
1591 // that all the callers do after this, is incorrect,
1592 // and this should instead be incremented/decremented.
1594 // As the value is only accessed via current(), that
1595 // limits the case to recursion, but increment/decrement
1596 // is still fast and correct and simple.
1597 g_assert (!async_context || !info->is_async_context);
1598 info->is_async_context = async_context;
1602 gboolean
1603 mono_thread_info_is_async_context (void)
1605 MonoThreadInfo *info = mono_thread_info_current ();
1607 if (info)
1608 return info->is_async_context;
1609 else
1610 return FALSE;
1614 * mono_thread_info_get_stack_bounds:
1616 * Return the address and size of the current threads stack. Return NULL as the
1617 * stack address if the stack address cannot be determined.
1619 void
1620 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1622 guint8 *current = (guint8 *)&stsize;
1623 mono_threads_platform_get_stack_bounds (staddr, stsize);
1624 if (!*staddr)
1625 return;
1627 /* Sanity check the result */
1628 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1630 #ifndef TARGET_WASM
1631 /* When running under emacs, sometimes staddr is not aligned to a page size */
1632 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1633 #endif
1636 gboolean
1637 mono_thread_info_yield (void)
1639 return mono_threads_platform_yield ();
1642 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1643 static MonoCoopMutex sleep_mutex;
1644 static MonoCoopCond sleep_cond;
1646 static void
1647 sleep_initialize (void)
1649 mono_coop_mutex_init (&sleep_mutex);
1650 mono_coop_cond_init (&sleep_cond);
1653 static void
1654 sleep_interrupt (gpointer data)
1656 mono_coop_mutex_lock (&sleep_mutex);
1657 mono_coop_cond_broadcast (&sleep_cond);
1658 mono_coop_mutex_unlock (&sleep_mutex);
1661 static guint32
1662 sleep_interruptable (guint32 ms, gboolean *alerted)
1664 gint64 now, end;
1666 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1668 g_assert (alerted);
1669 *alerted = FALSE;
1671 if (ms != MONO_INFINITE_WAIT)
1672 end = mono_msec_ticks() + ms;
1674 mono_lazy_initialize (&sleep_init, sleep_initialize);
1676 mono_coop_mutex_lock (&sleep_mutex);
1678 for (;;) {
1679 if (ms != MONO_INFINITE_WAIT) {
1680 now = mono_msec_ticks();
1681 if (now >= end)
1682 break;
1685 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1686 if (*alerted) {
1687 mono_coop_mutex_unlock (&sleep_mutex);
1688 return WAIT_IO_COMPLETION;
1691 if (ms != MONO_INFINITE_WAIT)
1692 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1693 else
1694 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1696 mono_thread_info_uninstall_interrupt (alerted);
1697 if (*alerted) {
1698 mono_coop_mutex_unlock (&sleep_mutex);
1699 return WAIT_IO_COMPLETION;
1703 mono_coop_mutex_unlock (&sleep_mutex);
1705 return 0;
1708 gint
1709 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1711 if (ms == 0) {
1712 MonoThreadInfo *info;
1714 mono_thread_info_yield ();
1716 info = mono_thread_info_current ();
1717 if (info && mono_thread_info_is_interrupt_state (info))
1718 return WAIT_IO_COMPLETION;
1720 return 0;
1723 if (alerted)
1724 return sleep_interruptable (ms, alerted);
1726 MONO_ENTER_GC_SAFE;
1728 if (ms == MONO_INFINITE_WAIT) {
1729 do {
1730 #ifdef HOST_WIN32
1731 Sleep (G_MAXUINT32);
1732 #else
1733 sleep (G_MAXUINT32);
1734 #endif
1735 } while (1);
1736 } else {
1737 #if defined (HAVE_CLOCK_NANOSLEEP) && !defined(__PASE__)
1738 int ret;
1739 struct timespec start, target;
1741 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1742 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1743 g_assert (ret == 0);
1745 target = start;
1746 target.tv_sec += ms / 1000;
1747 target.tv_nsec += (ms % 1000) * 1000000;
1748 if (target.tv_nsec > 999999999) {
1749 target.tv_nsec -= 999999999;
1750 target.tv_sec ++;
1753 do {
1754 ret = g_clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1755 } while (ret != 0);
1756 #elif HOST_WIN32
1757 Sleep (ms);
1758 #else
1759 int ret;
1760 struct timespec req, rem;
1762 req.tv_sec = ms / 1000;
1763 req.tv_nsec = (ms % 1000) * 1000000;
1765 do {
1766 memset (&rem, 0, sizeof (rem));
1767 ret = nanosleep (&req, &rem);
1768 } while (ret != 0);
1769 #endif /* __linux__ */
1772 MONO_EXIT_GC_SAFE;
1774 return 0;
1777 gint
1778 mono_thread_info_usleep (guint64 us)
1780 MONO_ENTER_GC_SAFE;
1781 g_usleep (us);
1782 MONO_EXIT_GC_SAFE;
1783 return 0;
1786 gpointer
1787 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1789 return ((MonoThreadInfo*)info)->tls [key];
1793 * mono_threads_info_tls_set:
1795 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1796 * values of TLS variables for threads other than the current thread.
1797 * This should only be used for infrequently changing TLS variables, and it should
1798 * be paired with setting the real TLS variable since this provides no GC tracking.
1800 void
1801 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1803 ((MonoThreadInfo*)info)->tls [key] = value;
1807 * mono_thread_info_exit:
1809 * Exit the current thread.
1810 * This function doesn't return.
1812 void
1813 mono_thread_info_exit (gsize exit_code)
1815 mono_thread_info_detach ();
1817 mono_threads_platform_exit (0);
1821 * mono_threads_open_thread_handle:
1823 * Duplicate the handle. The handle needs to be closed by calling
1824 * mono_threads_close_thread_handle () when it is no longer needed.
1826 MonoThreadHandle*
1827 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1829 return mono_refcount_inc (thread_handle);
1832 void
1833 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1835 if (!thread_handle)
1836 return;
1837 mono_refcount_dec (thread_handle);
1841 * mono_threads_open_native_thread_handle:
1843 * Duplicate the handle. The handle needs to be closed by calling
1844 * mono_threads_close_native_thread_handle () when it is no longer needed.
1846 MonoNativeThreadHandle
1847 mono_threads_open_native_thread_handle (MonoNativeThreadHandle thread_handle)
1849 #ifdef HOST_WIN32
1850 BOOL success = FALSE;
1851 HANDLE new_thread_handle = NULL;
1853 g_assert (thread_handle && thread_handle != INVALID_HANDLE_VALUE);
1854 return DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &new_thread_handle, 0, FALSE, DUPLICATE_SAME_ACCESS) ? new_thread_handle : NULL;
1855 #else
1856 return MONO_GPOINTER_TO_NATIVE_THREAD_HANDLE (NULL);
1857 #endif
1860 void
1861 mono_threads_close_native_thread_handle (MonoNativeThreadHandle thread_handle)
1863 #ifdef HOST_WIN32
1864 g_assert (thread_handle != INVALID_HANDLE_VALUE);
1865 if (thread_handle)
1866 CloseHandle (thread_handle);
1867 #endif
1870 static void
1871 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1873 mono_os_event_set (&thread_handle->event);
1876 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1878 struct _MonoThreadInfoInterruptToken {
1879 void (*callback) (gpointer data);
1880 gpointer data;
1884 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1886 * - @callback: must be able to be called from another thread and always cancel the wait
1887 * - @data: passed to the callback
1888 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1889 * if set to TRUE, it must mean that the thread is in interrupted state
1891 void
1892 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1894 MonoThreadInfo *info;
1895 MonoThreadInfoInterruptToken *previous_token, *token;
1897 g_assert (callback);
1899 g_assert (interrupted);
1900 *interrupted = FALSE;
1902 info = mono_thread_info_current ();
1903 g_assertf (info, ""); // f includes __func__
1905 /* The memory of this token can be freed at 2 places:
1906 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1907 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1908 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1909 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1910 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1911 token->callback = callback;
1912 token->data = data;
1914 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, token, NULL);
1916 if (previous_token) {
1917 if (previous_token != INTERRUPT_STATE)
1918 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1920 g_free (token);
1922 *interrupted = TRUE;
1925 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1926 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1929 void
1930 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1932 MonoThreadInfo *info;
1933 MonoThreadInfoInterruptToken *previous_token;
1935 /* Common to uninstall interrupt handler around OS API's affecting last error. */
1936 /* This method could call OS API's on some platforms that will reset last error so make sure to restore */
1937 /* last error before exit. */
1938 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
1940 g_assert (interrupted);
1941 *interrupted = FALSE;
1943 info = mono_thread_info_current ();
1944 g_assertf (info, ""); // f includes __func__
1946 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_xchg_ptr ((gpointer*) &info->interrupt_token, NULL);
1948 /* only the installer can uninstall the token */
1949 g_assert (previous_token);
1951 if (previous_token == INTERRUPT_STATE) {
1952 /* if it is interrupted, then it is going to be freed in finish interrupt */
1953 *interrupted = TRUE;
1954 } else {
1955 g_free (previous_token);
1958 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1959 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1961 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
1964 static MonoThreadInfoInterruptToken*
1965 set_interrupt_state (MonoThreadInfo *info)
1967 MonoThreadInfoInterruptToken *token, *previous_token;
1969 g_assertf (info, ""); // f includes __func__
1971 /* Atomically obtain the token the thread is
1972 * waiting on, and change it to a flag value. */
1974 do {
1975 previous_token = info->interrupt_token;
1977 /* Already interrupted */
1978 if (previous_token == INTERRUPT_STATE) {
1979 token = NULL;
1980 break;
1983 token = previous_token;
1984 } while (mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1986 return token;
1990 * mono_thread_info_prepare_interrupt:
1992 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1993 * - if the thread calls one of the WaitFor functions, the function will return with
1994 * WAIT_IO_COMPLETION instead of waiting
1995 * - if the thread was waiting when this function was called, the wait will be broken
1997 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1998 * didn't receive the interrupt signal yet, in this case it should call the wait function
1999 * again. This essentially means that the target thread will busy wait until it is ready to
2000 * process the interruption.
2002 MonoThreadInfoInterruptToken*
2003 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
2005 MonoThreadInfoInterruptToken *token;
2007 token = set_interrupt_state (info);
2009 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
2010 mono_thread_info_get_tid (info), token);
2012 return token;
2015 void
2016 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
2018 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
2020 if (token == NULL)
2021 return;
2023 g_assert (token->callback);
2025 token->callback (token->data);
2027 g_free (token);
2030 void
2031 mono_thread_info_self_interrupt (void)
2033 MonoThreadInfo *info;
2034 MonoThreadInfoInterruptToken *token;
2036 info = mono_thread_info_current ();
2037 g_assertf (info, ""); // f includes __func__
2039 token = set_interrupt_state (info);
2040 g_assert (!token);
2042 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
2043 mono_thread_info_get_tid (info));
2046 /* Clear the interrupted flag of the current thread, set with
2047 * mono_thread_info_self_interrupt, so it can wait again */
2048 void
2049 mono_thread_info_clear_self_interrupt (void)
2051 MonoThreadInfo *info;
2052 MonoThreadInfoInterruptToken *previous_token;
2054 info = mono_thread_info_current ();
2055 g_assertf (info, ""); // f includes __func__
2057 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
2058 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
2060 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
2063 gboolean
2064 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
2066 g_assertf (info, ""); // f includes __func__
2067 return mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
2070 void
2071 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
2073 g_assertf (info, ""); // f includes __func__
2075 if (!mono_atomic_load_ptr ((gpointer*) &info->interrupt_token))
2076 g_string_append_printf (text, "not waiting");
2077 else if (mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
2078 g_string_append_printf (text, "interrupted state");
2079 else
2080 g_string_append_printf (text, "waiting");
2083 gboolean
2084 mono_thread_info_is_current (MonoThreadInfo *info)
2086 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
2089 MonoThreadInfoWaitRet
2090 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
2092 MonoOSEventWaitRet res;
2094 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
2095 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
2096 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
2097 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
2098 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
2099 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
2100 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2101 else
2102 g_error ("%s: unknown res value %d", __func__, res);
2105 MonoThreadInfoWaitRet
2106 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
2108 MonoOSEventWaitRet res;
2109 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
2110 gint i;
2112 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
2113 if (background_change_event)
2114 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
2116 for (i = 0; i < nhandles; ++i)
2117 thread_events [i] = &thread_handles [i]->event;
2119 if (background_change_event)
2120 thread_events [nhandles ++] = background_change_event;
2122 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
2123 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
2124 return (MonoThreadInfoWaitRet)(MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0));
2125 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
2126 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
2127 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
2128 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
2129 else
2130 g_error ("%s: unknown res value %d", __func__, res);
2134 * mono_threads_join_mutex:
2136 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
2137 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
2138 * The code inside the lock should not block.
2140 void
2141 mono_threads_join_lock (void)
2143 #ifdef TARGET_OSX
2144 mono_os_mutex_lock (&join_mutex);
2145 #endif
2148 void
2149 mono_threads_join_unlock (void)
2151 #ifdef TARGET_OSX
2152 mono_os_mutex_unlock (&join_mutex);
2153 #endif
2157 gboolean
2158 mono_thread_info_set_tools_data (void *data)
2160 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
2161 if (!info)
2162 return FALSE;
2163 if (info->tools_data)
2164 return FALSE;
2165 info->tools_data = data;
2166 return TRUE;
2169 void*
2170 mono_thread_info_get_tools_data (void)
2172 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
2174 return info ? info->tools_data : NULL;