In mono_thread_info_suspend_lock in non-coop, don't assert MonoThreadInfo* is non...
[mono-project.git] / mono / utils / mono-threads.c
blob6afb74daf8de35618dbe6a1374f9100aaf64c124
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 guint32 tls_small_id;
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 InterlockedIncrement (&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 InterlockedIncrement (&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 InterlockedIncrement (&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 InterlockedIncrement (&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 InterlockedIncrement (&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 (void)
336 int small_id = mono_thread_small_id_alloc ();
337 #ifdef HAVE_KW_THREAD
338 tls_small_id = small_id;
339 #else
340 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
341 #endif
342 return small_id;
345 static void
346 thread_handle_destroy (gpointer data)
348 MonoThreadHandle *thread_handle;
350 thread_handle = (MonoThreadHandle*) data;
352 mono_os_event_destroy (&thread_handle->event);
353 g_free (thread_handle);
356 static void*
357 register_thread (MonoThreadInfo *info, gpointer baseptr)
359 size_t stsize = 0;
360 guint8 *staddr = NULL;
361 int small_id = mono_thread_info_register_small_id ();
362 gboolean result;
363 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
364 info->small_id = small_id;
366 info->handle = g_new0 (MonoThreadHandle, 1);
367 mono_refcount_init (info->handle, thread_handle_destroy);
368 mono_os_event_init (&info->handle->event, FALSE);
370 mono_os_sem_init (&info->resume_semaphore, 0);
372 /*set TLS early so SMR works */
373 mono_native_tls_set_value (thread_info_key, info);
375 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
377 if (threads_callbacks.thread_register) {
378 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
379 // g_warning ("thread registation failed\n");
380 mono_native_tls_set_value (thread_info_key, NULL);
381 g_free (info);
382 return NULL;
386 mono_thread_info_get_stack_bounds (&staddr, &stsize);
387 g_assert (staddr);
388 g_assert (stsize);
389 info->stack_start_limit = staddr;
390 info->stack_end = staddr + stsize;
392 info->stackdata = g_byte_array_new ();
394 mono_threads_suspend_register (info);
397 Transition it before taking any locks or publishing itself to reduce the chance
398 of others witnessing a detached thread.
399 We can reasonably expect that until this thread gets published, no other thread will
400 try to manipulate it.
402 mono_threads_transition_attach (info);
403 mono_thread_info_suspend_lock ();
404 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
405 result = mono_thread_info_insert (info);
406 g_assert (result);
407 mono_thread_info_suspend_unlock ();
408 return info;
411 static void
412 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
414 static void
415 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
417 static void
418 unregister_thread (void *arg)
420 gpointer gc_unsafe_stackdata;
421 MonoThreadInfo *info;
422 int small_id;
423 gboolean result;
424 gpointer handle;
426 info = (MonoThreadInfo *) arg;
427 g_assert (info);
428 g_assert (mono_thread_info_is_current (info));
429 g_assert (mono_thread_info_is_live (info));
431 /* Pump the HP queue while the thread is alive.*/
432 mono_thread_hazardous_try_free_some ();
434 small_id = info->small_id;
436 /* We only enter the GC unsafe region, as when exiting this function, the thread
437 * will be detached, and the current MonoThreadInfo* will be destroyed. */
438 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
440 THREADS_DEBUG ("unregistering info %p\n", info);
442 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
445 * TLS destruction order is not reliable so small_id might be cleaned up
446 * before us.
448 #ifndef HAVE_KW_THREAD
449 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
450 #endif
452 /* we need to duplicate it, as the info->handle is going
453 * to be closed when unregistering from the platform */
454 handle = mono_threads_open_thread_handle (info->handle);
457 First perform the callback that requires no locks.
458 This callback has the potential of taking other locks, so we do it before.
459 After it completes, the thread remains functional.
461 if (threads_callbacks.thread_detach)
462 threads_callbacks.thread_detach (info);
464 mono_thread_info_suspend_lock_with_info (info);
467 Now perform the callback that must be done under locks.
468 This will render the thread useless and non-suspendable, so it must
469 be done while holding the suspend lock to give no other thread chance
470 to suspend it.
472 if (threads_callbacks.thread_unregister)
473 threads_callbacks.thread_unregister (info);
475 /* The thread is no longer active, so unref its handle */
476 mono_threads_close_thread_handle (info->handle);
477 info->handle = NULL;
479 result = mono_thread_info_remove (info);
480 g_assert (result);
481 mono_threads_transition_detach (info);
483 mono_thread_info_suspend_unlock ();
485 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
487 /*now it's safe to free the thread info.*/
488 mono_thread_hazardous_try_free (info, free_thread_info);
490 mono_thread_small_id_free (small_id);
492 mono_threads_signal_thread_handle (handle);
494 mono_threads_close_thread_handle (handle);
497 static void
498 thread_exited_dtor (void *arg)
500 #if defined(__MACH__)
502 * Since we use pthread dtors to clean up thread data, if a thread
503 * is attached to the runtime by another pthread dtor after our dtor
504 * has ran, it will never be detached, leading to various problems
505 * since the thread ids etc. will be reused while they are still in
506 * the threads hashtables etc.
507 * Dtors are called in a loop until all user tls entries are 0,
508 * but the loop has a maximum count (4), so if we set the tls
509 * variable every time, it will remain set when system tls dtors
510 * are ran. This allows mono_thread_info_is_exiting () to detect
511 * whenever the thread is exiting, even if it is executed from a
512 * system tls dtor (i.e. obj-c dealloc methods).
514 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
515 #endif
518 MonoThreadInfo*
519 mono_thread_info_current_unchecked (void)
521 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
525 MonoThreadInfo*
526 mono_thread_info_current (void)
528 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
529 if (info)
530 return info;
532 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
535 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
536 The way to distinguish between before, during and after cleanup is the following:
538 -If the TLS key is set, cleanup has not begun;
539 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
540 -If the thread is nowhere to be found, cleanup has finished.
542 We cannot function after cleanup since there's no way to ensure what will happen.
544 g_assert (info);
546 /*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 */
547 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
549 return info;
553 mono_thread_info_get_small_id (void)
555 #ifdef HAVE_KW_THREAD
556 return tls_small_id;
557 #else
558 gpointer val = mono_native_tls_get_value (small_id_key);
559 if (!val)
560 return -1;
561 return GPOINTER_TO_INT (val) - 1;
562 #endif
565 MonoLinkedListSet*
566 mono_thread_info_list_head (void)
568 return &thread_list;
572 * mono_threads_attach_tools_thread
574 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
576 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
577 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
578 * the managed heap.
580 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
581 * doing things like resolving backtraces in their background processing thread.
583 void
584 mono_threads_attach_tools_thread (void)
586 int dummy = 0;
587 MonoThreadInfo *info;
589 /* Must only be called once */
590 g_assert (!mono_native_tls_get_value (thread_info_key));
592 while (!mono_threads_inited) {
593 mono_thread_info_usleep (10);
596 info = mono_thread_info_attach (&dummy);
597 g_assert (info);
599 info->tools_thread = TRUE;
602 MonoThreadInfo*
603 mono_thread_info_attach (void *baseptr)
605 MonoThreadInfo *info;
606 if (!mono_threads_inited)
608 #ifdef HOST_WIN32
609 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
610 * thread is created before an embedding API user initialized Mono. */
611 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
612 return NULL;
613 #else
614 g_assert (mono_threads_inited);
615 #endif
617 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
618 if (!info) {
619 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
620 THREADS_DEBUG ("attaching %p\n", info);
621 if (!register_thread (info, baseptr))
622 return NULL;
623 } else if (threads_callbacks.thread_attach) {
624 threads_callbacks.thread_attach (info);
626 return info;
629 void
630 mono_thread_info_detach (void)
632 MonoThreadInfo *info;
633 if (!mono_threads_inited)
635 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
636 * is created before an embedding API user initialized Mono. */
637 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
638 return;
640 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
641 if (info) {
642 THREADS_DEBUG ("detaching %p\n", info);
643 unregister_thread (info);
644 mono_native_tls_set_value (thread_info_key, NULL);
649 * mono_thread_info_is_exiting:
651 * Return whenever the current thread is exiting, i.e. it is running pthread
652 * dtors.
654 gboolean
655 mono_thread_info_is_exiting (void)
657 #if defined(__MACH__)
658 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
659 return TRUE;
660 #endif
661 return FALSE;
664 #ifndef HOST_WIN32
665 static void
666 thread_info_key_dtor (void *arg)
668 /* Put the MonoThreadInfo back for the duration of the
669 * unregister code. In some circumstances the thread needs to
670 * take the GC lock which may block which requires a coop
671 * state transition. */
672 mono_native_tls_set_value (thread_info_key, arg);
673 unregister_thread (arg);
674 mono_native_tls_set_value (thread_info_key, NULL);
676 #endif
678 void
679 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
681 gboolean res;
682 threads_callbacks = *callbacks;
683 thread_info_size = info_size;
684 char *sleepLimit;
685 #ifdef HOST_WIN32
686 res = mono_native_tls_alloc (&thread_info_key, NULL);
687 res = mono_native_tls_alloc (&thread_exited_key, NULL);
688 #else
689 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
690 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
691 #endif
693 g_assert (res);
695 #ifndef HAVE_KW_THREAD
696 res = mono_native_tls_alloc (&small_id_key, NULL);
697 #endif
698 g_assert (res);
700 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
701 errno = 0;
702 long threshold = strtol(sleepLimit, NULL, 10);
703 if ((errno == 0) && (threshold >= 40)) {
704 sleepAbortDuration = threshold;
705 sleepWarnDuration = threshold / 20;
706 } else
707 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
708 g_free (sleepLimit);
711 mono_os_sem_init (&global_suspend_semaphore, 1);
712 mono_os_sem_init (&suspend_semaphore, 0);
713 mono_os_mutex_init (&join_mutex);
715 mono_lls_init (&thread_list, NULL);
716 mono_thread_smr_init ();
717 mono_threads_suspend_init ();
718 mono_threads_coop_init ();
719 mono_threads_platform_init ();
721 #if defined(__MACH__)
722 mono_mach_init (thread_info_key);
723 #endif
725 mono_threads_inited = TRUE;
727 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
730 void
731 mono_threads_signals_init (void)
733 mono_threads_suspend_init_signals ();
736 void
737 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
739 runtime_callbacks = *callbacks;
742 MonoThreadInfoRuntimeCallbacks *
743 mono_threads_get_runtime_callbacks (void)
745 return &runtime_callbacks;
748 static gboolean
749 mono_thread_info_core_resume (MonoThreadInfo *info)
751 gboolean res = FALSE;
753 switch (mono_threads_transition_request_resume (info)) {
754 case ResumeError:
755 res = FALSE;
756 break;
757 case ResumeOk:
758 res = TRUE;
759 break;
760 case ResumeInitSelfResume:
761 resume_self_suspended (info);
762 res = TRUE;
763 break;
764 case ResumeInitAsyncResume:
765 resume_async_suspended (info);
766 res = TRUE;
767 break;
768 case ResumeInitBlockingResume:
769 resume_blocking_suspended (info);
770 res = TRUE;
771 break;
774 return res;
777 gboolean
778 mono_thread_info_resume (MonoNativeThreadId tid)
780 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
781 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
782 MonoThreadInfo *info;
784 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
786 mono_thread_info_suspend_lock ();
788 info = mono_thread_info_lookup (tid); /*info on HP1*/
789 if (!info) {
790 result = FALSE;
791 goto cleanup;
794 result = mono_thread_info_core_resume (info);
796 //Wait for the pending resume to finish
797 mono_threads_wait_pending_operations ();
799 cleanup:
800 mono_thread_info_suspend_unlock ();
801 mono_hazard_pointer_clear (hp, 1);
802 return result;
805 gboolean
806 mono_thread_info_begin_suspend (MonoThreadInfo *info)
808 switch (mono_threads_transition_request_async_suspension (info)) {
809 case AsyncSuspendAlreadySuspended:
810 case AsyncSuspendBlocking:
811 return TRUE;
812 case AsyncSuspendWait:
813 mono_threads_add_to_pending_operation_set (info);
814 return TRUE;
815 case AsyncSuspendInitSuspend:
816 return begin_async_suspend (info, FALSE);
817 default:
818 g_assert_not_reached ();
822 gboolean
823 mono_thread_info_begin_resume (MonoThreadInfo *info)
825 return mono_thread_info_core_resume (info);
829 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
830 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
832 static gboolean
833 is_thread_in_critical_region (MonoThreadInfo *info)
835 MonoMethod *method;
836 MonoJitInfo *ji;
837 gpointer stack_start;
838 MonoThreadUnwindState *state;
840 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
841 return TRUE;
843 /* Are we inside a system critical region? */
844 if (info->inside_critical_region)
845 return TRUE;
847 /* Are we inside a GC critical region? */
848 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
849 return TRUE;
852 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
853 state = mono_thread_info_get_suspend_state (info);
854 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
855 return FALSE;
857 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
858 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
859 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
860 return TRUE;
862 if (threads_callbacks.ip_in_critical_region)
863 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
865 ji = mono_jit_info_table_find (
866 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
867 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
869 if (!ji)
870 return FALSE;
872 method = mono_jit_info_get_method (ji);
874 return threads_callbacks.mono_method_is_critical (method);
877 gboolean
878 mono_thread_info_in_critical_location (MonoThreadInfo *info)
880 return is_thread_in_critical_region (info);
884 The return value is only valid until a matching mono_thread_info_resume is called
886 static MonoThreadInfo*
887 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
889 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
890 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
891 if (!info)
892 return NULL;
894 switch (mono_threads_transition_request_async_suspension (info)) {
895 case AsyncSuspendAlreadySuspended:
896 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
897 return info;
898 case AsyncSuspendWait:
899 mono_threads_add_to_pending_operation_set (info);
900 break;
901 case AsyncSuspendInitSuspend:
902 if (!begin_async_suspend (info, interrupt_kernel)) {
903 mono_hazard_pointer_clear (hp, 1);
904 return NULL;
906 break;
907 case AsyncSuspendBlocking:
908 if (interrupt_kernel)
909 mono_threads_suspend_abort_syscall (info);
911 break;
912 default:
913 g_assert_not_reached ();
916 //Wait for the pending suspend to finish
917 mono_threads_wait_pending_operations ();
919 if (!check_async_suspend (info)) {
920 mono_thread_info_core_resume (info);
921 mono_threads_wait_pending_operations ();
922 mono_hazard_pointer_clear (hp, 1);
923 return NULL;
925 return info;
928 static MonoThreadInfo*
929 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
931 MonoThreadInfo *info = NULL;
932 int sleep_duration = 0;
933 for (;;) {
934 if (!(info = suspend_sync (id, interrupt_kernel))) {
935 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
936 return NULL;
939 /*WARNING: We now are in interrupt context until we resume the thread. */
940 if (!is_thread_in_critical_region (info))
941 break;
943 if (!mono_thread_info_core_resume (info)) {
944 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
945 return NULL;
947 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
949 /* Wait for the pending resume to finish */
950 mono_threads_wait_pending_operations ();
952 if (sleep_duration == 0)
953 mono_thread_info_yield ();
954 else
955 g_usleep (sleep_duration);
957 sleep_duration += 10;
959 return info;
962 void
963 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
965 int result;
966 MonoThreadInfo *info = NULL;
967 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
969 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
970 /*FIXME: unify this with self-suspend*/
971 g_assert (id != mono_native_thread_id_get ());
973 /* This can block during stw */
974 mono_thread_info_suspend_lock ();
975 mono_threads_begin_global_suspend ();
977 info = suspend_sync_nolock (id, interrupt_kernel);
978 if (!info)
979 goto done;
981 switch (result = callback (info, user_data)) {
982 case MonoResumeThread:
983 mono_hazard_pointer_set (hp, 1, info);
984 mono_thread_info_core_resume (info);
985 mono_threads_wait_pending_operations ();
986 break;
987 case KeepSuspended:
988 g_assert (!mono_threads_is_coop_enabled ());
989 break;
990 default:
991 g_error ("Invalid suspend_and_run callback return value %d", result);
994 done:
995 mono_hazard_pointer_clear (hp, 1);
996 mono_threads_end_global_suspend ();
997 mono_thread_info_suspend_unlock ();
1001 Inject an assynchronous call into the target thread. The target thread must be suspended and
1002 only a single async call can be setup for a given suspend cycle.
1003 This async call must cause stack unwinding as the current implementation doesn't save enough state
1004 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1005 currently used only to deliver exceptions.
1007 void
1008 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1010 if (!mono_threads_is_coop_enabled ()) {
1011 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1012 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1013 * region or entering a gc unsafe region */
1014 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1016 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1017 g_assert (!info->async_target);
1018 info->async_target = target_func;
1019 /* This is not GC tracked */
1020 info->user_data = user_data;
1024 The suspend lock is held during any suspend in progress.
1025 A GC that has safepoints must take this lock as part of its
1026 STW to make sure no unsafe pending suspend is in progress.
1029 static void
1030 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1032 if (mono_threads_is_coop_enabled ()) {
1033 g_assert (info);
1034 g_assert (mono_thread_info_is_current (info));
1035 g_assert (mono_thread_info_is_live (info));
1037 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1039 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1040 g_assert (res != -1);
1042 MONO_EXIT_GC_SAFE_WITH_INFO;
1043 } else {
1044 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1045 g_assert (res != -1);
1049 void
1050 mono_thread_info_suspend_lock (void)
1052 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1055 void
1056 mono_thread_info_suspend_unlock (void)
1058 mono_os_sem_post (&global_suspend_semaphore);
1062 * This is a very specific function whose only purpose is to
1063 * break a given thread from socket syscalls.
1065 * This only exists because linux won't fail a call to connect
1066 * if the underlying is closed.
1068 * TODO We should cleanup and unify this with the other syscall abort
1069 * facility.
1071 void
1072 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1074 MonoThreadHazardPointers *hp;
1075 MonoThreadInfo *info;
1077 if (tid == mono_native_thread_id_get ())
1078 return;
1080 hp = mono_hazard_pointer_get ();
1081 info = mono_thread_info_lookup (tid);
1082 if (!info)
1083 return;
1085 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1086 mono_hazard_pointer_clear (hp, 1);
1087 return;
1090 mono_thread_info_suspend_lock ();
1091 mono_threads_begin_global_suspend ();
1093 mono_threads_suspend_abort_syscall (info);
1094 mono_threads_wait_pending_operations ();
1096 mono_hazard_pointer_clear (hp, 1);
1098 mono_threads_end_global_suspend ();
1099 mono_thread_info_suspend_unlock ();
1103 * mono_thread_info_set_is_async_context:
1105 * Set whenever the current thread is in an async context. Some runtime functions might behave
1106 * differently while in an async context in order to be async safe.
1108 void
1109 mono_thread_info_set_is_async_context (gboolean async_context)
1111 MonoThreadInfo *info = mono_thread_info_current ();
1113 if (info)
1114 info->is_async_context = async_context;
1117 gboolean
1118 mono_thread_info_is_async_context (void)
1120 MonoThreadInfo *info = mono_thread_info_current ();
1122 if (info)
1123 return info->is_async_context;
1124 else
1125 return FALSE;
1129 * mono_thread_info_get_stack_bounds:
1131 * Return the address and size of the current threads stack. Return NULL as the
1132 * stack address if the stack address cannot be determined.
1134 void
1135 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1137 guint8 *current = (guint8 *)&stsize;
1138 mono_threads_platform_get_stack_bounds (staddr, stsize);
1139 if (!*staddr)
1140 return;
1142 /* Sanity check the result */
1143 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1145 /* When running under emacs, sometimes staddr is not aligned to a page size */
1146 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1149 gboolean
1150 mono_thread_info_yield (void)
1152 return mono_threads_platform_yield ();
1155 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1156 static MonoCoopMutex sleep_mutex;
1157 static MonoCoopCond sleep_cond;
1159 static void
1160 sleep_initialize (void)
1162 mono_coop_mutex_init (&sleep_mutex);
1163 mono_coop_cond_init (&sleep_cond);
1166 static void
1167 sleep_interrupt (gpointer data)
1169 mono_coop_mutex_lock (&sleep_mutex);
1170 mono_coop_cond_broadcast (&sleep_cond);
1171 mono_coop_mutex_unlock (&sleep_mutex);
1174 static inline guint32
1175 sleep_interruptable (guint32 ms, gboolean *alerted)
1177 gint64 now, end;
1179 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1181 g_assert (alerted);
1182 *alerted = FALSE;
1184 if (ms != MONO_INFINITE_WAIT)
1185 end = mono_msec_ticks() + ms;
1187 mono_lazy_initialize (&sleep_init, sleep_initialize);
1189 mono_coop_mutex_lock (&sleep_mutex);
1191 for (;;) {
1192 if (ms != MONO_INFINITE_WAIT) {
1193 now = mono_msec_ticks();
1194 if (now >= end)
1195 break;
1198 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1199 if (*alerted) {
1200 mono_coop_mutex_unlock (&sleep_mutex);
1201 return WAIT_IO_COMPLETION;
1204 if (ms != MONO_INFINITE_WAIT)
1205 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1206 else
1207 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1209 mono_thread_info_uninstall_interrupt (alerted);
1210 if (*alerted) {
1211 mono_coop_mutex_unlock (&sleep_mutex);
1212 return WAIT_IO_COMPLETION;
1216 mono_coop_mutex_unlock (&sleep_mutex);
1218 return 0;
1221 gint
1222 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1224 if (ms == 0) {
1225 MonoThreadInfo *info;
1227 mono_thread_info_yield ();
1229 info = mono_thread_info_current ();
1230 if (info && mono_thread_info_is_interrupt_state (info))
1231 return WAIT_IO_COMPLETION;
1233 return 0;
1236 if (alerted)
1237 return sleep_interruptable (ms, alerted);
1239 MONO_ENTER_GC_SAFE;
1241 if (ms == MONO_INFINITE_WAIT) {
1242 do {
1243 #ifdef HOST_WIN32
1244 Sleep (G_MAXUINT32);
1245 #else
1246 sleep (G_MAXUINT32);
1247 #endif
1248 } while (1);
1249 } else {
1250 int ret;
1251 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1252 struct timespec start, target;
1254 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1255 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1256 g_assert (ret == 0);
1258 target = start;
1259 target.tv_sec += ms / 1000;
1260 target.tv_nsec += (ms % 1000) * 1000000;
1261 if (target.tv_nsec > 999999999) {
1262 target.tv_nsec -= 999999999;
1263 target.tv_sec ++;
1266 do {
1267 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1268 } while (ret != 0);
1269 #elif HOST_WIN32
1270 Sleep (ms);
1271 #else
1272 struct timespec req, rem;
1274 req.tv_sec = ms / 1000;
1275 req.tv_nsec = (ms % 1000) * 1000000;
1277 do {
1278 memset (&rem, 0, sizeof (rem));
1279 ret = nanosleep (&req, &rem);
1280 } while (ret != 0);
1281 #endif /* __linux__ */
1284 MONO_EXIT_GC_SAFE;
1286 return 0;
1289 gint
1290 mono_thread_info_usleep (guint64 us)
1292 MONO_ENTER_GC_SAFE;
1293 g_usleep (us);
1294 MONO_EXIT_GC_SAFE;
1295 return 0;
1298 gpointer
1299 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1301 return ((MonoThreadInfo*)info)->tls [key];
1305 * mono_threads_info_tls_set:
1307 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1308 * values of TLS variables for threads other than the current thread.
1309 * This should only be used for infrequently changing TLS variables, and it should
1310 * be paired with setting the real TLS variable since this provides no GC tracking.
1312 void
1313 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1315 ((MonoThreadInfo*)info)->tls [key] = value;
1319 * mono_thread_info_exit:
1321 * Exit the current thread.
1322 * This function doesn't return.
1324 void
1325 mono_thread_info_exit (gsize exit_code)
1327 mono_thread_info_detach ();
1329 mono_threads_platform_exit (0);
1333 * mono_threads_open_thread_handle:
1335 * Duplicate the handle. The handle needs to be closed by calling
1336 * mono_threads_close_thread_handle () when it is no longer needed.
1338 MonoThreadHandle*
1339 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1341 return mono_refcount_inc (thread_handle);
1344 void
1345 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1347 mono_refcount_dec (thread_handle);
1350 static void
1351 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1353 mono_os_event_set (&thread_handle->event);
1356 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1358 struct _MonoThreadInfoInterruptToken {
1359 void (*callback) (gpointer data);
1360 gpointer data;
1364 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1366 * - @callback: must be able to be called from another thread and always cancel the wait
1367 * - @data: passed to the callback
1368 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1369 * if set to TRUE, it must mean that the thread is in interrupted state
1371 void
1372 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1374 MonoThreadInfo *info;
1375 MonoThreadInfoInterruptToken *previous_token, *token;
1377 g_assert (callback);
1379 g_assert (interrupted);
1380 *interrupted = FALSE;
1382 info = mono_thread_info_current ();
1383 g_assert (info);
1385 /* The memory of this token can be freed at 2 places:
1386 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1387 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1388 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1389 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1390 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1391 token->callback = callback;
1392 token->data = data;
1394 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1396 if (previous_token) {
1397 if (previous_token != INTERRUPT_STATE)
1398 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1400 g_free (token);
1402 *interrupted = TRUE;
1405 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1406 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1409 void
1410 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1412 MonoThreadInfo *info;
1413 MonoThreadInfoInterruptToken *previous_token;
1415 g_assert (interrupted);
1416 *interrupted = FALSE;
1418 info = mono_thread_info_current ();
1419 g_assert (info);
1421 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1423 /* only the installer can uninstall the token */
1424 g_assert (previous_token);
1426 if (previous_token == INTERRUPT_STATE) {
1427 /* if it is interrupted, then it is going to be freed in finish interrupt */
1428 *interrupted = TRUE;
1429 } else {
1430 g_free (previous_token);
1433 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1434 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1437 static MonoThreadInfoInterruptToken*
1438 set_interrupt_state (MonoThreadInfo *info)
1440 MonoThreadInfoInterruptToken *token, *previous_token;
1442 g_assert (info);
1444 /* Atomically obtain the token the thread is
1445 * waiting on, and change it to a flag value. */
1447 do {
1448 previous_token = info->interrupt_token;
1450 /* Already interrupted */
1451 if (previous_token == INTERRUPT_STATE) {
1452 token = NULL;
1453 break;
1456 token = previous_token;
1457 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1459 return token;
1463 * mono_thread_info_prepare_interrupt:
1465 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1466 * - if the thread calls one of the WaitFor functions, the function will return with
1467 * WAIT_IO_COMPLETION instead of waiting
1468 * - if the thread was waiting when this function was called, the wait will be broken
1470 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1471 * didn't receive the interrupt signal yet, in this case it should call the wait function
1472 * again. This essentially means that the target thread will busy wait until it is ready to
1473 * process the interruption.
1475 MonoThreadInfoInterruptToken*
1476 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1478 MonoThreadInfoInterruptToken *token;
1480 token = set_interrupt_state (info);
1482 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1483 mono_thread_info_get_tid (info), token);
1485 return token;
1488 void
1489 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1491 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1493 if (token == NULL)
1494 return;
1496 g_assert (token->callback);
1498 token->callback (token->data);
1500 g_free (token);
1503 void
1504 mono_thread_info_self_interrupt (void)
1506 MonoThreadInfo *info;
1507 MonoThreadInfoInterruptToken *token;
1509 info = mono_thread_info_current ();
1510 g_assert (info);
1512 token = set_interrupt_state (info);
1513 g_assert (!token);
1515 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1516 mono_thread_info_get_tid (info));
1519 /* Clear the interrupted flag of the current thread, set with
1520 * mono_thread_info_self_interrupt, so it can wait again */
1521 void
1522 mono_thread_info_clear_self_interrupt ()
1524 MonoThreadInfo *info;
1525 MonoThreadInfoInterruptToken *previous_token;
1527 info = mono_thread_info_current ();
1528 g_assert (info);
1530 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1531 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1533 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1536 gboolean
1537 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1539 g_assert (info);
1540 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1543 void
1544 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1546 g_assert (info);
1548 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1549 g_string_append_printf (text, "not waiting");
1550 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1551 g_string_append_printf (text, "interrupted state");
1552 else
1553 g_string_append_printf (text, "waiting");
1556 gboolean
1557 mono_thread_info_is_current (MonoThreadInfo *info)
1559 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1562 MonoThreadInfoWaitRet
1563 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1565 MonoOSEventWaitRet res;
1567 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1568 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1569 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1570 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1571 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1572 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1573 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1574 else
1575 g_error ("%s: unknown res value %d", __func__, res);
1578 MonoThreadInfoWaitRet
1579 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1581 MonoOSEventWaitRet res;
1582 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1583 gint i;
1585 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1586 if (background_change_event)
1587 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1589 for (i = 0; i < nhandles; ++i)
1590 thread_events [i] = &thread_handles [i]->event;
1592 if (background_change_event)
1593 thread_events [nhandles ++] = background_change_event;
1595 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1596 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1597 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1598 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1599 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1600 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1601 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1602 else
1603 g_error ("%s: unknown res value %d", __func__, res);
1607 * mono_threads_join_mutex:
1609 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1610 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1611 * The code inside the lock should not block.
1613 void
1614 mono_threads_join_lock (void)
1616 #ifdef TARGET_OSX
1617 mono_os_mutex_lock (&join_mutex);
1618 #endif
1621 void
1622 mono_threads_join_unlock (void)
1624 #ifdef TARGET_OSX
1625 mono_os_mutex_unlock (&join_mutex);
1626 #endif