[threads] Fix conflicting signal with boehm (#4096)
[mono-project.git] / mono / utils / mono-threads.c
blobe344ec1ed1f11728d88fe90db446891adaaae43a
1 /*
2 * mono-threads.c: Low-level threading
4 * Author:
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
14 /* enable pthread extensions */
15 #ifdef TARGET_MACH
16 #define _DARWIN_C_SOURCE
17 #endif
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-os-semaphore.h>
21 #include <mono/utils/mono-threads.h>
22 #include <mono/utils/mono-tls.h>
23 #include <mono/utils/hazard-pointer.h>
24 #include <mono/utils/mono-memory-model.h>
25 #include <mono/utils/mono-mmap.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-time.h>
28 #include <mono/utils/mono-lazy-init.h>
29 #include <mono/utils/mono-coop-mutex.h>
30 #include <mono/utils/mono-coop-semaphore.h>
31 #include <mono/utils/mono-threads-coop.h>
32 #include <mono/utils/mono-threads-debug.h>
33 #include <mono/utils/os-event.h>
35 #include <mono/io-layer/io-layer.h>
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 MONO_TLS_FAST;
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 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
81 /*warn at 50 ms*/
82 #define SLEEP_DURATION_BEFORE_WARNING (50)
83 /*never aborts */
84 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
86 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
87 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
89 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
91 void
92 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
94 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
95 InterlockedIncrement (&abort_posts);
96 mono_os_sem_post (&suspend_semaphore);
99 void
100 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
103 InterlockedIncrement (&suspend_posts);
104 mono_os_sem_post (&suspend_semaphore);
107 void
108 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
110 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
111 InterlockedIncrement (&resume_posts);
112 mono_os_sem_post (&suspend_semaphore);
115 static gboolean
116 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
118 if (mono_threads_is_coop_enabled ()) {
119 /* There's nothing else to do after we async request the thread to suspend */
120 mono_threads_add_to_pending_operation_set (info);
121 return TRUE;
124 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
127 static gboolean
128 check_async_suspend (MonoThreadInfo *info)
130 if (mono_threads_is_coop_enabled ()) {
131 /* Async suspend can't async fail on coop */
132 return TRUE;
135 return mono_threads_suspend_check_suspend_result (info);
138 static void
139 resume_async_suspended (MonoThreadInfo *info)
141 if (mono_threads_is_coop_enabled ())
142 g_assert_not_reached ();
144 g_assert (mono_threads_suspend_begin_async_resume (info));
147 static void
148 resume_self_suspended (MonoThreadInfo* info)
150 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
151 mono_os_sem_post (&info->resume_semaphore);
154 void
155 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
157 int res;
158 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
159 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
160 g_assert (res != -1);
163 static void
164 resume_blocking_suspended (MonoThreadInfo* info)
166 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
167 mono_os_sem_post (&info->resume_semaphore);
170 void
171 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
173 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
174 ++pending_suspends;
175 InterlockedIncrement (&pending_ops);
178 void
179 mono_threads_begin_global_suspend (void)
181 size_t ps = pending_suspends;
182 if (G_UNLIKELY (ps != 0))
183 g_error ("pending_suspends = %d, but must be 0", ps);
184 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
185 abort_posts, waits_done, pending_ops);
186 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
187 mono_threads_coop_begin_global_suspend ();
190 void
191 mono_threads_end_global_suspend (void)
193 size_t ps = pending_suspends;
194 if (G_UNLIKELY (ps != 0))
195 g_error ("pending_suspends = %d, but must be 0", ps);
196 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
197 abort_posts, waits_done, pending_ops);
198 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
199 mono_threads_coop_end_global_suspend ();
202 static void
203 dump_threads (void)
205 MonoThreadInfo *cur = mono_thread_info_current ();
207 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
218 FOREACH_THREAD_SAFE (info) {
219 #ifdef TARGET_MACH
220 char thread_name [256] = { 0 };
221 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
223 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
224 #else
225 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
226 #endif
227 } FOREACH_THREAD_SAFE_END
230 gboolean
231 mono_threads_wait_pending_operations (void)
233 int i;
234 int c = pending_suspends;
236 /* Wait threads to park */
237 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
238 if (pending_suspends) {
239 MonoStopwatch suspension_time;
240 mono_stopwatch_start (&suspension_time);
241 for (i = 0; i < pending_suspends; ++i) {
242 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
243 InterlockedIncrement (&waits_done);
244 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
245 continue;
246 mono_stopwatch_stop (&suspension_time);
248 dump_threads ();
250 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
251 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
253 mono_stopwatch_stop (&suspension_time);
254 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
258 pending_suspends = 0;
260 return c > 0;
264 //Thread initialization code
266 static inline void
267 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
269 if (retain != 0)
270 mono_hazard_pointer_clear (hp, 0);
271 if (retain != 1)
272 mono_hazard_pointer_clear (hp, 1);
273 if (retain != 2)
274 mono_hazard_pointer_clear (hp, 2);
278 If return non null Hazard Pointer 1 holds the return value.
280 MonoThreadInfo*
281 mono_thread_info_lookup (MonoNativeThreadId id)
283 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
285 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
286 mono_hazard_pointer_clear_all (hp, -1);
287 return NULL;
290 mono_hazard_pointer_clear_all (hp, 1);
291 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
294 static gboolean
295 mono_thread_info_insert (MonoThreadInfo *info)
297 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
299 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
300 mono_hazard_pointer_clear_all (hp, -1);
301 return FALSE;
304 mono_hazard_pointer_clear_all (hp, -1);
305 return TRUE;
308 static gboolean
309 mono_thread_info_remove (MonoThreadInfo *info)
311 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
312 gboolean res;
314 THREADS_DEBUG ("removing info %p\n", info);
315 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
316 mono_hazard_pointer_clear_all (hp, -1);
317 return res;
320 static void
321 free_thread_info (gpointer mem)
323 MonoThreadInfo *info = (MonoThreadInfo *) mem;
325 mono_os_sem_destroy (&info->resume_semaphore);
326 mono_threads_suspend_free (info);
328 g_free (info);
332 mono_thread_info_register_small_id (void)
334 int small_id = mono_thread_small_id_alloc ();
335 #ifdef HAVE_KW_THREAD
336 tls_small_id = small_id;
337 #else
338 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
339 #endif
340 return small_id;
343 static void
344 thread_handle_destroy (gpointer data)
346 MonoThreadHandle *thread_handle;
348 thread_handle = (MonoThreadHandle*) data;
350 mono_os_event_destroy (&thread_handle->event);
351 g_free (thread_handle);
354 static void*
355 register_thread (MonoThreadInfo *info, gpointer baseptr)
357 size_t stsize = 0;
358 guint8 *staddr = NULL;
359 int small_id = mono_thread_info_register_small_id ();
360 gboolean result;
361 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
362 info->small_id = small_id;
364 info->handle = g_new0 (MonoThreadHandle, 1);
365 mono_refcount_init (info->handle, thread_handle_destroy);
366 mono_os_event_init (&info->handle->event, FALSE);
368 mono_os_sem_init (&info->resume_semaphore, 0);
370 /*set TLS early so SMR works */
371 mono_native_tls_set_value (thread_info_key, info);
373 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
375 if (threads_callbacks.thread_register) {
376 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
377 // g_warning ("thread registation failed\n");
378 mono_native_tls_set_value (thread_info_key, NULL);
379 g_free (info);
380 return NULL;
384 mono_thread_info_get_stack_bounds (&staddr, &stsize);
385 g_assert (staddr);
386 g_assert (stsize);
387 info->stack_start_limit = staddr;
388 info->stack_end = staddr + stsize;
390 info->stackdata = g_byte_array_new ();
392 mono_threads_suspend_register (info);
395 Transition it before taking any locks or publishing itself to reduce the chance
396 of others witnessing a detached thread.
397 We can reasonably expect that until this thread gets published, no other thread will
398 try to manipulate it.
400 mono_threads_transition_attach (info);
401 mono_thread_info_suspend_lock ();
402 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
403 result = mono_thread_info_insert (info);
404 g_assert (result);
405 mono_thread_info_suspend_unlock ();
406 return info;
409 static void
410 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
412 static void
413 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
415 static void
416 unregister_thread (void *arg)
418 gpointer gc_unsafe_stackdata;
419 MonoThreadInfo *info;
420 int small_id;
421 gboolean result;
422 gpointer handle;
424 info = (MonoThreadInfo *) arg;
425 g_assert (info);
426 g_assert (mono_thread_info_is_current (info));
427 g_assert (mono_thread_info_is_live (info));
429 small_id = info->small_id;
431 /* We only enter the GC unsafe region, as when exiting this function, the thread
432 * will be detached, and the current MonoThreadInfo* will be destroyed. */
433 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
435 THREADS_DEBUG ("unregistering info %p\n", info);
437 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
440 * TLS destruction order is not reliable so small_id might be cleaned up
441 * before us.
443 #ifndef HAVE_KW_THREAD
444 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
445 #endif
447 /* we need to duplicate it, as the info->handle is going
448 * to be closed when unregistering from the platform */
449 handle = mono_threads_open_thread_handle (info->handle);
452 First perform the callback that requires no locks.
453 This callback has the potential of taking other locks, so we do it before.
454 After it completes, the thread remains functional.
456 if (threads_callbacks.thread_detach)
457 threads_callbacks.thread_detach (info);
459 mono_thread_info_suspend_lock_with_info (info);
462 Now perform the callback that must be done under locks.
463 This will render the thread useless and non-suspendable, so it must
464 be done while holding the suspend lock to give no other thread chance
465 to suspend it.
467 if (threads_callbacks.thread_unregister)
468 threads_callbacks.thread_unregister (info);
470 /* The thread is no longer active, so unref its handle */
471 mono_threads_close_thread_handle (info->handle);
472 info->handle = NULL;
474 result = mono_thread_info_remove (info);
475 g_assert (result);
476 mono_threads_transition_detach (info);
478 mono_thread_info_suspend_unlock ();
480 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
482 /*now it's safe to free the thread info.*/
483 mono_thread_hazardous_try_free (info, free_thread_info);
484 /* Pump the HP queue */
485 mono_thread_hazardous_try_free_some ();
487 mono_thread_small_id_free (small_id);
489 mono_threads_signal_thread_handle (handle);
491 mono_threads_close_thread_handle (handle);
494 static void
495 thread_exited_dtor (void *arg)
497 #if defined(__MACH__)
499 * Since we use pthread dtors to clean up thread data, if a thread
500 * is attached to the runtime by another pthread dtor after our dtor
501 * has ran, it will never be detached, leading to various problems
502 * since the thread ids etc. will be reused while they are still in
503 * the threads hashtables etc.
504 * Dtors are called in a loop until all user tls entries are 0,
505 * but the loop has a maximum count (4), so if we set the tls
506 * variable every time, it will remain set when system tls dtors
507 * are ran. This allows mono_thread_info_is_exiting () to detect
508 * whenever the thread is exiting, even if it is executed from a
509 * system tls dtor (i.e. obj-c dealloc methods).
511 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
512 #endif
515 MonoThreadInfo*
516 mono_thread_info_current_unchecked (void)
518 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
522 MonoThreadInfo*
523 mono_thread_info_current (void)
525 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
526 if (info)
527 return info;
529 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
532 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
533 The way to distinguish between before, during and after cleanup is the following:
535 -If the TLS key is set, cleanup has not begun;
536 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
537 -If the thread is nowhere to be found, cleanup has finished.
539 We cannot function after cleanup since there's no way to ensure what will happen.
541 g_assert (info);
543 /*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 */
544 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
546 return info;
550 mono_thread_info_get_small_id (void)
552 #ifdef HAVE_KW_THREAD
553 return tls_small_id;
554 #else
555 gpointer val = mono_native_tls_get_value (small_id_key);
556 if (!val)
557 return -1;
558 return GPOINTER_TO_INT (val) - 1;
559 #endif
562 MonoLinkedListSet*
563 mono_thread_info_list_head (void)
565 return &thread_list;
569 * mono_threads_attach_tools_thread
571 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
573 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
574 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
575 * the managed heap.
577 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
578 * doing things like resolving backtraces in their background processing thread.
580 void
581 mono_threads_attach_tools_thread (void)
583 int dummy = 0;
584 MonoThreadInfo *info;
586 /* Must only be called once */
587 g_assert (!mono_native_tls_get_value (thread_info_key));
589 while (!mono_threads_inited) {
590 mono_thread_info_usleep (10);
593 info = mono_thread_info_attach (&dummy);
594 g_assert (info);
596 info->tools_thread = TRUE;
599 MonoThreadInfo*
600 mono_thread_info_attach (void *baseptr)
602 MonoThreadInfo *info;
603 if (!mono_threads_inited)
605 #ifdef HOST_WIN32
606 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
607 * thread is created before an embedding API user initialized Mono. */
608 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
609 return NULL;
610 #else
611 g_assert (mono_threads_inited);
612 #endif
614 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
615 if (!info) {
616 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
617 THREADS_DEBUG ("attaching %p\n", info);
618 if (!register_thread (info, baseptr))
619 return NULL;
620 } else if (threads_callbacks.thread_attach) {
621 threads_callbacks.thread_attach (info);
623 return info;
626 void
627 mono_thread_info_detach (void)
629 MonoThreadInfo *info;
630 if (!mono_threads_inited)
632 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
633 * is created before an embedding API user initialized Mono. */
634 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
635 return;
637 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
638 if (info) {
639 THREADS_DEBUG ("detaching %p\n", info);
640 unregister_thread (info);
641 mono_native_tls_set_value (thread_info_key, NULL);
646 * mono_thread_info_is_exiting:
648 * Return whenever the current thread is exiting, i.e. it is running pthread
649 * dtors.
651 gboolean
652 mono_thread_info_is_exiting (void)
654 #if defined(__MACH__)
655 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
656 return TRUE;
657 #endif
658 return FALSE;
661 #ifndef HOST_WIN32
662 static void
663 thread_info_key_dtor (void *arg)
665 /* Put the MonoThreadInfo back for the duration of the
666 * unregister code. In some circumstances the thread needs to
667 * take the GC lock which may block which requires a coop
668 * state transition. */
669 mono_native_tls_set_value (thread_info_key, arg);
670 unregister_thread (arg);
671 mono_native_tls_set_value (thread_info_key, NULL);
673 #endif
675 void
676 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
678 gboolean res;
679 threads_callbacks = *callbacks;
680 thread_info_size = info_size;
681 const char *sleepLimit;
682 #ifdef HOST_WIN32
683 res = mono_native_tls_alloc (&thread_info_key, NULL);
684 res = mono_native_tls_alloc (&thread_exited_key, NULL);
685 #else
686 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
687 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
688 #endif
690 g_assert (res);
692 #ifndef HAVE_KW_THREAD
693 res = mono_native_tls_alloc (&small_id_key, NULL);
694 #endif
695 g_assert (res);
697 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
698 errno = 0;
699 long threshold = strtol(sleepLimit, NULL, 10);
700 if ((errno == 0) && (threshold >= 40)) {
701 sleepAbortDuration = threshold;
702 sleepWarnDuration = threshold / 20;
703 } else
704 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
707 mono_os_sem_init (&global_suspend_semaphore, 1);
708 mono_os_sem_init (&suspend_semaphore, 0);
710 mono_lls_init (&thread_list, NULL);
711 mono_thread_smr_init ();
712 mono_threads_suspend_init ();
713 mono_threads_coop_init ();
715 #if defined(__MACH__)
716 mono_mach_init (thread_info_key);
717 #endif
719 mono_threads_inited = TRUE;
721 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
724 void
725 mono_threads_signals_init (void)
727 mono_threads_suspend_init_signals ();
730 void
731 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
733 runtime_callbacks = *callbacks;
736 MonoThreadInfoRuntimeCallbacks *
737 mono_threads_get_runtime_callbacks (void)
739 return &runtime_callbacks;
742 static gboolean
743 mono_thread_info_core_resume (MonoThreadInfo *info)
745 gboolean res = FALSE;
747 switch (mono_threads_transition_request_resume (info)) {
748 case ResumeError:
749 res = FALSE;
750 break;
751 case ResumeOk:
752 res = TRUE;
753 break;
754 case ResumeInitSelfResume:
755 resume_self_suspended (info);
756 res = TRUE;
757 break;
758 case ResumeInitAsyncResume:
759 resume_async_suspended (info);
760 res = TRUE;
761 break;
762 case ResumeInitBlockingResume:
763 resume_blocking_suspended (info);
764 res = TRUE;
765 break;
768 return res;
771 gboolean
772 mono_thread_info_resume (MonoNativeThreadId tid)
774 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
775 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
776 MonoThreadInfo *info;
778 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
780 mono_thread_info_suspend_lock ();
782 info = mono_thread_info_lookup (tid); /*info on HP1*/
783 if (!info) {
784 result = FALSE;
785 goto cleanup;
788 result = mono_thread_info_core_resume (info);
790 //Wait for the pending resume to finish
791 mono_threads_wait_pending_operations ();
793 cleanup:
794 mono_thread_info_suspend_unlock ();
795 mono_hazard_pointer_clear (hp, 1);
796 return result;
799 gboolean
800 mono_thread_info_begin_suspend (MonoThreadInfo *info)
802 switch (mono_threads_transition_request_async_suspension (info)) {
803 case AsyncSuspendAlreadySuspended:
804 case AsyncSuspendBlocking:
805 return TRUE;
806 case AsyncSuspendWait:
807 mono_threads_add_to_pending_operation_set (info);
808 return TRUE;
809 case AsyncSuspendInitSuspend:
810 return begin_async_suspend (info, FALSE);
811 default:
812 g_assert_not_reached ();
816 gboolean
817 mono_thread_info_begin_resume (MonoThreadInfo *info)
819 return mono_thread_info_core_resume (info);
823 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
824 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
826 static gboolean
827 is_thread_in_critical_region (MonoThreadInfo *info)
829 MonoMethod *method;
830 MonoJitInfo *ji;
831 gpointer stack_start;
832 MonoThreadUnwindState *state;
834 /* Are we inside a system critical region? */
835 if (info->inside_critical_region)
836 return TRUE;
838 /* Are we inside a GC critical region? */
839 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
840 return TRUE;
843 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
844 state = mono_thread_info_get_suspend_state (info);
845 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
846 return FALSE;
848 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
849 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
850 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
851 return TRUE;
853 if (threads_callbacks.ip_in_critical_region)
854 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
856 ji = mono_jit_info_table_find (
857 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
858 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
860 if (!ji)
861 return FALSE;
863 method = mono_jit_info_get_method (ji);
865 return threads_callbacks.mono_method_is_critical (method);
868 gboolean
869 mono_thread_info_in_critical_location (MonoThreadInfo *info)
871 return is_thread_in_critical_region (info);
875 The return value is only valid until a matching mono_thread_info_resume is called
877 static MonoThreadInfo*
878 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
880 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
881 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
882 if (!info)
883 return NULL;
885 switch (mono_threads_transition_request_async_suspension (info)) {
886 case AsyncSuspendAlreadySuspended:
887 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
888 return info;
889 case AsyncSuspendWait:
890 mono_threads_add_to_pending_operation_set (info);
891 break;
892 case AsyncSuspendInitSuspend:
893 if (!begin_async_suspend (info, interrupt_kernel)) {
894 mono_hazard_pointer_clear (hp, 1);
895 return NULL;
897 break;
898 case AsyncSuspendBlocking:
899 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
900 mono_threads_suspend_abort_syscall (info);
902 break;
903 default:
904 g_assert_not_reached ();
907 //Wait for the pending suspend to finish
908 mono_threads_wait_pending_operations ();
910 if (!check_async_suspend (info)) {
911 mono_thread_info_core_resume (info);
912 mono_threads_wait_pending_operations ();
913 mono_hazard_pointer_clear (hp, 1);
914 return NULL;
916 return info;
919 static MonoThreadInfo*
920 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
922 MonoThreadInfo *info = NULL;
923 int sleep_duration = 0;
924 for (;;) {
925 if (!(info = suspend_sync (id, interrupt_kernel))) {
926 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
927 return NULL;
930 /*WARNING: We now are in interrupt context until we resume the thread. */
931 if (!is_thread_in_critical_region (info))
932 break;
934 if (!mono_thread_info_core_resume (info)) {
935 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
936 return NULL;
938 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
940 /* Wait for the pending resume to finish */
941 mono_threads_wait_pending_operations ();
943 if (sleep_duration == 0)
944 mono_thread_info_yield ();
945 else
946 g_usleep (sleep_duration);
948 sleep_duration += 10;
950 return info;
953 void
954 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
956 int result;
957 MonoThreadInfo *info = NULL;
958 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
960 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
961 /*FIXME: unify this with self-suspend*/
962 g_assert (id != mono_native_thread_id_get ());
964 /* This can block during stw */
965 mono_thread_info_suspend_lock ();
966 mono_threads_begin_global_suspend ();
968 info = suspend_sync_nolock (id, interrupt_kernel);
969 if (!info)
970 goto done;
972 switch (result = callback (info, user_data)) {
973 case MonoResumeThread:
974 mono_hazard_pointer_set (hp, 1, info);
975 mono_thread_info_core_resume (info);
976 mono_threads_wait_pending_operations ();
977 break;
978 case KeepSuspended:
979 g_assert (!mono_threads_is_coop_enabled ());
980 break;
981 default:
982 g_error ("Invalid suspend_and_run callback return value %d", result);
985 done:
986 mono_hazard_pointer_clear (hp, 1);
987 mono_threads_end_global_suspend ();
988 mono_thread_info_suspend_unlock ();
992 Inject an assynchronous call into the target thread. The target thread must be suspended and
993 only a single async call can be setup for a given suspend cycle.
994 This async call must cause stack unwinding as the current implementation doesn't save enough state
995 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
996 currently used only to deliver exceptions.
998 void
999 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1001 /* An async call can only be setup on an async suspended thread */
1002 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1003 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1004 g_assert (!info->async_target);
1005 info->async_target = target_func;
1006 /* This is not GC tracked */
1007 info->user_data = user_data;
1011 The suspend lock is held during any suspend in progress.
1012 A GC that has safepoints must take this lock as part of its
1013 STW to make sure no unsafe pending suspend is in progress.
1016 static void
1017 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1019 g_assert (info);
1020 g_assert (mono_thread_info_is_current (info));
1021 g_assert (mono_thread_info_is_live (info));
1023 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1025 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1026 g_assert (res != -1);
1028 MONO_EXIT_GC_SAFE_WITH_INFO;
1031 void
1032 mono_thread_info_suspend_lock (void)
1034 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1037 void
1038 mono_thread_info_suspend_unlock (void)
1040 mono_os_sem_post (&global_suspend_semaphore);
1044 * This is a very specific function whose only purpose is to
1045 * break a given thread from socket syscalls.
1047 * This only exists because linux won't fail a call to connect
1048 * if the underlying is closed.
1050 * TODO We should cleanup and unify this with the other syscall abort
1051 * facility.
1053 void
1054 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1056 MonoThreadHazardPointers *hp;
1057 MonoThreadInfo *info;
1059 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1060 return;
1062 hp = mono_hazard_pointer_get ();
1063 info = mono_thread_info_lookup (tid);
1064 if (!info)
1065 return;
1067 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1068 mono_hazard_pointer_clear (hp, 1);
1069 return;
1072 mono_thread_info_suspend_lock ();
1073 mono_threads_begin_global_suspend ();
1075 mono_threads_suspend_abort_syscall (info);
1076 mono_threads_wait_pending_operations ();
1078 mono_hazard_pointer_clear (hp, 1);
1080 mono_threads_end_global_suspend ();
1081 mono_thread_info_suspend_unlock ();
1085 * mono_thread_info_set_is_async_context:
1087 * Set whenever the current thread is in an async context. Some runtime functions might behave
1088 * differently while in an async context in order to be async safe.
1090 void
1091 mono_thread_info_set_is_async_context (gboolean async_context)
1093 MonoThreadInfo *info = mono_thread_info_current ();
1095 if (info)
1096 info->is_async_context = async_context;
1099 gboolean
1100 mono_thread_info_is_async_context (void)
1102 MonoThreadInfo *info = mono_thread_info_current ();
1104 if (info)
1105 return info->is_async_context;
1106 else
1107 return FALSE;
1110 typedef struct {
1111 MonoRefCount ref;
1112 MonoThreadStart start_routine;
1113 gpointer start_routine_arg;
1114 MonoCoopSem registered;
1115 MonoThreadHandle *handle;
1116 } CreateThreadData;
1118 static void
1119 create_thread_data_destroy (gpointer data)
1121 CreateThreadData *thread_data;
1123 thread_data = (CreateThreadData*) data;
1125 mono_coop_sem_destroy (&thread_data->registered);
1126 g_free (thread_data);
1129 static gsize WINAPI
1130 inner_start_thread (gpointer data)
1132 CreateThreadData *thread_data;
1133 MonoThreadInfo *info;
1134 MonoThreadStart start_routine;
1135 gpointer start_routine_arg;
1136 gsize start_routine_res;
1137 gsize dummy;
1139 thread_data = (CreateThreadData*) data;
1140 g_assert (thread_data);
1142 start_routine = thread_data->start_routine;
1143 start_routine_arg = thread_data->start_routine_arg;
1145 info = mono_thread_info_attach (&dummy);
1146 info->runtime_thread = TRUE;
1148 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1150 mono_coop_sem_post (&thread_data->registered);
1152 mono_refcount_dec (thread_data);
1154 /* thread_data is not valid anymore */
1155 thread_data = NULL;
1157 /* Run the actual main function of the thread */
1158 start_routine_res = start_routine (start_routine_arg);
1160 mono_thread_info_exit (start_routine_res);
1162 g_assert_not_reached ();
1166 * mono_threads_create_thread:
1168 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1169 * Returns: a windows or io-layer handle for the thread.
1171 MonoThreadHandle*
1172 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1174 CreateThreadData *thread_data;
1175 gint res;
1176 MonoThreadHandle *ret;
1178 thread_data = g_new0 (CreateThreadData, 1);
1179 mono_refcount_init (thread_data, create_thread_data_destroy);
1180 thread_data->start_routine = start;
1181 thread_data->start_routine_arg = arg;
1182 mono_coop_sem_init (&thread_data->registered, 0);
1184 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
1185 if (res != 0) {
1186 /* ref is not going to be decremented in inner_start_thread */
1187 mono_refcount_dec (thread_data);
1188 ret = NULL;
1189 goto done;
1192 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1193 g_assert (res == 0);
1195 ret = thread_data->handle;
1196 g_assert (ret);
1198 done:
1199 mono_refcount_dec (thread_data);
1201 return ret;
1205 * mono_thread_info_get_stack_bounds:
1207 * Return the address and size of the current threads stack. Return NULL as the
1208 * stack address if the stack address cannot be determined.
1210 void
1211 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1213 guint8 *current = (guint8 *)&stsize;
1214 mono_threads_platform_get_stack_bounds (staddr, stsize);
1215 if (!*staddr)
1216 return;
1218 /* Sanity check the result */
1219 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1221 /* When running under emacs, sometimes staddr is not aligned to a page size */
1222 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1225 gboolean
1226 mono_thread_info_yield (void)
1228 return mono_threads_platform_yield ();
1230 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1231 static MonoCoopMutex sleep_mutex;
1232 static MonoCoopCond sleep_cond;
1234 static void
1235 sleep_initialize (void)
1237 mono_coop_mutex_init (&sleep_mutex);
1238 mono_coop_cond_init (&sleep_cond);
1241 static void
1242 sleep_interrupt (gpointer data)
1244 mono_coop_mutex_lock (&sleep_mutex);
1245 mono_coop_cond_broadcast (&sleep_cond);
1246 mono_coop_mutex_unlock (&sleep_mutex);
1249 static inline guint32
1250 sleep_interruptable (guint32 ms, gboolean *alerted)
1252 gint64 now, end;
1254 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1256 g_assert (alerted);
1257 *alerted = FALSE;
1259 if (ms != MONO_INFINITE_WAIT)
1260 end = mono_msec_ticks() + ms;
1262 mono_lazy_initialize (&sleep_init, sleep_initialize);
1264 mono_coop_mutex_lock (&sleep_mutex);
1266 for (;;) {
1267 if (ms != MONO_INFINITE_WAIT) {
1268 now = mono_msec_ticks();
1269 if (now >= end)
1270 break;
1273 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1274 if (*alerted) {
1275 mono_coop_mutex_unlock (&sleep_mutex);
1276 return WAIT_IO_COMPLETION;
1279 if (ms != MONO_INFINITE_WAIT)
1280 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1281 else
1282 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1284 mono_thread_info_uninstall_interrupt (alerted);
1285 if (*alerted) {
1286 mono_coop_mutex_unlock (&sleep_mutex);
1287 return WAIT_IO_COMPLETION;
1291 mono_coop_mutex_unlock (&sleep_mutex);
1293 return 0;
1296 gint
1297 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1299 if (ms == 0) {
1300 MonoThreadInfo *info;
1302 mono_thread_info_yield ();
1304 info = mono_thread_info_current ();
1305 if (info && mono_thread_info_is_interrupt_state (info))
1306 return WAIT_IO_COMPLETION;
1308 return 0;
1311 if (alerted)
1312 return sleep_interruptable (ms, alerted);
1314 MONO_ENTER_GC_SAFE;
1316 if (ms == MONO_INFINITE_WAIT) {
1317 do {
1318 #ifdef HOST_WIN32
1319 Sleep (G_MAXUINT32);
1320 #else
1321 sleep (G_MAXUINT32);
1322 #endif
1323 } while (1);
1324 } else {
1325 int ret;
1326 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1327 struct timespec start, target;
1329 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1330 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1331 g_assert (ret == 0);
1333 target = start;
1334 target.tv_sec += ms / 1000;
1335 target.tv_nsec += (ms % 1000) * 1000000;
1336 if (target.tv_nsec > 999999999) {
1337 target.tv_nsec -= 999999999;
1338 target.tv_sec ++;
1341 do {
1342 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1343 } while (ret != 0);
1344 #elif HOST_WIN32
1345 Sleep (ms);
1346 #else
1347 struct timespec req, rem;
1349 req.tv_sec = ms / 1000;
1350 req.tv_nsec = (ms % 1000) * 1000000;
1352 do {
1353 memset (&rem, 0, sizeof (rem));
1354 ret = nanosleep (&req, &rem);
1355 } while (ret != 0);
1356 #endif /* __linux__ */
1359 MONO_EXIT_GC_SAFE;
1361 return 0;
1364 gint
1365 mono_thread_info_usleep (guint64 us)
1367 MONO_ENTER_GC_SAFE;
1368 g_usleep (us);
1369 MONO_EXIT_GC_SAFE;
1370 return 0;
1373 gpointer
1374 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1376 return ((MonoThreadInfo*)info)->tls [key];
1380 * mono_threads_info_tls_set:
1382 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1383 * values of TLS variables for threads other than the current thread.
1384 * This should only be used for infrequently changing TLS variables, and it should
1385 * be paired with setting the real TLS variable since this provides no GC tracking.
1387 void
1388 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1390 ((MonoThreadInfo*)info)->tls [key] = value;
1393 #if defined(__native_client__)
1394 void nacl_shutdown_gc_thread(void);
1395 #endif
1398 * mono_thread_info_exit:
1400 * Exit the current thread.
1401 * This function doesn't return.
1403 void
1404 mono_thread_info_exit (gsize exit_code)
1406 #if defined(__native_client__)
1407 nacl_shutdown_gc_thread();
1408 #endif
1410 mono_thread_info_detach ();
1412 mono_threads_platform_exit (0);
1416 * mono_threads_open_thread_handle:
1418 * Duplicate the handle. The handle needs to be closed by calling
1419 * mono_threads_close_thread_handle () when it is no longer needed.
1421 MonoThreadHandle*
1422 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1424 return mono_refcount_inc (thread_handle);
1427 void
1428 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1430 mono_refcount_dec (thread_handle);
1433 static void
1434 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1436 mono_os_event_set (&thread_handle->event);
1439 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1441 struct _MonoThreadInfoInterruptToken {
1442 void (*callback) (gpointer data);
1443 gpointer data;
1447 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1449 * - @callback: must be able to be called from another thread and always cancel the wait
1450 * - @data: passed to the callback
1451 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1452 * if set to TRUE, it must mean that the thread is in interrupted state
1454 void
1455 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1457 MonoThreadInfo *info;
1458 MonoThreadInfoInterruptToken *previous_token, *token;
1460 g_assert (callback);
1462 g_assert (interrupted);
1463 *interrupted = FALSE;
1465 info = mono_thread_info_current ();
1466 g_assert (info);
1468 /* The memory of this token can be freed at 2 places:
1469 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1470 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1471 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1472 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1473 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1474 token->callback = callback;
1475 token->data = data;
1477 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1479 if (previous_token) {
1480 if (previous_token != INTERRUPT_STATE)
1481 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1483 g_free (token);
1485 *interrupted = TRUE;
1488 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1489 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1492 void
1493 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1495 MonoThreadInfo *info;
1496 MonoThreadInfoInterruptToken *previous_token;
1498 g_assert (interrupted);
1499 *interrupted = FALSE;
1501 info = mono_thread_info_current ();
1502 g_assert (info);
1504 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1506 /* only the installer can uninstall the token */
1507 g_assert (previous_token);
1509 if (previous_token == INTERRUPT_STATE) {
1510 /* if it is interrupted, then it is going to be freed in finish interrupt */
1511 *interrupted = TRUE;
1512 } else {
1513 g_free (previous_token);
1516 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1517 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1520 static MonoThreadInfoInterruptToken*
1521 set_interrupt_state (MonoThreadInfo *info)
1523 MonoThreadInfoInterruptToken *token, *previous_token;
1525 g_assert (info);
1527 /* Atomically obtain the token the thread is
1528 * waiting on, and change it to a flag value. */
1530 do {
1531 previous_token = info->interrupt_token;
1533 /* Already interrupted */
1534 if (previous_token == INTERRUPT_STATE) {
1535 token = NULL;
1536 break;
1539 token = previous_token;
1540 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1542 return token;
1546 * mono_thread_info_prepare_interrupt:
1548 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1549 * - if the thread calls one of the WaitFor functions, the function will return with
1550 * WAIT_IO_COMPLETION instead of waiting
1551 * - if the thread was waiting when this function was called, the wait will be broken
1553 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1554 * didn't receive the interrupt signal yet, in this case it should call the wait function
1555 * again. This essentially means that the target thread will busy wait until it is ready to
1556 * process the interruption.
1558 MonoThreadInfoInterruptToken*
1559 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1561 MonoThreadInfoInterruptToken *token;
1563 token = set_interrupt_state (info);
1565 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1566 mono_thread_info_get_tid (info), token);
1568 return token;
1571 void
1572 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1574 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1576 if (token == NULL)
1577 return;
1579 g_assert (token->callback);
1581 token->callback (token->data);
1583 g_free (token);
1586 void
1587 mono_thread_info_self_interrupt (void)
1589 MonoThreadInfo *info;
1590 MonoThreadInfoInterruptToken *token;
1592 info = mono_thread_info_current ();
1593 g_assert (info);
1595 token = set_interrupt_state (info);
1596 g_assert (!token);
1598 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1599 mono_thread_info_get_tid (info));
1602 /* Clear the interrupted flag of the current thread, set with
1603 * mono_thread_info_self_interrupt, so it can wait again */
1604 void
1605 mono_thread_info_clear_self_interrupt ()
1607 MonoThreadInfo *info;
1608 MonoThreadInfoInterruptToken *previous_token;
1610 info = mono_thread_info_current ();
1611 g_assert (info);
1613 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1614 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1616 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1619 gboolean
1620 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1622 g_assert (info);
1623 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1626 void
1627 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1629 g_assert (info);
1631 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1632 g_string_append_printf (text, "not waiting");
1633 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1634 g_string_append_printf (text, "interrupted state");
1635 else
1636 g_string_append_printf (text, "waiting");
1639 gboolean
1640 mono_thread_info_is_current (MonoThreadInfo *info)
1642 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1645 MonoThreadInfoWaitRet
1646 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1648 MonoOSEventWaitRet res;
1650 res = mono_os_event_wait_one (&thread_handle->event, timeout);
1651 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1652 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1653 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1654 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1655 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1656 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1657 else
1658 g_error ("%s: unknown res value %d", __func__, res);
1661 MonoThreadInfoWaitRet
1662 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1664 MonoOSEventWaitRet res;
1665 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1666 gint i;
1668 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1669 if (background_change_event)
1670 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1672 for (i = 0; i < nhandles; ++i)
1673 thread_events [i] = &thread_handles [i]->event;
1675 if (background_change_event)
1676 thread_events [nhandles ++] = background_change_event;
1678 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout);
1679 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1680 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1681 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1682 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1683 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1684 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1685 else
1686 g_error ("%s: unknown res value %d", __func__, res);