2 * mono-threads.c: Low-level threading
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.
14 /* enable pthread extensions */
16 #define _DARWIN_C_SOURCE
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>
38 #include <mono/utils/mach-support.h>
42 Mutex that makes sure only a single thread can be suspending others.
43 Suspend is a very racy operation since it requires restarting until
44 the target thread is not on an unsafe region.
46 We could implement this using critical regions, but would be much much
47 harder for an operation that is hardly performance critical.
49 The GC has to acquire this lock before starting a STW to make sure
50 a runtime suspend won't make it wronly see a thread in a safepoint
51 when it is in fact not.
53 This has to be a naked locking primitive, and not a coop aware one, as
54 it needs to be usable when destroying thread_info_key, the TLS key for
55 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
56 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
57 to an assertion error. We then simply switch state manually in
58 mono_thread_info_suspend_lock_with_info.
60 static MonoSemType global_suspend_semaphore;
62 static size_t thread_info_size;
63 static MonoThreadInfoCallbacks threads_callbacks;
64 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
65 static MonoNativeTlsKey thread_info_key, thread_exited_key;
67 static __thread guint32 tls_small_id MONO_TLS_FAST;
69 static MonoNativeTlsKey small_id_key;
71 static MonoLinkedListSet thread_list;
72 static gboolean mono_threads_inited = FALSE;
74 static MonoSemType suspend_semaphore;
75 static size_t pending_suspends;
77 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
80 #define SLEEP_DURATION_BEFORE_WARNING (50)
82 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
84 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
85 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
87 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
90 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
92 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
93 InterlockedIncrement (&abort_posts);
94 mono_os_sem_post (&suspend_semaphore);
98 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
100 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
101 InterlockedIncrement (&suspend_posts);
102 mono_os_sem_post (&suspend_semaphore);
106 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
108 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
109 InterlockedIncrement (&resume_posts);
110 mono_os_sem_post (&suspend_semaphore);
114 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
116 if (mono_threads_is_coop_enabled ()) {
117 /* There's nothing else to do after we async request the thread to suspend */
118 mono_threads_add_to_pending_operation_set (info);
122 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
126 check_async_suspend (MonoThreadInfo *info)
128 if (mono_threads_is_coop_enabled ()) {
129 /* Async suspend can't async fail on coop */
133 return mono_threads_suspend_check_suspend_result (info);
137 resume_async_suspended (MonoThreadInfo *info)
139 if (mono_threads_is_coop_enabled ())
140 g_assert_not_reached ();
142 g_assert (mono_threads_suspend_begin_async_resume (info));
146 resume_self_suspended (MonoThreadInfo* info)
148 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
149 mono_os_sem_post (&info->resume_semaphore);
153 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
156 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
157 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
158 g_assert (res != -1);
162 resume_blocking_suspended (MonoThreadInfo* info)
164 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
165 mono_os_sem_post (&info->resume_semaphore);
169 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
171 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
173 InterlockedIncrement (&pending_ops);
177 mono_threads_begin_global_suspend (void)
179 size_t ps = pending_suspends;
180 if (G_UNLIKELY (ps != 0))
181 g_error ("pending_suspends = %d, but must be 0", ps);
182 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,
183 abort_posts, waits_done, pending_ops);
184 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
185 mono_threads_coop_begin_global_suspend ();
189 mono_threads_end_global_suspend (void)
191 size_t ps = pending_suspends;
192 if (G_UNLIKELY (ps != 0))
193 g_error ("pending_suspends = %d, but must be 0", ps);
194 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
195 abort_posts, waits_done, pending_ops);
196 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
197 mono_threads_coop_end_global_suspend ();
203 MonoThreadInfo *cur = mono_thread_info_current ();
205 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
206 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
207 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
216 FOREACH_THREAD_SAFE (info) {
218 char thread_name [256] = { 0 };
219 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
221 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" : "" );
223 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" : "" );
225 } FOREACH_THREAD_SAFE_END
229 mono_threads_wait_pending_operations (void)
232 int c = pending_suspends;
234 /* Wait threads to park */
235 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
236 if (pending_suspends) {
237 MonoStopwatch suspension_time;
238 mono_stopwatch_start (&suspension_time);
239 for (i = 0; i < pending_suspends; ++i) {
240 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
241 InterlockedIncrement (&waits_done);
242 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
244 mono_stopwatch_stop (&suspension_time);
248 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
249 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
251 mono_stopwatch_stop (&suspension_time);
252 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
256 pending_suspends = 0;
262 //Thread initialization code
265 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
268 mono_hazard_pointer_clear (hp, 0);
270 mono_hazard_pointer_clear (hp, 1);
272 mono_hazard_pointer_clear (hp, 2);
276 If return non null Hazard Pointer 1 holds the return value.
279 mono_thread_info_lookup (MonoNativeThreadId id)
281 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
283 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
284 mono_hazard_pointer_clear_all (hp, -1);
288 mono_hazard_pointer_clear_all (hp, 1);
289 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
293 mono_thread_info_insert (MonoThreadInfo *info)
295 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
297 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
298 mono_hazard_pointer_clear_all (hp, -1);
302 mono_hazard_pointer_clear_all (hp, -1);
307 mono_thread_info_remove (MonoThreadInfo *info)
309 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
312 THREADS_DEBUG ("removing info %p\n", info);
313 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
314 mono_hazard_pointer_clear_all (hp, -1);
319 free_thread_info (gpointer mem)
321 MonoThreadInfo *info = (MonoThreadInfo *) mem;
323 mono_os_sem_destroy (&info->resume_semaphore);
324 mono_threads_suspend_free (info);
330 mono_thread_info_register_small_id (void)
332 int small_id = mono_thread_small_id_alloc ();
333 #ifdef HAVE_KW_THREAD
334 tls_small_id = small_id;
336 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
342 register_thread (MonoThreadInfo *info, gpointer baseptr)
345 guint8 *staddr = NULL;
346 int small_id = mono_thread_info_register_small_id ();
348 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
349 info->small_id = small_id;
351 info->handle = g_new0 (MonoThreadHandle, 1);
352 info->handle->ref = 1;
353 mono_os_event_init (&info->handle->event, TRUE, FALSE);
355 mono_os_sem_init (&info->resume_semaphore, 0);
357 /*set TLS early so SMR works */
358 mono_native_tls_set_value (thread_info_key, info);
360 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
362 if (threads_callbacks.thread_register) {
363 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
364 // g_warning ("thread registation failed\n");
365 mono_native_tls_set_value (thread_info_key, NULL);
371 mono_thread_info_get_stack_bounds (&staddr, &stsize);
374 info->stack_start_limit = staddr;
375 info->stack_end = staddr + stsize;
377 info->stackdata = g_byte_array_new ();
379 mono_threads_suspend_register (info);
382 Transition it before taking any locks or publishing itself to reduce the chance
383 of others witnessing a detached thread.
384 We can reasonably expect that until this thread gets published, no other thread will
385 try to manipulate it.
387 mono_threads_transition_attach (info);
388 mono_thread_info_suspend_lock ();
389 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
390 result = mono_thread_info_insert (info);
392 mono_thread_info_suspend_unlock ();
397 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
400 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
403 unregister_thread (void *arg)
405 gpointer gc_unsafe_stackdata;
406 MonoThreadInfo *info;
411 info = (MonoThreadInfo *) arg;
413 g_assert (mono_thread_info_is_current (info));
414 g_assert (mono_thread_info_is_live (info));
416 small_id = info->small_id;
418 /* We only enter the GC unsafe region, as when exiting this function, the thread
419 * will be detached, and the current MonoThreadInfo* will be destroyed. */
420 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
422 THREADS_DEBUG ("unregistering info %p\n", info);
424 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
427 * TLS destruction order is not reliable so small_id might be cleaned up
430 #ifndef HAVE_KW_THREAD
431 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
434 /* we need to duplicate it, as the info->handle is going
435 * to be closed when unregistering from the platform */
436 handle = mono_threads_open_thread_handle (info->handle);
439 First perform the callback that requires no locks.
440 This callback has the potential of taking other locks, so we do it before.
441 After it completes, the thread remains functional.
443 if (threads_callbacks.thread_detach)
444 threads_callbacks.thread_detach (info);
446 mono_thread_info_suspend_lock_with_info (info);
449 Now perform the callback that must be done under locks.
450 This will render the thread useless and non-suspendable, so it must
451 be done while holding the suspend lock to give no other thread chance
454 if (threads_callbacks.thread_unregister)
455 threads_callbacks.thread_unregister (info);
457 /* The thread is no longer active, so unref its handle */
458 mono_threads_close_thread_handle (info->handle);
461 result = mono_thread_info_remove (info);
463 mono_threads_transition_detach (info);
465 mono_thread_info_suspend_unlock ();
467 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
469 /*now it's safe to free the thread info.*/
470 mono_thread_hazardous_try_free (info, free_thread_info);
471 /* Pump the HP queue */
472 mono_thread_hazardous_try_free_some ();
474 mono_thread_small_id_free (small_id);
476 mono_threads_signal_thread_handle (handle);
478 mono_threads_close_thread_handle (handle);
482 thread_exited_dtor (void *arg)
484 #if defined(__MACH__)
486 * Since we use pthread dtors to clean up thread data, if a thread
487 * is attached to the runtime by another pthread dtor after our dtor
488 * has ran, it will never be detached, leading to various problems
489 * since the thread ids etc. will be reused while they are still in
490 * the threads hashtables etc.
491 * Dtors are called in a loop until all user tls entries are 0,
492 * but the loop has a maximum count (4), so if we set the tls
493 * variable every time, it will remain set when system tls dtors
494 * are ran. This allows mono_thread_info_is_exiting () to detect
495 * whenever the thread is exiting, even if it is executed from a
496 * system tls dtor (i.e. obj-c dealloc methods).
498 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
503 mono_thread_info_current_unchecked (void)
505 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
510 mono_thread_info_current (void)
512 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
516 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
519 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
520 The way to distinguish between before, during and after cleanup is the following:
522 -If the TLS key is set, cleanup has not begun;
523 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
524 -If the thread is nowhere to be found, cleanup has finished.
526 We cannot function after cleanup since there's no way to ensure what will happen.
530 /*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 */
531 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
537 mono_thread_info_get_small_id (void)
539 #ifdef HAVE_KW_THREAD
542 gpointer val = mono_native_tls_get_value (small_id_key);
545 return GPOINTER_TO_INT (val) - 1;
550 mono_thread_info_list_head (void)
556 * mono_threads_attach_tools_thread
558 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
560 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
561 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
564 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
565 * doing things like resolving backtraces in their background processing thread.
568 mono_threads_attach_tools_thread (void)
571 MonoThreadInfo *info;
573 /* Must only be called once */
574 g_assert (!mono_native_tls_get_value (thread_info_key));
576 while (!mono_threads_inited) {
577 mono_thread_info_usleep (10);
580 info = mono_thread_info_attach (&dummy);
583 info->tools_thread = TRUE;
587 mono_thread_info_attach (void *baseptr)
589 MonoThreadInfo *info;
590 if (!mono_threads_inited)
593 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
594 * thread is created before an embedding API user initialized Mono. */
595 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
598 g_assert (mono_threads_inited);
601 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
603 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
604 THREADS_DEBUG ("attaching %p\n", info);
605 if (!register_thread (info, baseptr))
607 } else if (threads_callbacks.thread_attach) {
608 threads_callbacks.thread_attach (info);
614 mono_thread_info_detach (void)
616 MonoThreadInfo *info;
617 if (!mono_threads_inited)
619 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
620 * is created before an embedding API user initialized Mono. */
621 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
624 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
626 THREADS_DEBUG ("detaching %p\n", info);
627 unregister_thread (info);
628 mono_native_tls_set_value (thread_info_key, NULL);
633 * mono_thread_info_is_exiting:
635 * Return whenever the current thread is exiting, i.e. it is running pthread
639 mono_thread_info_is_exiting (void)
641 #if defined(__MACH__)
642 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
650 thread_info_key_dtor (void *arg)
652 /* Put the MonoThreadInfo back for the duration of the
653 * unregister code. In some circumstances the thread needs to
654 * take the GC lock which may block which requires a coop
655 * state transition. */
656 mono_native_tls_set_value (thread_info_key, arg);
657 unregister_thread (arg);
658 mono_native_tls_set_value (thread_info_key, NULL);
663 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
666 threads_callbacks = *callbacks;
667 thread_info_size = info_size;
668 const char *sleepLimit;
670 res = mono_native_tls_alloc (&thread_info_key, NULL);
671 res = mono_native_tls_alloc (&thread_exited_key, NULL);
673 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
674 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
679 #ifndef HAVE_KW_THREAD
680 res = mono_native_tls_alloc (&small_id_key, NULL);
684 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
686 long threshold = strtol(sleepLimit, NULL, 10);
687 if ((errno == 0) && (threshold >= 40)) {
688 sleepAbortDuration = threshold;
689 sleepWarnDuration = threshold / 20;
691 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
694 mono_os_sem_init (&global_suspend_semaphore, 1);
695 mono_os_sem_init (&suspend_semaphore, 0);
697 mono_lls_init (&thread_list, NULL);
698 mono_thread_smr_init ();
699 mono_threads_suspend_init ();
700 mono_threads_suspend_init_signals ();
701 mono_threads_coop_init ();
703 #if defined(__MACH__)
704 mono_mach_init (thread_info_key);
707 mono_threads_inited = TRUE;
709 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
713 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
715 runtime_callbacks = *callbacks;
718 MonoThreadInfoRuntimeCallbacks *
719 mono_threads_get_runtime_callbacks (void)
721 return &runtime_callbacks;
725 Signal that the current thread wants to be suspended.
726 This function can be called without holding the suspend lock held.
727 To finish suspending, call mono_suspend_check.
730 mono_thread_info_begin_self_suspend (void)
732 g_assert (!mono_threads_is_coop_enabled ());
734 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
738 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
739 mono_threads_transition_request_self_suspension (info);
743 mono_thread_info_end_self_suspend (void)
745 MonoThreadInfo *info;
747 g_assert (!mono_threads_is_coop_enabled ());
749 info = mono_thread_info_current ();
752 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
754 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
756 /* commit the saved state and notify others if needed */
757 switch (mono_threads_transition_state_poll (info)) {
758 case SelfSuspendResumed:
760 case SelfSuspendWait:
761 mono_thread_info_wait_for_resume (info);
763 case SelfSuspendNotifyAndWait:
764 mono_threads_notify_initiator_of_suspend (info);
765 mono_thread_info_wait_for_resume (info);
766 mono_threads_notify_initiator_of_resume (info);
772 mono_thread_info_core_resume (MonoThreadInfo *info)
774 gboolean res = FALSE;
776 switch (mono_threads_transition_request_resume (info)) {
783 case ResumeInitSelfResume:
784 resume_self_suspended (info);
787 case ResumeInitAsyncResume:
788 resume_async_suspended (info);
791 case ResumeInitBlockingResume:
792 resume_blocking_suspended (info);
801 mono_thread_info_resume (MonoNativeThreadId tid)
803 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
804 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
805 MonoThreadInfo *info;
807 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
809 mono_thread_info_suspend_lock ();
811 info = mono_thread_info_lookup (tid); /*info on HP1*/
817 result = mono_thread_info_core_resume (info);
819 //Wait for the pending resume to finish
820 mono_threads_wait_pending_operations ();
823 mono_thread_info_suspend_unlock ();
824 mono_hazard_pointer_clear (hp, 1);
829 mono_thread_info_begin_suspend (MonoThreadInfo *info)
831 switch (mono_threads_transition_request_async_suspension (info)) {
832 case AsyncSuspendAlreadySuspended:
833 case AsyncSuspendBlocking:
835 case AsyncSuspendWait:
836 mono_threads_add_to_pending_operation_set (info);
838 case AsyncSuspendInitSuspend:
839 return begin_async_suspend (info, FALSE);
841 g_assert_not_reached ();
846 mono_thread_info_begin_resume (MonoThreadInfo *info)
848 return mono_thread_info_core_resume (info);
852 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
853 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
856 is_thread_in_critical_region (MonoThreadInfo *info)
860 gpointer stack_start;
861 MonoThreadUnwindState *state;
863 /* Are we inside a system critical region? */
864 if (info->inside_critical_region)
867 /* Are we inside a GC critical region? */
868 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
872 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
873 state = mono_thread_info_get_suspend_state (info);
874 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
877 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
878 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
879 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
882 if (threads_callbacks.ip_in_critical_region)
883 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
885 ji = mono_jit_info_table_find (
886 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
887 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
892 method = mono_jit_info_get_method (ji);
894 return threads_callbacks.mono_method_is_critical (method);
898 mono_thread_info_in_critical_location (MonoThreadInfo *info)
900 return is_thread_in_critical_region (info);
904 The return value is only valid until a matching mono_thread_info_resume is called
906 static MonoThreadInfo*
907 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
909 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
910 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
914 switch (mono_threads_transition_request_async_suspension (info)) {
915 case AsyncSuspendAlreadySuspended:
916 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
918 case AsyncSuspendWait:
919 mono_threads_add_to_pending_operation_set (info);
921 case AsyncSuspendInitSuspend:
922 if (!begin_async_suspend (info, interrupt_kernel)) {
923 mono_hazard_pointer_clear (hp, 1);
927 case AsyncSuspendBlocking:
928 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
929 mono_threads_suspend_abort_syscall (info);
933 g_assert_not_reached ();
936 //Wait for the pending suspend to finish
937 mono_threads_wait_pending_operations ();
939 if (!check_async_suspend (info)) {
940 mono_thread_info_core_resume (info);
941 mono_threads_wait_pending_operations ();
942 mono_hazard_pointer_clear (hp, 1);
948 static MonoThreadInfo*
949 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
951 MonoThreadInfo *info = NULL;
952 int sleep_duration = 0;
954 if (!(info = suspend_sync (id, interrupt_kernel))) {
955 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
959 /*WARNING: We now are in interrupt context until we resume the thread. */
960 if (!is_thread_in_critical_region (info))
963 if (!mono_thread_info_core_resume (info)) {
964 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
967 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
969 /* Wait for the pending resume to finish */
970 mono_threads_wait_pending_operations ();
972 if (sleep_duration == 0)
973 mono_thread_info_yield ();
975 g_usleep (sleep_duration);
977 sleep_duration += 10;
983 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
986 MonoThreadInfo *info = NULL;
987 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
989 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
990 /*FIXME: unify this with self-suspend*/
991 g_assert (id != mono_native_thread_id_get ());
993 /* This can block during stw */
994 mono_thread_info_suspend_lock ();
995 mono_threads_begin_global_suspend ();
997 info = suspend_sync_nolock (id, interrupt_kernel);
1001 switch (result = callback (info, user_data)) {
1002 case MonoResumeThread:
1003 mono_hazard_pointer_set (hp, 1, info);
1004 mono_thread_info_core_resume (info);
1005 mono_threads_wait_pending_operations ();
1008 g_assert (!mono_threads_is_coop_enabled ());
1011 g_error ("Invalid suspend_and_run callback return value %d", result);
1015 mono_hazard_pointer_clear (hp, 1);
1016 mono_threads_end_global_suspend ();
1017 mono_thread_info_suspend_unlock ();
1021 Inject an assynchronous call into the target thread. The target thread must be suspended and
1022 only a single async call can be setup for a given suspend cycle.
1023 This async call must cause stack unwinding as the current implementation doesn't save enough state
1024 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1025 currently used only to deliver exceptions.
1028 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1030 /* An async call can only be setup on an async suspended thread */
1031 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1032 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1033 g_assert (!info->async_target);
1034 info->async_target = target_func;
1035 /* This is not GC tracked */
1036 info->user_data = user_data;
1040 The suspend lock is held during any suspend in progress.
1041 A GC that has safepoints must take this lock as part of its
1042 STW to make sure no unsafe pending suspend is in progress.
1046 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1049 g_assert (mono_thread_info_is_current (info));
1050 g_assert (mono_thread_info_is_live (info));
1052 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1054 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1055 g_assert (res != -1);
1057 MONO_EXIT_GC_SAFE_WITH_INFO;
1061 mono_thread_info_suspend_lock (void)
1063 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1067 mono_thread_info_suspend_unlock (void)
1069 mono_os_sem_post (&global_suspend_semaphore);
1073 * This is a very specific function whose only purpose is to
1074 * break a given thread from socket syscalls.
1076 * This only exists because linux won't fail a call to connect
1077 * if the underlying is closed.
1079 * TODO We should cleanup and unify this with the other syscall abort
1083 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1085 MonoThreadHazardPointers *hp;
1086 MonoThreadInfo *info;
1088 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1091 hp = mono_hazard_pointer_get ();
1092 info = mono_thread_info_lookup (tid);
1096 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1097 mono_hazard_pointer_clear (hp, 1);
1101 mono_thread_info_suspend_lock ();
1102 mono_threads_begin_global_suspend ();
1104 mono_threads_suspend_abort_syscall (info);
1105 mono_threads_wait_pending_operations ();
1107 mono_hazard_pointer_clear (hp, 1);
1109 mono_threads_end_global_suspend ();
1110 mono_thread_info_suspend_unlock ();
1114 * mono_thread_info_set_is_async_context:
1116 * Set whenever the current thread is in an async context. Some runtime functions might behave
1117 * differently while in an async context in order to be async safe.
1120 mono_thread_info_set_is_async_context (gboolean async_context)
1122 MonoThreadInfo *info = mono_thread_info_current ();
1125 info->is_async_context = async_context;
1129 mono_thread_info_is_async_context (void)
1131 MonoThreadInfo *info = mono_thread_info_current ();
1134 return info->is_async_context;
1141 MonoThreadStart start_routine;
1142 gpointer start_routine_arg;
1144 MonoCoopSem registered;
1145 MonoThreadHandle *handle;
1149 inner_start_thread (gpointer data)
1151 CreateThreadData *thread_data;
1152 MonoThreadInfo *info;
1153 MonoThreadStart start_routine;
1154 gpointer start_routine_arg;
1155 gsize start_routine_res;
1158 thread_data = (CreateThreadData*) data;
1159 g_assert (thread_data);
1161 start_routine = thread_data->start_routine;
1162 start_routine_arg = thread_data->start_routine_arg;
1164 info = mono_thread_info_attach (&dummy);
1165 info->runtime_thread = TRUE;
1167 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1169 mono_coop_sem_post (&thread_data->registered);
1171 if (InterlockedDecrement (&thread_data->ref) == 0) {
1172 mono_coop_sem_destroy (&thread_data->registered);
1173 g_free (thread_data);
1176 /* thread_data is not valid anymore */
1179 /* Run the actual main function of the thread */
1180 start_routine_res = start_routine (start_routine_arg);
1182 mono_thread_info_exit (start_routine_res);
1184 g_assert_not_reached ();
1188 * mono_threads_create_thread:
1190 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1191 * Returns: a windows or io-layer handle for the thread.
1194 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1196 CreateThreadData *thread_data;
1198 MonoThreadHandle *ret;
1200 thread_data = g_new0 (CreateThreadData, 1);
1201 thread_data->ref = 2;
1202 thread_data->start_routine = start;
1203 thread_data->start_routine_arg = arg;
1204 mono_coop_sem_init (&thread_data->registered, 0);
1206 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, stack_size, out_tid);
1208 /* ref is not going to be decremented in inner_start_thread */
1209 InterlockedDecrement (&thread_data->ref);
1214 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1215 g_assert (res == 0);
1217 ret = thread_data->handle;
1221 if (InterlockedDecrement (&thread_data->ref) == 0) {
1222 mono_coop_sem_destroy (&thread_data->registered);
1223 g_free (thread_data);
1230 * mono_thread_info_get_stack_bounds:
1232 * Return the address and size of the current threads stack. Return NULL as the
1233 * stack address if the stack address cannot be determined.
1236 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1238 guint8 *current = (guint8 *)&stsize;
1239 mono_threads_platform_get_stack_bounds (staddr, stsize);
1243 /* Sanity check the result */
1244 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1246 /* When running under emacs, sometimes staddr is not aligned to a page size */
1247 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1251 mono_thread_info_yield (void)
1253 return mono_threads_platform_yield ();
1255 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1256 static MonoCoopMutex sleep_mutex;
1257 static MonoCoopCond sleep_cond;
1260 sleep_initialize (void)
1262 mono_coop_mutex_init (&sleep_mutex);
1263 mono_coop_cond_init (&sleep_cond);
1267 sleep_interrupt (gpointer data)
1269 mono_coop_mutex_lock (&sleep_mutex);
1270 mono_coop_cond_broadcast (&sleep_cond);
1271 mono_coop_mutex_unlock (&sleep_mutex);
1274 static inline guint32
1275 sleep_interruptable (guint32 ms, gboolean *alerted)
1279 g_assert (INFINITE == G_MAXUINT32);
1285 end = mono_msec_ticks() + ms;
1287 mono_lazy_initialize (&sleep_init, sleep_initialize);
1289 mono_coop_mutex_lock (&sleep_mutex);
1292 if (ms != INFINITE) {
1293 now = mono_msec_ticks();
1298 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1300 mono_coop_mutex_unlock (&sleep_mutex);
1301 return WAIT_IO_COMPLETION;
1305 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1307 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1309 mono_thread_info_uninstall_interrupt (alerted);
1311 mono_coop_mutex_unlock (&sleep_mutex);
1312 return WAIT_IO_COMPLETION;
1316 mono_coop_mutex_unlock (&sleep_mutex);
1322 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1325 MonoThreadInfo *info;
1327 mono_thread_info_yield ();
1329 info = mono_thread_info_current ();
1330 if (info && mono_thread_info_is_interrupt_state (info))
1331 return WAIT_IO_COMPLETION;
1337 return sleep_interruptable (ms, alerted);
1341 if (ms == INFINITE) {
1344 Sleep (G_MAXUINT32);
1346 sleep (G_MAXUINT32);
1351 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1352 struct timespec start, target;
1354 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1355 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1356 g_assert (ret == 0);
1359 target.tv_sec += ms / 1000;
1360 target.tv_nsec += (ms % 1000) * 1000000;
1361 if (target.tv_nsec > 999999999) {
1362 target.tv_nsec -= 999999999;
1367 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1372 struct timespec req, rem;
1374 req.tv_sec = ms / 1000;
1375 req.tv_nsec = (ms % 1000) * 1000000;
1378 memset (&rem, 0, sizeof (rem));
1379 ret = nanosleep (&req, &rem);
1381 #endif /* __linux__ */
1390 mono_thread_info_usleep (guint64 us)
1399 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1401 return ((MonoThreadInfo*)info)->tls [key];
1405 * mono_threads_info_tls_set:
1407 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1408 * values of TLS variables for threads other than the current thread.
1409 * This should only be used for infrequently changing TLS variables, and it should
1410 * be paired with setting the real TLS variable since this provides no GC tracking.
1413 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1415 ((MonoThreadInfo*)info)->tls [key] = value;
1418 #if defined(__native_client__)
1419 void nacl_shutdown_gc_thread(void);
1423 * mono_thread_info_exit:
1425 * Exit the current thread.
1426 * This function doesn't return.
1429 mono_thread_info_exit (gsize exit_code)
1431 #if defined(__native_client__)
1432 nacl_shutdown_gc_thread();
1435 mono_thread_info_detach ();
1437 mono_threads_platform_exit (0);
1441 * mono_threads_open_thread_handle:
1443 * Duplicate the handle. The handle needs to be closed by calling
1444 * mono_threads_close_thread_handle () when it is no longer needed.
1447 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1449 guint32 oldref, newref;
1451 g_assert (thread_handle);
1454 oldref = thread_handle->ref;
1456 g_error ("%s: thread_handle %p has ref %u, it should be >= 1", __func__, thread_handle, oldref);
1458 newref = oldref + 1;
1459 } while (InterlockedCompareExchange ((gint32*) &thread_handle->ref, newref, oldref) != oldref);
1461 return thread_handle;
1465 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1467 guint32 oldref, newref;
1469 g_assert (thread_handle);
1472 oldref = thread_handle->ref;
1474 g_error ("%s: thread_handle %p has ref %u, it should be >= 1", __func__, thread_handle, oldref);
1476 newref = oldref - 1;
1477 } while (InterlockedCompareExchange ((gint32*) &thread_handle->ref, newref, oldref) != oldref);
1480 mono_os_event_destroy (&thread_handle->event);
1481 g_free (thread_handle);
1486 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1488 mono_os_event_set (&thread_handle->event);
1491 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1493 struct _MonoThreadInfoInterruptToken {
1494 void (*callback) (gpointer data);
1499 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1501 * - @callback: must be able to be called from another thread and always cancel the wait
1502 * - @data: passed to the callback
1503 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1504 * if set to TRUE, it must mean that the thread is in interrupted state
1507 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1509 MonoThreadInfo *info;
1510 MonoThreadInfoInterruptToken *previous_token, *token;
1512 g_assert (callback);
1514 g_assert (interrupted);
1515 *interrupted = FALSE;
1517 info = mono_thread_info_current ();
1520 /* The memory of this token can be freed at 2 places:
1521 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1522 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1523 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1524 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1525 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1526 token->callback = callback;
1529 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1531 if (previous_token) {
1532 if (previous_token != INTERRUPT_STATE)
1533 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1537 *interrupted = TRUE;
1540 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1541 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1545 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1547 MonoThreadInfo *info;
1548 MonoThreadInfoInterruptToken *previous_token;
1550 g_assert (interrupted);
1551 *interrupted = FALSE;
1553 info = mono_thread_info_current ();
1556 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1558 /* only the installer can uninstall the token */
1559 g_assert (previous_token);
1561 if (previous_token == INTERRUPT_STATE) {
1562 /* if it is interrupted, then it is going to be freed in finish interrupt */
1563 *interrupted = TRUE;
1565 g_free (previous_token);
1568 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1569 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1572 static MonoThreadInfoInterruptToken*
1573 set_interrupt_state (MonoThreadInfo *info)
1575 MonoThreadInfoInterruptToken *token, *previous_token;
1579 /* Atomically obtain the token the thread is
1580 * waiting on, and change it to a flag value. */
1583 previous_token = info->interrupt_token;
1585 /* Already interrupted */
1586 if (previous_token == INTERRUPT_STATE) {
1591 token = previous_token;
1592 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1598 * mono_thread_info_prepare_interrupt:
1600 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1601 * - if the thread calls one of the WaitFor functions, the function will return with
1602 * WAIT_IO_COMPLETION instead of waiting
1603 * - if the thread was waiting when this function was called, the wait will be broken
1605 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1606 * didn't receive the interrupt signal yet, in this case it should call the wait function
1607 * again. This essentially means that the target thread will busy wait until it is ready to
1608 * process the interruption.
1610 MonoThreadInfoInterruptToken*
1611 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1613 MonoThreadInfoInterruptToken *token;
1615 token = set_interrupt_state (info);
1617 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1618 mono_thread_info_get_tid (info), token);
1624 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1626 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1631 g_assert (token->callback);
1633 token->callback (token->data);
1639 mono_thread_info_self_interrupt (void)
1641 MonoThreadInfo *info;
1642 MonoThreadInfoInterruptToken *token;
1644 info = mono_thread_info_current ();
1647 token = set_interrupt_state (info);
1650 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1651 mono_thread_info_get_tid (info));
1654 /* Clear the interrupted flag of the current thread, set with
1655 * mono_thread_info_self_interrupt, so it can wait again */
1657 mono_thread_info_clear_self_interrupt ()
1659 MonoThreadInfo *info;
1660 MonoThreadInfoInterruptToken *previous_token;
1662 info = mono_thread_info_current ();
1665 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1666 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1668 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1672 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1675 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1679 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1683 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1684 g_string_append_printf (text, "not waiting");
1685 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1686 g_string_append_printf (text, "interrupted state");
1688 g_string_append_printf (text, "waiting");
1692 mono_thread_info_is_current (MonoThreadInfo *info)
1694 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1697 MonoThreadInfoWaitRet
1698 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1700 MonoOSEventWaitRet res;
1702 res = mono_os_event_wait_one (&thread_handle->event, timeout);
1703 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1704 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1705 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1706 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1707 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1708 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1710 g_error ("%s: unknown res value %d", __func__, res);
1713 MonoThreadInfoWaitRet
1714 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1716 MonoOSEventWaitRet res;
1717 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1720 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1721 if (background_change_event)
1722 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1724 for (i = 0; i < nhandles; ++i)
1725 thread_events [i] = &thread_handles [i]->event;
1727 if (background_change_event)
1728 thread_events [nhandles ++] = background_change_event;
1730 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout);
1731 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1732 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1733 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1734 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1735 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1736 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1738 g_error ("%s: unknown res value %d", __func__, res);