Remove suspicious mono_gchandle_free
[mono-project.git] / mono / utils / mono-threads.c
blob4e77d0d5582a40ffcc00d14f2b331651aa3640f2
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>
39 #if defined(__MACH__)
40 #include <mono/utils/mach-support.h>
41 #endif
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
62 static MonoSemType global_suspend_semaphore;
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
68 #ifdef HAVE_KW_THREAD
69 static __thread gint32 tls_small_id = -1;
70 #else
71 static MonoNativeTlsKey small_id_key;
72 #endif
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
79 static mono_mutex_t join_mutex;
81 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
83 /*warn at 50 ms*/
84 #define SLEEP_DURATION_BEFORE_WARNING (50)
85 /*never aborts */
86 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
88 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
89 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
91 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
93 void
94 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
96 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
97 mono_atomic_inc_i32 (&abort_posts);
98 mono_os_sem_post (&suspend_semaphore);
101 void
102 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
104 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
105 mono_atomic_inc_i32 (&suspend_posts);
106 mono_os_sem_post (&suspend_semaphore);
109 void
110 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
112 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
113 mono_atomic_inc_i32 (&resume_posts);
114 mono_os_sem_post (&suspend_semaphore);
117 static gboolean
118 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
120 if (mono_threads_is_coop_enabled ()) {
121 /* There's nothing else to do after we async request the thread to suspend */
122 mono_threads_add_to_pending_operation_set (info);
123 return TRUE;
126 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
129 static gboolean
130 check_async_suspend (MonoThreadInfo *info)
132 if (mono_threads_is_coop_enabled ()) {
133 /* Async suspend can't async fail on coop */
134 return TRUE;
137 return mono_threads_suspend_check_suspend_result (info);
140 static void
141 resume_async_suspended (MonoThreadInfo *info)
143 if (mono_threads_is_coop_enabled ())
144 g_assert_not_reached ();
146 g_assert (mono_threads_suspend_begin_async_resume (info));
149 static void
150 resume_self_suspended (MonoThreadInfo* info)
152 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
153 mono_os_sem_post (&info->resume_semaphore);
156 void
157 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
159 int res;
160 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
161 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
162 g_assert (res != -1);
165 static void
166 resume_blocking_suspended (MonoThreadInfo* info)
168 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
169 mono_os_sem_post (&info->resume_semaphore);
172 void
173 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
175 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
176 ++pending_suspends;
177 mono_atomic_inc_i32 (&pending_ops);
180 void
181 mono_threads_begin_global_suspend (void)
183 size_t ps = pending_suspends;
184 if (G_UNLIKELY (ps != 0))
185 g_error ("pending_suspends = %d, but must be 0", ps);
186 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,
187 abort_posts, waits_done, pending_ops);
188 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
189 mono_threads_coop_begin_global_suspend ();
192 void
193 mono_threads_end_global_suspend (void)
195 size_t ps = pending_suspends;
196 if (G_UNLIKELY (ps != 0))
197 g_error ("pending_suspends = %d, but must be 0", ps);
198 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
199 abort_posts, waits_done, pending_ops);
200 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
201 mono_threads_coop_end_global_suspend ();
204 static void
205 dump_threads (void)
207 MonoThreadInfo *cur = mono_thread_info_current ();
209 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
217 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
218 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
220 FOREACH_THREAD_SAFE (info) {
221 #ifdef TARGET_MACH
222 char thread_name [256] = { 0 };
223 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
225 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
226 #else
227 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
228 #endif
229 } FOREACH_THREAD_SAFE_END
232 gboolean
233 mono_threads_wait_pending_operations (void)
235 int i;
236 int c = pending_suspends;
238 /* Wait threads to park */
239 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
240 if (pending_suspends) {
241 MonoStopwatch suspension_time;
242 mono_stopwatch_start (&suspension_time);
243 for (i = 0; i < pending_suspends; ++i) {
244 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
245 mono_atomic_inc_i32 (&waits_done);
246 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
247 continue;
248 mono_stopwatch_stop (&suspension_time);
250 dump_threads ();
252 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
253 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
255 mono_stopwatch_stop (&suspension_time);
256 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
260 pending_suspends = 0;
262 return c > 0;
266 //Thread initialization code
268 static inline void
269 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
271 if (retain != 0)
272 mono_hazard_pointer_clear (hp, 0);
273 if (retain != 1)
274 mono_hazard_pointer_clear (hp, 1);
275 if (retain != 2)
276 mono_hazard_pointer_clear (hp, 2);
280 If return non null Hazard Pointer 1 holds the return value.
282 MonoThreadInfo*
283 mono_thread_info_lookup (MonoNativeThreadId id)
285 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
287 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
288 mono_hazard_pointer_clear_all (hp, -1);
289 return NULL;
292 mono_hazard_pointer_clear_all (hp, 1);
293 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
296 static gboolean
297 mono_thread_info_insert (MonoThreadInfo *info)
299 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
301 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
302 mono_hazard_pointer_clear_all (hp, -1);
303 return FALSE;
306 mono_hazard_pointer_clear_all (hp, -1);
307 return TRUE;
310 static gboolean
311 mono_thread_info_remove (MonoThreadInfo *info)
313 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
314 gboolean res;
316 THREADS_DEBUG ("removing info %p\n", info);
317 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
318 mono_hazard_pointer_clear_all (hp, -1);
319 return res;
322 static void
323 free_thread_info (gpointer mem)
325 MonoThreadInfo *info = (MonoThreadInfo *) mem;
327 mono_os_sem_destroy (&info->resume_semaphore);
328 mono_threads_suspend_free (info);
330 g_free (info);
334 * mono_thread_info_register_small_id
336 * Registers a small ID for the current thread. This is a 16-bit value uniquely
337 * identifying the current thread. If the current thread already has a small ID
338 * assigned, that small ID will be returned; otherwise, the newly assigned small
339 * ID is returned.
342 mono_thread_info_register_small_id (void)
344 int small_id = mono_thread_info_get_small_id ();
346 if (small_id != -1)
347 return small_id;
349 small_id = mono_thread_small_id_alloc ();
350 #ifdef HAVE_KW_THREAD
351 tls_small_id = small_id;
352 #else
353 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
354 #endif
355 return small_id;
358 static void
359 thread_handle_destroy (gpointer data)
361 MonoThreadHandle *thread_handle;
363 thread_handle = (MonoThreadHandle*) data;
365 mono_os_event_destroy (&thread_handle->event);
366 g_free (thread_handle);
369 static gboolean
370 register_thread (MonoThreadInfo *info)
372 size_t stsize = 0;
373 guint8 *staddr = NULL;
374 gboolean result;
376 info->small_id = mono_thread_info_register_small_id ();
377 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
379 info->handle = g_new0 (MonoThreadHandle, 1);
380 mono_refcount_init (info->handle, thread_handle_destroy);
381 mono_os_event_init (&info->handle->event, FALSE);
383 mono_os_sem_init (&info->resume_semaphore, 0);
385 /*set TLS early so SMR works */
386 mono_native_tls_set_value (thread_info_key, info);
388 mono_thread_info_get_stack_bounds (&staddr, &stsize);
389 g_assert (staddr);
390 g_assert (stsize);
391 info->stack_start_limit = staddr;
392 info->stack_end = staddr + stsize;
394 info->stackdata = g_byte_array_new ();
396 info->internal_thread_gchandle = G_MAXUINT32;
398 info->profiler_signal_ack = 1;
400 mono_threads_suspend_register (info);
402 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
404 if (threads_callbacks.thread_attach) {
405 if (!threads_callbacks.thread_attach (info)) {
406 // g_warning ("thread registation failed\n");
407 mono_native_tls_set_value (thread_info_key, NULL);
408 return FALSE;
413 Transition it before taking any locks or publishing itself to reduce the chance
414 of others witnessing a detached thread.
415 We can reasonably expect that until this thread gets published, no other thread will
416 try to manipulate it.
418 mono_threads_transition_attach (info);
419 mono_thread_info_suspend_lock ();
420 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
421 result = mono_thread_info_insert (info);
422 g_assert (result);
423 mono_thread_info_suspend_unlock ();
425 return TRUE;
428 static void
429 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
431 static void
432 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
434 static void
435 unregister_thread (void *arg)
437 gpointer gc_unsafe_stackdata;
438 MonoThreadInfo *info;
439 int small_id;
440 gboolean result;
441 gpointer handle;
443 info = (MonoThreadInfo *) arg;
444 g_assert (info);
445 g_assert (mono_thread_info_is_current (info));
446 g_assert (mono_thread_info_is_live (info));
448 /* Pump the HP queue while the thread is alive.*/
449 mono_thread_hazardous_try_free_some ();
451 small_id = info->small_id;
453 /* We only enter the GC unsafe region, as when exiting this function, the thread
454 * will be detached, and the current MonoThreadInfo* will be destroyed. */
455 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
457 THREADS_DEBUG ("unregistering info %p\n", info);
459 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
462 * TLS destruction order is not reliable so small_id might be cleaned up
463 * before us.
465 #ifndef HAVE_KW_THREAD
466 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
467 #endif
469 /* we need to duplicate it, as the info->handle is going
470 * to be closed when unregistering from the platform */
471 handle = mono_threads_open_thread_handle (info->handle);
474 First perform the callback that requires no locks.
475 This callback has the potential of taking other locks, so we do it before.
476 After it completes, the thread remains functional.
478 if (threads_callbacks.thread_detach)
479 threads_callbacks.thread_detach (info);
481 mono_thread_info_suspend_lock_with_info (info);
484 Now perform the callback that must be done under locks.
485 This will render the thread useless and non-suspendable, so it must
486 be done while holding the suspend lock to give no other thread chance
487 to suspend it.
489 if (threads_callbacks.thread_detach_with_lock)
490 threads_callbacks.thread_detach_with_lock (info);
492 /* The thread is no longer active, so unref its handle */
493 mono_threads_close_thread_handle (info->handle);
494 info->handle = NULL;
496 result = mono_thread_info_remove (info);
497 g_assert (result);
498 mono_threads_transition_detach (info);
500 mono_thread_info_suspend_unlock ();
502 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
504 /*now it's safe to free the thread info.*/
505 mono_thread_hazardous_try_free (info, free_thread_info);
507 mono_thread_small_id_free (small_id);
509 mono_threads_signal_thread_handle (handle);
511 mono_threads_close_thread_handle (handle);
513 mono_native_tls_set_value (thread_info_key, NULL);
516 static void
517 thread_exited_dtor (void *arg)
519 #if defined(__MACH__)
521 * Since we use pthread dtors to clean up thread data, if a thread
522 * is attached to the runtime by another pthread dtor after our dtor
523 * has ran, it will never be detached, leading to various problems
524 * since the thread ids etc. will be reused while they are still in
525 * the threads hashtables etc.
526 * Dtors are called in a loop until all user tls entries are 0,
527 * but the loop has a maximum count (4), so if we set the tls
528 * variable every time, it will remain set when system tls dtors
529 * are ran. This allows mono_thread_info_is_exiting () to detect
530 * whenever the thread is exiting, even if it is executed from a
531 * system tls dtor (i.e. obj-c dealloc methods).
533 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
534 #endif
537 MonoThreadInfo*
538 mono_thread_info_current_unchecked (void)
540 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
544 MonoThreadInfo*
545 mono_thread_info_current (void)
547 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
548 if (info)
549 return info;
551 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
554 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
555 The way to distinguish between before, during and after cleanup is the following:
557 -If the TLS key is set, cleanup has not begun;
558 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
559 -If the thread is nowhere to be found, cleanup has finished.
561 We cannot function after cleanup since there's no way to ensure what will happen.
563 g_assert (info);
565 /*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 */
566 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
568 return info;
572 * mono_thread_info_get_small_id
574 * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
575 * identifying the current thread. Returns -1 if the current thread doesn't have
576 * a small ID assigned.
578 * To ensure that the calling thread has a small ID assigned, call either
579 * mono_thread_info_attach or mono_thread_info_register_small_id.
582 mono_thread_info_get_small_id (void)
584 #ifdef HAVE_KW_THREAD
585 return tls_small_id;
586 #else
587 gpointer val = mono_native_tls_get_value (small_id_key);
588 if (!val)
589 return -1;
590 return GPOINTER_TO_INT (val) - 1;
591 #endif
594 MonoLinkedListSet*
595 mono_thread_info_list_head (void)
597 return &thread_list;
601 * mono_threads_attach_tools_thread
603 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
605 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
606 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
607 * the managed heap.
609 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
610 * doing things like resolving backtraces in their background processing thread.
612 void
613 mono_threads_attach_tools_thread (void)
615 MonoThreadInfo *info;
617 /* Must only be called once */
618 g_assert (!mono_native_tls_get_value (thread_info_key));
620 while (!mono_threads_inited) {
621 mono_thread_info_usleep (10);
624 info = mono_thread_info_attach ();
625 g_assert (info);
627 info->tools_thread = TRUE;
630 MonoThreadInfo*
631 mono_thread_info_attach (void)
633 MonoThreadInfo *info;
635 #ifdef HOST_WIN32
636 if (!mono_threads_inited)
638 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
639 * thread is created before an embedding API user initialized Mono. */
640 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
641 return NULL;
643 #endif
645 g_assert (mono_threads_inited);
647 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
648 if (!info) {
649 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
650 THREADS_DEBUG ("attaching %p\n", info);
651 if (!register_thread (info)) {
652 g_free (info);
653 return NULL;
657 return info;
660 void
661 mono_thread_info_detach (void)
663 MonoThreadInfo *info;
665 #ifdef HOST_WIN32
666 if (!mono_threads_inited)
668 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
669 * is created before an embedding API user initialized Mono. */
670 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
671 return;
673 #endif
675 g_assert (mono_threads_inited);
677 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
678 if (info) {
679 THREADS_DEBUG ("detaching %p\n", info);
680 unregister_thread (info);
684 gboolean
685 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
687 g_assert (info);
688 g_assert (mono_thread_info_is_current (info));
690 if (info->internal_thread_gchandle == G_MAXUINT32)
691 return FALSE;
693 *gchandle = info->internal_thread_gchandle;
694 return TRUE;
697 void
698 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
700 g_assert (info);
701 g_assert (mono_thread_info_is_current (info));
702 g_assert (gchandle != G_MAXUINT32);
703 info->internal_thread_gchandle = gchandle;
706 void
707 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
709 g_assert (info);
710 g_assert (mono_thread_info_is_current (info));
711 info->internal_thread_gchandle = G_MAXUINT32;
715 * mono_thread_info_is_exiting:
717 * Return whenever the current thread is exiting, i.e. it is running pthread
718 * dtors.
720 gboolean
721 mono_thread_info_is_exiting (void)
723 #if defined(__MACH__)
724 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
725 return TRUE;
726 #endif
727 return FALSE;
730 #ifndef HOST_WIN32
731 static void
732 thread_info_key_dtor (void *arg)
734 /* Put the MonoThreadInfo back for the duration of the
735 * unregister code. In some circumstances the thread needs to
736 * take the GC lock which may block which requires a coop
737 * state transition. */
738 mono_native_tls_set_value (thread_info_key, arg);
739 unregister_thread (arg);
740 mono_native_tls_set_value (thread_info_key, NULL);
742 #endif
744 void
745 mono_thread_info_init (size_t info_size)
747 gboolean res;
748 thread_info_size = info_size;
749 char *sleepLimit;
750 #ifdef HOST_WIN32
751 res = mono_native_tls_alloc (&thread_info_key, NULL);
752 res = mono_native_tls_alloc (&thread_exited_key, NULL);
753 #else
754 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
755 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
756 #endif
758 g_assert (res);
760 #ifndef HAVE_KW_THREAD
761 res = mono_native_tls_alloc (&small_id_key, NULL);
762 #endif
763 g_assert (res);
765 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
766 errno = 0;
767 long threshold = strtol(sleepLimit, NULL, 10);
768 if ((errno == 0) && (threshold >= 40)) {
769 sleepAbortDuration = threshold;
770 sleepWarnDuration = threshold / 20;
771 } else
772 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
773 g_free (sleepLimit);
776 mono_os_sem_init (&global_suspend_semaphore, 1);
777 mono_os_sem_init (&suspend_semaphore, 0);
778 mono_os_mutex_init (&join_mutex);
780 mono_lls_init (&thread_list, NULL);
781 mono_thread_smr_init ();
782 mono_threads_suspend_init ();
783 mono_threads_coop_init ();
784 mono_threads_platform_init ();
786 #if defined(__MACH__)
787 mono_mach_init (thread_info_key);
788 #endif
790 mono_threads_inited = TRUE;
792 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
795 void
796 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
798 threads_callbacks = *callbacks;
801 void
802 mono_thread_info_signals_init (void)
804 mono_threads_suspend_init_signals ();
807 void
808 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
810 runtime_callbacks = *callbacks;
813 MonoThreadInfoRuntimeCallbacks *
814 mono_threads_get_runtime_callbacks (void)
816 return &runtime_callbacks;
819 static gboolean
820 mono_thread_info_core_resume (MonoThreadInfo *info)
822 gboolean res = FALSE;
824 switch (mono_threads_transition_request_resume (info)) {
825 case ResumeError:
826 res = FALSE;
827 break;
828 case ResumeOk:
829 res = TRUE;
830 break;
831 case ResumeInitSelfResume:
832 resume_self_suspended (info);
833 res = TRUE;
834 break;
835 case ResumeInitAsyncResume:
836 resume_async_suspended (info);
837 res = TRUE;
838 break;
839 case ResumeInitBlockingResume:
840 resume_blocking_suspended (info);
841 res = TRUE;
842 break;
845 return res;
848 gboolean
849 mono_thread_info_resume (MonoNativeThreadId tid)
851 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
852 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
853 MonoThreadInfo *info;
855 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
857 mono_thread_info_suspend_lock ();
859 info = mono_thread_info_lookup (tid); /*info on HP1*/
860 if (!info) {
861 result = FALSE;
862 goto cleanup;
865 result = mono_thread_info_core_resume (info);
867 //Wait for the pending resume to finish
868 mono_threads_wait_pending_operations ();
870 cleanup:
871 mono_thread_info_suspend_unlock ();
872 mono_hazard_pointer_clear (hp, 1);
873 return result;
876 gboolean
877 mono_thread_info_begin_suspend (MonoThreadInfo *info)
879 switch (mono_threads_transition_request_async_suspension (info)) {
880 case AsyncSuspendAlreadySuspended:
881 case AsyncSuspendBlocking:
882 return TRUE;
883 case AsyncSuspendWait:
884 mono_threads_add_to_pending_operation_set (info);
885 return TRUE;
886 case AsyncSuspendInitSuspend:
887 return begin_async_suspend (info, FALSE);
888 default:
889 g_assert_not_reached ();
893 gboolean
894 mono_thread_info_begin_resume (MonoThreadInfo *info)
896 return mono_thread_info_core_resume (info);
900 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
901 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
903 static gboolean
904 is_thread_in_critical_region (MonoThreadInfo *info)
906 gpointer stack_start;
907 MonoThreadUnwindState *state;
909 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
910 return TRUE;
912 /* Are we inside a system critical region? */
913 if (info->inside_critical_region)
914 return TRUE;
916 /* Are we inside a GC critical region? */
917 if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
918 return TRUE;
921 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
922 state = mono_thread_info_get_suspend_state (info);
923 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
924 return FALSE;
926 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
927 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
928 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
929 return TRUE;
931 if (threads_callbacks.ip_in_critical_region)
932 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
934 return FALSE;
937 gboolean
938 mono_thread_info_in_critical_location (MonoThreadInfo *info)
940 return is_thread_in_critical_region (info);
944 The return value is only valid until a matching mono_thread_info_resume is called
946 static MonoThreadInfo*
947 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
949 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
950 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
951 if (!info)
952 return NULL;
954 switch (mono_threads_transition_request_async_suspension (info)) {
955 case AsyncSuspendAlreadySuspended:
956 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
957 return info;
958 case AsyncSuspendWait:
959 mono_threads_add_to_pending_operation_set (info);
960 break;
961 case AsyncSuspendInitSuspend:
962 if (!begin_async_suspend (info, interrupt_kernel)) {
963 mono_hazard_pointer_clear (hp, 1);
964 return NULL;
966 break;
967 case AsyncSuspendBlocking:
968 if (interrupt_kernel)
969 mono_threads_suspend_abort_syscall (info);
971 return info;
972 default:
973 g_assert_not_reached ();
976 //Wait for the pending suspend to finish
977 mono_threads_wait_pending_operations ();
979 if (!check_async_suspend (info)) {
980 mono_thread_info_core_resume (info);
981 mono_threads_wait_pending_operations ();
982 mono_hazard_pointer_clear (hp, 1);
983 return NULL;
985 return info;
988 static MonoThreadInfo*
989 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
991 MonoThreadInfo *info = NULL;
992 int sleep_duration = 0;
993 for (;;) {
994 if (!(info = suspend_sync (id, interrupt_kernel))) {
995 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
996 return NULL;
999 /*WARNING: We now are in interrupt context until we resume the thread. */
1000 if (!is_thread_in_critical_region (info))
1001 break;
1003 if (!mono_thread_info_core_resume (info)) {
1004 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
1005 return NULL;
1007 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
1009 /* Wait for the pending resume to finish */
1010 mono_threads_wait_pending_operations ();
1012 if (sleep_duration == 0)
1013 mono_thread_info_yield ();
1014 else
1015 g_usleep (sleep_duration);
1017 sleep_duration += 10;
1019 return info;
1022 void
1023 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
1025 int result;
1026 MonoThreadInfo *info = NULL;
1027 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1029 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
1030 /*FIXME: unify this with self-suspend*/
1031 g_assert (id != mono_native_thread_id_get ());
1033 /* This can block during stw */
1034 mono_thread_info_suspend_lock ();
1035 mono_threads_begin_global_suspend ();
1037 info = suspend_sync_nolock (id, interrupt_kernel);
1038 if (!info)
1039 goto done;
1041 switch (result = callback (info, user_data)) {
1042 case MonoResumeThread:
1043 mono_hazard_pointer_set (hp, 1, info);
1044 mono_thread_info_core_resume (info);
1045 mono_threads_wait_pending_operations ();
1046 break;
1047 case KeepSuspended:
1048 g_assert (!mono_threads_is_coop_enabled ());
1049 break;
1050 default:
1051 g_error ("Invalid suspend_and_run callback return value %d", result);
1054 done:
1055 mono_hazard_pointer_clear (hp, 1);
1056 mono_threads_end_global_suspend ();
1057 mono_thread_info_suspend_unlock ();
1061 Inject an assynchronous call into the target thread. The target thread must be suspended and
1062 only a single async call can be setup for a given suspend cycle.
1063 This async call must cause stack unwinding as the current implementation doesn't save enough state
1064 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1065 currently used only to deliver exceptions.
1067 void
1068 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1070 if (!mono_threads_is_coop_enabled ()) {
1071 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1072 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1073 * region or entering a gc unsafe region */
1074 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1076 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1077 g_assert (!info->async_target);
1078 info->async_target = target_func;
1079 /* This is not GC tracked */
1080 info->user_data = user_data;
1084 The suspend lock is held during any suspend in progress.
1085 A GC that has safepoints must take this lock as part of its
1086 STW to make sure no unsafe pending suspend is in progress.
1089 static void
1090 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1092 g_assert (info);
1093 g_assert (mono_thread_info_is_current (info));
1094 g_assert (mono_thread_info_is_live (info));
1096 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1098 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1099 g_assert (res != -1);
1101 MONO_EXIT_GC_SAFE_WITH_INFO;
1104 void
1105 mono_thread_info_suspend_lock (void)
1107 MonoThreadInfo *info;
1108 gint res;
1110 info = mono_thread_info_current_unchecked ();
1111 if (info && mono_thread_info_is_live (info)) {
1112 mono_thread_info_suspend_lock_with_info (info);
1113 return;
1116 /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
1117 * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
1119 res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1120 g_assert (res != -1);
1123 void
1124 mono_thread_info_suspend_unlock (void)
1126 mono_os_sem_post (&global_suspend_semaphore);
1130 * This is a very specific function whose only purpose is to
1131 * break a given thread from socket syscalls.
1133 * This only exists because linux won't fail a call to connect
1134 * if the underlying is closed.
1136 * TODO We should cleanup and unify this with the other syscall abort
1137 * facility.
1139 void
1140 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1142 MonoThreadHazardPointers *hp;
1143 MonoThreadInfo *info;
1145 if (tid == mono_native_thread_id_get ())
1146 return;
1148 hp = mono_hazard_pointer_get ();
1149 info = mono_thread_info_lookup (tid);
1150 if (!info)
1151 return;
1153 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1154 mono_hazard_pointer_clear (hp, 1);
1155 return;
1158 mono_thread_info_suspend_lock ();
1159 mono_threads_begin_global_suspend ();
1161 mono_threads_suspend_abort_syscall (info);
1162 mono_threads_wait_pending_operations ();
1164 mono_hazard_pointer_clear (hp, 1);
1166 mono_threads_end_global_suspend ();
1167 mono_thread_info_suspend_unlock ();
1171 * mono_thread_info_set_is_async_context:
1173 * Set whenever the current thread is in an async context. Some runtime functions might behave
1174 * differently while in an async context in order to be async safe.
1176 void
1177 mono_thread_info_set_is_async_context (gboolean async_context)
1179 MonoThreadInfo *info = mono_thread_info_current ();
1181 if (info)
1182 info->is_async_context = async_context;
1185 gboolean
1186 mono_thread_info_is_async_context (void)
1188 MonoThreadInfo *info = mono_thread_info_current ();
1190 if (info)
1191 return info->is_async_context;
1192 else
1193 return FALSE;
1197 * mono_thread_info_get_stack_bounds:
1199 * Return the address and size of the current threads stack. Return NULL as the
1200 * stack address if the stack address cannot be determined.
1202 void
1203 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1205 guint8 *current = (guint8 *)&stsize;
1206 mono_threads_platform_get_stack_bounds (staddr, stsize);
1207 if (!*staddr)
1208 return;
1210 /* Sanity check the result */
1211 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1213 /* When running under emacs, sometimes staddr is not aligned to a page size */
1214 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1217 gboolean
1218 mono_thread_info_yield (void)
1220 return mono_threads_platform_yield ();
1223 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1224 static MonoCoopMutex sleep_mutex;
1225 static MonoCoopCond sleep_cond;
1227 static void
1228 sleep_initialize (void)
1230 mono_coop_mutex_init (&sleep_mutex);
1231 mono_coop_cond_init (&sleep_cond);
1234 static void
1235 sleep_interrupt (gpointer data)
1237 mono_coop_mutex_lock (&sleep_mutex);
1238 mono_coop_cond_broadcast (&sleep_cond);
1239 mono_coop_mutex_unlock (&sleep_mutex);
1242 static inline guint32
1243 sleep_interruptable (guint32 ms, gboolean *alerted)
1245 gint64 now, end;
1247 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1249 g_assert (alerted);
1250 *alerted = FALSE;
1252 if (ms != MONO_INFINITE_WAIT)
1253 end = mono_msec_ticks() + ms;
1255 mono_lazy_initialize (&sleep_init, sleep_initialize);
1257 mono_coop_mutex_lock (&sleep_mutex);
1259 for (;;) {
1260 if (ms != MONO_INFINITE_WAIT) {
1261 now = mono_msec_ticks();
1262 if (now >= end)
1263 break;
1266 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1267 if (*alerted) {
1268 mono_coop_mutex_unlock (&sleep_mutex);
1269 return WAIT_IO_COMPLETION;
1272 if (ms != MONO_INFINITE_WAIT)
1273 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1274 else
1275 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1277 mono_thread_info_uninstall_interrupt (alerted);
1278 if (*alerted) {
1279 mono_coop_mutex_unlock (&sleep_mutex);
1280 return WAIT_IO_COMPLETION;
1284 mono_coop_mutex_unlock (&sleep_mutex);
1286 return 0;
1289 gint
1290 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1292 if (ms == 0) {
1293 MonoThreadInfo *info;
1295 mono_thread_info_yield ();
1297 info = mono_thread_info_current ();
1298 if (info && mono_thread_info_is_interrupt_state (info))
1299 return WAIT_IO_COMPLETION;
1301 return 0;
1304 if (alerted)
1305 return sleep_interruptable (ms, alerted);
1307 MONO_ENTER_GC_SAFE;
1309 if (ms == MONO_INFINITE_WAIT) {
1310 do {
1311 #ifdef HOST_WIN32
1312 Sleep (G_MAXUINT32);
1313 #else
1314 sleep (G_MAXUINT32);
1315 #endif
1316 } while (1);
1317 } else {
1318 int ret;
1319 #if defined (__linux__) && !defined(HOST_ANDROID)
1320 struct timespec start, target;
1322 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1323 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1324 g_assert (ret == 0);
1326 target = start;
1327 target.tv_sec += ms / 1000;
1328 target.tv_nsec += (ms % 1000) * 1000000;
1329 if (target.tv_nsec > 999999999) {
1330 target.tv_nsec -= 999999999;
1331 target.tv_sec ++;
1334 do {
1335 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1336 } while (ret != 0);
1337 #elif HOST_WIN32
1338 Sleep (ms);
1339 #else
1340 struct timespec req, rem;
1342 req.tv_sec = ms / 1000;
1343 req.tv_nsec = (ms % 1000) * 1000000;
1345 do {
1346 memset (&rem, 0, sizeof (rem));
1347 ret = nanosleep (&req, &rem);
1348 } while (ret != 0);
1349 #endif /* __linux__ */
1352 MONO_EXIT_GC_SAFE;
1354 return 0;
1357 gint
1358 mono_thread_info_usleep (guint64 us)
1360 MONO_ENTER_GC_SAFE;
1361 g_usleep (us);
1362 MONO_EXIT_GC_SAFE;
1363 return 0;
1366 gpointer
1367 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1369 return ((MonoThreadInfo*)info)->tls [key];
1373 * mono_threads_info_tls_set:
1375 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1376 * values of TLS variables for threads other than the current thread.
1377 * This should only be used for infrequently changing TLS variables, and it should
1378 * be paired with setting the real TLS variable since this provides no GC tracking.
1380 void
1381 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1383 ((MonoThreadInfo*)info)->tls [key] = value;
1387 * mono_thread_info_exit:
1389 * Exit the current thread.
1390 * This function doesn't return.
1392 void
1393 mono_thread_info_exit (gsize exit_code)
1395 mono_thread_info_detach ();
1397 mono_threads_platform_exit (0);
1401 * mono_threads_open_thread_handle:
1403 * Duplicate the handle. The handle needs to be closed by calling
1404 * mono_threads_close_thread_handle () when it is no longer needed.
1406 MonoThreadHandle*
1407 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1409 return mono_refcount_inc (thread_handle);
1412 void
1413 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1415 mono_refcount_dec (thread_handle);
1418 static void
1419 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1421 mono_os_event_set (&thread_handle->event);
1424 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1426 struct _MonoThreadInfoInterruptToken {
1427 void (*callback) (gpointer data);
1428 gpointer data;
1432 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1434 * - @callback: must be able to be called from another thread and always cancel the wait
1435 * - @data: passed to the callback
1436 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1437 * if set to TRUE, it must mean that the thread is in interrupted state
1439 void
1440 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1442 MonoThreadInfo *info;
1443 MonoThreadInfoInterruptToken *previous_token, *token;
1445 g_assert (callback);
1447 g_assert (interrupted);
1448 *interrupted = FALSE;
1450 info = mono_thread_info_current ();
1451 g_assert (info);
1453 /* The memory of this token can be freed at 2 places:
1454 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1455 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1456 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1457 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1458 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1459 token->callback = callback;
1460 token->data = data;
1462 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, token, NULL);
1464 if (previous_token) {
1465 if (previous_token != INTERRUPT_STATE)
1466 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1468 g_free (token);
1470 *interrupted = TRUE;
1473 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1474 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1477 void
1478 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1480 MonoThreadInfo *info;
1481 MonoThreadInfoInterruptToken *previous_token;
1483 g_assert (interrupted);
1484 *interrupted = FALSE;
1486 info = mono_thread_info_current ();
1487 g_assert (info);
1489 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_xchg_ptr ((gpointer*) &info->interrupt_token, NULL);
1491 /* only the installer can uninstall the token */
1492 g_assert (previous_token);
1494 if (previous_token == INTERRUPT_STATE) {
1495 /* if it is interrupted, then it is going to be freed in finish interrupt */
1496 *interrupted = TRUE;
1497 } else {
1498 g_free (previous_token);
1501 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1502 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1505 static MonoThreadInfoInterruptToken*
1506 set_interrupt_state (MonoThreadInfo *info)
1508 MonoThreadInfoInterruptToken *token, *previous_token;
1510 g_assert (info);
1512 /* Atomically obtain the token the thread is
1513 * waiting on, and change it to a flag value. */
1515 do {
1516 previous_token = info->interrupt_token;
1518 /* Already interrupted */
1519 if (previous_token == INTERRUPT_STATE) {
1520 token = NULL;
1521 break;
1524 token = previous_token;
1525 } while (mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1527 return token;
1531 * mono_thread_info_prepare_interrupt:
1533 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1534 * - if the thread calls one of the WaitFor functions, the function will return with
1535 * WAIT_IO_COMPLETION instead of waiting
1536 * - if the thread was waiting when this function was called, the wait will be broken
1538 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1539 * didn't receive the interrupt signal yet, in this case it should call the wait function
1540 * again. This essentially means that the target thread will busy wait until it is ready to
1541 * process the interruption.
1543 MonoThreadInfoInterruptToken*
1544 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1546 MonoThreadInfoInterruptToken *token;
1548 token = set_interrupt_state (info);
1550 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1551 mono_thread_info_get_tid (info), token);
1553 return token;
1556 void
1557 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1559 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1561 if (token == NULL)
1562 return;
1564 g_assert (token->callback);
1566 token->callback (token->data);
1568 g_free (token);
1571 void
1572 mono_thread_info_self_interrupt (void)
1574 MonoThreadInfo *info;
1575 MonoThreadInfoInterruptToken *token;
1577 info = mono_thread_info_current ();
1578 g_assert (info);
1580 token = set_interrupt_state (info);
1581 g_assert (!token);
1583 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1584 mono_thread_info_get_tid (info));
1587 /* Clear the interrupted flag of the current thread, set with
1588 * mono_thread_info_self_interrupt, so it can wait again */
1589 void
1590 mono_thread_info_clear_self_interrupt ()
1592 MonoThreadInfo *info;
1593 MonoThreadInfoInterruptToken *previous_token;
1595 info = mono_thread_info_current ();
1596 g_assert (info);
1598 previous_token = (MonoThreadInfoInterruptToken *)mono_atomic_cas_ptr ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1599 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1601 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1604 gboolean
1605 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1607 g_assert (info);
1608 return mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1611 void
1612 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1614 g_assert (info);
1616 if (!mono_atomic_load_ptr ((gpointer*) &info->interrupt_token))
1617 g_string_append_printf (text, "not waiting");
1618 else if (mono_atomic_load_ptr ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1619 g_string_append_printf (text, "interrupted state");
1620 else
1621 g_string_append_printf (text, "waiting");
1624 gboolean
1625 mono_thread_info_is_current (MonoThreadInfo *info)
1627 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1630 MonoThreadInfoWaitRet
1631 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1633 MonoOSEventWaitRet res;
1635 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1636 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1637 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1638 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1639 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1640 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1641 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1642 else
1643 g_error ("%s: unknown res value %d", __func__, res);
1646 MonoThreadInfoWaitRet
1647 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1649 MonoOSEventWaitRet res;
1650 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1651 gint i;
1653 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1654 if (background_change_event)
1655 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1657 for (i = 0; i < nhandles; ++i)
1658 thread_events [i] = &thread_handles [i]->event;
1660 if (background_change_event)
1661 thread_events [nhandles ++] = background_change_event;
1663 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1664 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1665 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1666 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1667 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1668 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1669 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1670 else
1671 g_error ("%s: unknown res value %d", __func__, res);
1675 * mono_threads_join_mutex:
1677 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1678 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1679 * The code inside the lock should not block.
1681 void
1682 mono_threads_join_lock (void)
1684 #ifdef TARGET_OSX
1685 mono_os_mutex_lock (&join_mutex);
1686 #endif
1689 void
1690 mono_threads_join_unlock (void)
1692 #ifdef TARGET_OSX
1693 mono_os_mutex_unlock (&join_mutex);
1694 #endif