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)
13 #include <mono/utils/mono-compiler.h>
14 #include <mono/utils/mono-semaphore.h>
15 #include <mono/utils/mono-threads.h>
16 #include <mono/utils/mono-tls.h>
17 #include <mono/utils/hazard-pointer.h>
18 #include <mono/utils/mono-memory-model.h>
19 #include <mono/utils/mono-mmap.h>
20 #include <mono/utils/atomic.h>
21 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mach-support.h>
31 Mutex that makes sure only a single thread can be suspending others.
32 Suspend is a very racy operation since it requires restarting until
33 the target thread is not on an unsafe region.
35 We could implement this using critical regions, but would be much much
36 harder for an operation that is hardly performance critical.
38 The GC has to acquire this lock before starting a STW to make sure
39 a runtime suspend won't make it wronly see a thread in a safepoint
40 when it is in fact not.
42 static MonoSemType global_suspend_semaphore;
44 static size_t thread_info_size;
45 static MonoThreadInfoCallbacks threads_callbacks;
46 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
47 static MonoNativeTlsKey thread_info_key, thread_exited_key;
49 static __thread guint32 tls_small_id MONO_TLS_FAST;
51 static MonoNativeTlsKey small_id_key;
53 static MonoLinkedListSet thread_list;
54 static gboolean mono_threads_inited = FALSE;
56 static MonoSemType suspend_semaphore;
57 static size_t pending_suspends;
58 static gboolean unified_suspend_enabled;
60 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
63 #define SLEEP_DURATION_BEFORE_WARNING (10)
65 #define SLEEP_DURATION_BEFORE_ABORT 200
67 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
70 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
72 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
73 InterlockedIncrement (&abort_posts);
74 MONO_SEM_POST (&suspend_semaphore);
78 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
80 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
81 InterlockedIncrement (&suspend_posts);
82 MONO_SEM_POST (&suspend_semaphore);
86 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
88 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
89 InterlockedIncrement (&resume_posts);
90 MONO_SEM_POST (&suspend_semaphore);
94 resume_async_suspended (MonoThreadInfo *info)
96 g_assert (mono_threads_core_begin_async_resume (info));
100 resume_self_suspended (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
103 MONO_SEM_POST (&info->resume_semaphore);
107 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
109 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
110 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
114 resume_blocking_suspended (MonoThreadInfo* info)
116 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
117 MONO_SEM_POST (&info->resume_semaphore);
121 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
123 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
125 InterlockedIncrement (&pending_ops);
129 mono_threads_begin_global_suspend (void)
131 g_assert (pending_suspends == 0);
132 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,
133 abort_posts, waits_done, pending_ops);
134 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
135 mono_threads_core_begin_global_suspend ();
139 mono_threads_end_global_suspend (void)
141 g_assert (pending_suspends == 0);
142 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
143 abort_posts, waits_done, pending_ops);
144 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
145 mono_threads_core_end_global_suspend ();
151 MonoThreadInfo *info;
152 MonoThreadInfo *cur = mono_thread_info_current ();
154 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
155 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
156 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
157 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
158 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
159 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
160 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
161 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
162 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
163 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
165 FOREACH_THREAD_SAFE (info) {
166 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" : "" );
167 } END_FOREACH_THREAD_SAFE
171 mono_threads_wait_pending_operations (void)
174 int c = pending_suspends;
176 /* Wait threads to park */
177 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
178 if (pending_suspends) {
179 MonoStopwatch suspension_time;
180 mono_stopwatch_start (&suspension_time);
181 for (i = 0; i < pending_suspends; ++i) {
182 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
183 InterlockedIncrement (&waits_done);
184 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
186 mono_stopwatch_stop (&suspension_time);
190 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
191 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), SLEEP_DURATION_BEFORE_ABORT);
193 mono_stopwatch_stop (&suspension_time);
194 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
198 pending_suspends = 0;
204 //Thread initialization code
206 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
209 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
212 mono_hazard_pointer_clear (hp, 0);
214 mono_hazard_pointer_clear (hp, 1);
216 mono_hazard_pointer_clear (hp, 2);
220 If return non null Hazard Pointer 1 holds the return value.
223 mono_thread_info_lookup (MonoNativeThreadId id)
225 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
227 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
228 mono_hazard_pointer_clear_all (hp, -1);
232 mono_hazard_pointer_clear_all (hp, 1);
233 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
237 mono_thread_info_insert (MonoThreadInfo *info)
239 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
241 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
242 mono_hazard_pointer_clear_all (hp, -1);
246 mono_hazard_pointer_clear_all (hp, -1);
251 mono_thread_info_remove (MonoThreadInfo *info)
253 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
256 THREADS_DEBUG ("removing info %p\n", info);
257 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
258 mono_hazard_pointer_clear_all (hp, -1);
263 free_thread_info (gpointer mem)
265 MonoThreadInfo *info = (MonoThreadInfo *) mem;
267 MONO_SEM_DESTROY (&info->resume_semaphore);
268 mono_threads_platform_free (info);
274 mono_thread_info_register_small_id (void)
276 int small_id = mono_thread_small_id_alloc ();
277 #ifdef HAVE_KW_THREAD
278 tls_small_id = small_id;
280 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
286 register_thread (MonoThreadInfo *info, gpointer baseptr)
289 guint8 *staddr = NULL;
290 int small_id = mono_thread_info_register_small_id ();
292 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
293 info->small_id = small_id;
295 MONO_SEM_INIT (&info->resume_semaphore, 0);
297 /*set TLS early so SMR works */
298 mono_native_tls_set_value (thread_info_key, info);
300 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
302 if (threads_callbacks.thread_register) {
303 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
304 // g_warning ("thread registation failed\n");
310 mono_thread_info_get_stack_bounds (&staddr, &stsize);
313 info->stack_start_limit = staddr;
314 info->stack_end = staddr + stsize;
316 mono_threads_platform_register (info);
319 Transition it before taking any locks or publishing itself to reduce the chance
320 of others witnessing a detached thread.
321 We can reasonably expect that until this thread gets published, no other thread will
322 try to manipulate it.
324 mono_threads_transition_attach (info);
325 mono_thread_info_suspend_lock ();
326 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
327 result = mono_thread_info_insert (info);
329 mono_thread_info_suspend_unlock ();
334 unregister_thread (void *arg)
336 MonoThreadInfo *info = (MonoThreadInfo *) arg;
337 int small_id = info->small_id;
340 THREADS_DEBUG ("unregistering info %p\n", info);
342 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
344 mono_threads_core_unregister (info);
347 * TLS destruction order is not reliable so small_id might be cleaned up
350 #ifndef HAVE_KW_THREAD
351 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
355 First perform the callback that requires no locks.
356 This callback has the potential of taking other locks, so we do it before.
357 After it completes, the thread remains functional.
359 if (threads_callbacks.thread_detach)
360 threads_callbacks.thread_detach (info);
362 mono_thread_info_suspend_lock ();
365 Now perform the callback that must be done under locks.
366 This will render the thread useless and non-suspendable, so it must
367 be done while holding the suspend lock to give no other thread chance
370 if (threads_callbacks.thread_unregister)
371 threads_callbacks.thread_unregister (info);
372 mono_threads_unregister_current_thread (info);
373 mono_threads_transition_detach (info);
375 mono_thread_info_suspend_unlock ();
377 /*now it's safe to free the thread info.*/
378 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
379 mono_thread_small_id_free (small_id);
383 thread_exited_dtor (void *arg)
385 #if defined(__MACH__)
387 * Since we use pthread dtors to clean up thread data, if a thread
388 * is attached to the runtime by another pthread dtor after our dtor
389 * has ran, it will never be detached, leading to various problems
390 * since the thread ids etc. will be reused while they are still in
391 * the threads hashtables etc.
392 * Dtors are called in a loop until all user tls entries are 0,
393 * but the loop has a maximum count (4), so if we set the tls
394 * variable every time, it will remain set when system tls dtors
395 * are ran. This allows mono_thread_info_is_exiting () to detect
396 * whenever the thread is exiting, even if it is executed from a
397 * system tls dtor (i.e. obj-c dealloc methods).
399 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
404 * Removes the current thread from the thread list.
405 * This must be called from the thread unregister callback and nowhere else.
406 * The current thread must be passed as TLS might have already been cleaned up.
409 mono_threads_unregister_current_thread (MonoThreadInfo *info)
412 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
413 result = mono_thread_info_remove (info);
418 mono_thread_info_current_unchecked (void)
420 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
425 mono_thread_info_current (void)
427 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
431 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
434 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
435 The way to distinguish between before, during and after cleanup is the following:
437 -If the TLS key is set, cleanup has not begun;
438 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
439 -If the thread is nowhere to be found, cleanup has finished.
441 We cannot function after cleanup since there's no way to ensure what will happen.
445 /*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 */
446 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
452 mono_thread_info_get_small_id (void)
454 #ifdef HAVE_KW_THREAD
457 gpointer val = mono_native_tls_get_value (small_id_key);
460 return GPOINTER_TO_INT (val) - 1;
465 mono_thread_info_list_head (void)
471 * mono_threads_attach_tools_thread
473 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
475 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
476 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
479 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
480 * doing things like resolving backtraces in their background processing thread.
483 mono_threads_attach_tools_thread (void)
486 MonoThreadInfo *info;
488 /* Must only be called once */
489 g_assert (!mono_native_tls_get_value (thread_info_key));
491 while (!mono_threads_inited) {
495 info = mono_thread_info_attach (&dummy);
498 info->tools_thread = TRUE;
502 mono_thread_info_attach (void *baseptr)
504 MonoThreadInfo *info;
505 if (!mono_threads_inited)
508 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
509 * thread is created before an embedding API user initialized Mono. */
510 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
513 g_assert (mono_threads_inited);
516 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
518 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
519 THREADS_DEBUG ("attaching %p\n", info);
520 if (!register_thread (info, baseptr))
522 } else if (threads_callbacks.thread_attach) {
523 threads_callbacks.thread_attach (info);
529 mono_thread_info_detach (void)
531 MonoThreadInfo *info;
532 if (!mono_threads_inited)
534 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
535 * is created before an embedding API user initialized Mono. */
536 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
539 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
541 THREADS_DEBUG ("detaching %p\n", info);
542 unregister_thread (info);
543 mono_native_tls_set_value (thread_info_key, NULL);
548 * mono_thread_info_is_exiting:
550 * Return whenever the current thread is exiting, i.e. it is running pthread
554 mono_thread_info_is_exiting (void)
556 #if defined(__MACH__)
557 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
564 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
567 threads_callbacks = *callbacks;
568 thread_info_size = info_size;
570 res = mono_native_tls_alloc (&thread_info_key, NULL);
571 res = mono_native_tls_alloc (&thread_exited_key, NULL);
573 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
574 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
579 #ifndef HAVE_KW_THREAD
580 res = mono_native_tls_alloc (&small_id_key, NULL);
584 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || MONO_THREADS_PLATFORM_REQUIRES_UNIFIED_SUSPEND;
586 MONO_SEM_INIT (&global_suspend_semaphore, 1);
587 MONO_SEM_INIT (&suspend_semaphore, 0);
589 mono_lls_init (&thread_list, NULL);
590 mono_thread_smr_init ();
591 mono_threads_init_platform ();
593 #if defined(__MACH__)
594 mono_mach_init (thread_info_key);
597 mono_threads_inited = TRUE;
599 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
603 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
605 runtime_callbacks = *callbacks;
608 MonoThreadInfoRuntimeCallbacks *
609 mono_threads_get_runtime_callbacks (void)
611 return &runtime_callbacks;
615 The return value is only valid until a matching mono_thread_info_resume is called
617 static MonoThreadInfo*
618 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
620 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
621 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
623 *error_condition = "Thread not found";
627 switch (mono_threads_transition_request_async_suspension (info)) {
628 case AsyncSuspendAlreadySuspended:
629 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
631 case AsyncSuspendWait:
632 mono_threads_add_to_pending_operation_set (info);
634 case AsyncSuspendInitSuspend:
635 if (!mono_threads_core_begin_async_suspend (info, interrupt_kernel)) {
636 mono_hazard_pointer_clear (hp, 1);
637 *error_condition = "Could not suspend thread";
642 //Wait for the pending suspend to finish
643 mono_threads_wait_pending_operations ();
645 if (!mono_threads_core_check_suspend_result (info)) {
647 mono_hazard_pointer_clear (hp, 1);
648 *error_condition = "Post suspend failed";
655 Signal that the current thread wants to be suspended.
656 This function can be called without holding the suspend lock held.
657 To finish suspending, call mono_suspend_check.
660 mono_thread_info_begin_self_suspend (void)
662 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
666 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
667 mono_threads_transition_request_self_suspension (info);
671 mono_thread_info_end_self_suspend (void)
673 MonoThreadInfo *info;
675 info = mono_thread_info_current ();
678 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
680 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
682 /* commit the saved state and notify others if needed */
683 switch (mono_threads_transition_state_poll (info)) {
684 case SelfSuspendResumed:
686 case SelfSuspendWait:
687 mono_thread_info_wait_for_resume (info);
689 case SelfSuspendNotifyAndWait:
690 mono_threads_notify_initiator_of_suspend (info);
691 mono_thread_info_wait_for_resume (info);
692 mono_threads_notify_initiator_of_resume (info);
698 mono_thread_info_core_resume (MonoThreadInfo *info)
700 gboolean res = FALSE;
701 if (info->create_suspended) {
702 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
703 /* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
704 info->create_suspended = FALSE;
705 mono_threads_core_resume_created (info, tid);
709 switch (mono_threads_transition_request_resume (info)) {
716 case ResumeInitSelfResume:
717 resume_self_suspended (info);
720 case ResumeInitAsyncResume:
721 resume_async_suspended (info);
724 case ResumeInitBlockingResume:
725 resume_blocking_suspended (info);
734 mono_thread_info_resume (MonoNativeThreadId tid)
736 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
737 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
738 MonoThreadInfo *info;
740 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
742 mono_thread_info_suspend_lock ();
744 info = mono_thread_info_lookup (tid); /*info on HP1*/
750 result = mono_thread_info_core_resume (info);
752 //Wait for the pending resume to finish
753 mono_threads_wait_pending_operations ();
756 mono_thread_info_suspend_unlock ();
757 mono_hazard_pointer_clear (hp, 1);
762 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
764 switch (mono_threads_transition_request_async_suspension (info)) {
765 case AsyncSuspendAlreadySuspended:
767 case AsyncSuspendWait:
768 mono_threads_add_to_pending_operation_set (info);
770 case AsyncSuspendInitSuspend:
771 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
773 g_assert_not_reached ();
778 mono_thread_info_begin_resume (MonoThreadInfo *info)
780 return mono_thread_info_core_resume (info);
784 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
785 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
788 is_thread_in_critical_region (MonoThreadInfo *info)
792 gpointer stack_start;
793 MonoThreadUnwindState *state;
795 /* Are we inside a system critical region? */
796 if (info->inside_critical_region)
799 /* Are we inside a GC critical region? */
800 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
804 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
805 state = mono_thread_info_get_suspend_state (info);
806 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
809 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
810 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
811 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
814 ji = mono_jit_info_table_find (
815 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
816 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
821 method = mono_jit_info_get_method (ji);
823 return threads_callbacks.mono_method_is_critical (method);
827 mono_thread_info_in_critical_location (MonoThreadInfo *info)
829 return is_thread_in_critical_region (info);
832 static MonoThreadInfo*
833 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
835 MonoThreadInfo *info = NULL;
836 int sleep_duration = 0;
838 const char *suspend_error = "Unknown error";
839 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
840 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
844 /*WARNING: We now are in interrupt context until we resume the thread. */
845 if (!is_thread_in_critical_region (info))
848 if (!mono_thread_info_core_resume (info)) {
849 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
852 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
854 /* Wait for the pending resume to finish */
855 mono_threads_wait_pending_operations ();
857 if (!sleep_duration) {
865 g_usleep (sleep_duration);
867 sleep_duration += 10;
873 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
876 MonoThreadInfo *info = NULL;
877 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
879 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
880 /*FIXME: unify this with self-suspend*/
881 g_assert (id != mono_native_thread_id_get ());
883 /* This can block during stw */
884 mono_thread_info_suspend_lock ();
885 mono_threads_begin_global_suspend ();
887 info = suspend_sync_nolock (id, interrupt_kernel);
891 switch (result = callback (info, user_data)) {
892 case MonoResumeThread:
893 mono_hazard_pointer_set (hp, 1, info);
894 mono_thread_info_core_resume (info);
895 mono_threads_wait_pending_operations ();
900 g_error ("Invalid suspend_and_run callback return value %d", result);
904 mono_hazard_pointer_clear (hp, 1);
905 mono_threads_end_global_suspend ();
906 mono_thread_info_suspend_unlock ();
911 If we are trying to suspend a target that is on a critical region
912 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
913 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
915 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
917 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
920 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
922 MonoThreadInfo *info = NULL;
924 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
925 /*FIXME: unify this with self-suspend*/
926 g_assert (id != mono_native_thread_id_get ());
928 mono_thread_info_suspend_lock ();
929 mono_threads_begin_global_suspend ();
931 info = suspend_sync_nolock (id, interrupt_kernel);
933 /* XXX this clears HP 1, so we restated it again */
934 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
935 mono_threads_end_global_suspend ();
936 mono_thread_info_suspend_unlock ();
942 Inject an assynchronous call into the target thread. The target thread must be suspended and
943 only a single async call can be setup for a given suspend cycle.
944 This async call must cause stack unwinding as the current implementation doesn't save enough state
945 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
946 currently used only to deliver exceptions.
949 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
951 /* An async call can only be setup on an async suspended thread */
952 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
953 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
954 g_assert (!info->async_target);
955 info->async_target = target_func;
956 /* This is not GC tracked */
957 info->user_data = user_data;
961 The suspend lock is held during any suspend in progress.
962 A GC that has safepoints must take this lock as part of its
963 STW to make sure no unsafe pending suspend is in progress.
966 mono_thread_info_suspend_lock (void)
969 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
970 MONO_FINISH_TRY_BLOCKING;
974 mono_thread_info_suspend_unlock (void)
976 MONO_SEM_POST (&global_suspend_semaphore);
980 * This is a very specific function whose only purpose is to
981 * break a given thread from socket syscalls.
983 * This only exists because linux won't fail a call to connect
984 * if the underlying is closed.
986 * TODO We should cleanup and unify this with the other syscall abort
990 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
992 MonoThreadHazardPointers *hp;
993 MonoThreadInfo *info;
995 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
998 hp = mono_hazard_pointer_get ();
999 info = mono_thread_info_lookup (tid); /*info on HP1*/
1003 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
1004 mono_hazard_pointer_clear (hp, 1);
1008 mono_thread_info_suspend_lock ();
1009 mono_threads_begin_global_suspend ();
1011 mono_threads_core_abort_syscall (info);
1012 mono_threads_wait_pending_operations ();
1014 mono_hazard_pointer_clear (hp, 1);
1016 mono_threads_end_global_suspend ();
1017 mono_thread_info_suspend_unlock ();
1021 mono_thread_info_unified_management_enabled (void)
1023 return unified_suspend_enabled;
1027 * mono_thread_info_set_is_async_context:
1029 * Set whenever the current thread is in an async context. Some runtime functions might behave
1030 * differently while in an async context in order to be async safe.
1033 mono_thread_info_set_is_async_context (gboolean async_context)
1035 MonoThreadInfo *info = mono_thread_info_current ();
1038 info->is_async_context = async_context;
1042 mono_thread_info_is_async_context (void)
1044 MonoThreadInfo *info = mono_thread_info_current ();
1047 return info->is_async_context;
1053 * mono_threads_create_thread:
1055 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1056 * Returns: a windows or io-layer handle for the thread.
1059 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1061 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1065 * mono_thread_info_get_stack_bounds:
1067 * Return the address and size of the current threads stack. Return NULL as the
1068 * stack address if the stack address cannot be determined.
1071 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1073 guint8 *current = (guint8 *)&stsize;
1074 mono_threads_core_get_stack_bounds (staddr, stsize);
1078 /* Sanity check the result */
1079 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1081 /* When running under emacs, sometimes staddr is not aligned to a page size */
1082 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1086 mono_thread_info_yield (void)
1088 return mono_threads_core_yield ();
1092 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1094 return ((MonoThreadInfo*)info)->tls [key];
1098 * mono_threads_info_tls_set:
1100 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1101 * values of TLS variables for threads other than the current thread.
1102 * This should only be used for infrequently changing TLS variables, and it should
1103 * be paired with setting the real TLS variable since this provides no GC tracking.
1106 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1108 ((MonoThreadInfo*)info)->tls [key] = value;
1112 * mono_thread_info_exit:
1114 * Exit the current thread.
1115 * This function doesn't return.
1118 mono_thread_info_exit (void)
1120 mono_threads_core_exit (0);
1124 * mono_thread_info_open_handle:
1126 * Return a io-layer/win32 handle for the current thread.
1127 * The handle need to be closed by calling CloseHandle () when it is no
1131 mono_thread_info_open_handle (void)
1133 return mono_threads_core_open_handle ();
1137 * mono_threads_open_thread_handle:
1139 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1140 * The handle need to be closed by calling CloseHandle () when it is no
1144 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1146 return mono_threads_core_open_thread_handle (handle, tid);
1150 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1152 mono_threads_core_set_name (tid, name);
1155 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1157 struct _MonoThreadInfoInterruptToken {
1158 void (*callback) (gpointer data);
1163 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1165 * - @callback: must be able to be called from another thread and always cancel the wait
1166 * - @data: passed to the callback
1167 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1168 * if set to TRUE, it must mean that the thread is in interrupted state
1171 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1173 MonoThreadInfo *info;
1174 MonoThreadInfoInterruptToken *previous_token, *token;
1176 g_assert (callback);
1178 g_assert (interrupted);
1179 *interrupted = FALSE;
1181 info = mono_thread_info_current ();
1184 /* The memory of this token can be freed at 2 places:
1185 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1186 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1187 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1188 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1189 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1190 token->callback = callback;
1193 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1195 if (previous_token) {
1196 if (previous_token != INTERRUPT_STATE)
1197 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1201 *interrupted = TRUE;
1204 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1205 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1209 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1211 MonoThreadInfo *info;
1212 MonoThreadInfoInterruptToken *previous_token;
1214 g_assert (interrupted);
1215 *interrupted = FALSE;
1217 info = mono_thread_info_current ();
1220 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1222 /* only the installer can uninstall the token */
1223 g_assert (previous_token);
1225 if (previous_token == INTERRUPT_STATE) {
1226 /* if it is interrupted, then it is going to be freed in finish interrupt */
1227 *interrupted = TRUE;
1229 g_free (previous_token);
1232 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1233 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1236 static MonoThreadInfoInterruptToken*
1237 set_interrupt_state (MonoThreadInfo *info)
1239 MonoThreadInfoInterruptToken *token, *previous_token;
1243 /* Atomically obtain the token the thread is
1244 * waiting on, and change it to a flag value. */
1247 previous_token = info->interrupt_token;
1249 /* Already interrupted */
1250 if (previous_token == INTERRUPT_STATE) {
1255 token = previous_token;
1256 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1262 * mono_thread_info_prepare_interrupt:
1264 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1265 * - if the thread calls one of the WaitFor functions, the function will return with
1266 * WAIT_IO_COMPLETION instead of waiting
1267 * - if the thread was waiting when this function was called, the wait will be broken
1269 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1270 * didn't receive the interrupt signal yet, in this case it should call the wait function
1271 * again. This essentially means that the target thread will busy wait until it is ready to
1272 * process the interruption.
1274 MonoThreadInfoInterruptToken*
1275 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1277 MonoThreadInfoInterruptToken *token;
1279 token = set_interrupt_state (info);
1281 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1282 mono_thread_info_get_tid (info), token);
1288 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1290 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1295 g_assert (token->callback);
1297 token->callback (token->data);
1303 mono_thread_info_self_interrupt (void)
1305 MonoThreadInfo *info;
1306 MonoThreadInfoInterruptToken *token;
1308 info = mono_thread_info_current ();
1311 token = set_interrupt_state (info);
1314 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1315 mono_thread_info_get_tid (info));
1318 /* Clear the interrupted flag of the current thread, set with
1319 * mono_thread_info_self_interrupt, so it can wait again */
1321 mono_thread_info_clear_self_interrupt ()
1323 MonoThreadInfo *info;
1324 MonoThreadInfoInterruptToken *previous_token;
1326 info = mono_thread_info_current ();
1329 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1330 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1332 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1336 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1339 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1343 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1347 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1348 g_string_append_printf (text, "not waiting");
1349 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1350 g_string_append_printf (text, "interrupted state");
1352 g_string_append_printf (text, "waiting");
1355 /* info must be self or be held in a hazard pointer. */
1357 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1359 MonoAsyncJob old_job;
1361 old_job = (MonoAsyncJob) info->service_requests;
1364 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1369 mono_threads_consume_async_jobs (void)
1371 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1374 return (MonoAsyncJob) 0;
1376 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);