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 /* enable pthread extensions */
15 #define _DARWIN_C_SOURCE
18 #include <mono/utils/mono-compiler.h>
19 #include <mono/utils/mono-semaphore.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-lazy-init.h>
33 #include <mono/utils/mach-support.h>
37 Mutex that makes sure only a single thread can be suspending others.
38 Suspend is a very racy operation since it requires restarting until
39 the target thread is not on an unsafe region.
41 We could implement this using critical regions, but would be much much
42 harder for an operation that is hardly performance critical.
44 The GC has to acquire this lock before starting a STW to make sure
45 a runtime suspend won't make it wronly see a thread in a safepoint
46 when it is in fact not.
48 static MonoSemType global_suspend_semaphore;
50 static size_t thread_info_size;
51 static MonoThreadInfoCallbacks threads_callbacks;
52 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
53 static MonoNativeTlsKey thread_info_key, thread_exited_key;
55 static __thread guint32 tls_small_id MONO_TLS_FAST;
57 static MonoNativeTlsKey small_id_key;
59 static MonoLinkedListSet thread_list;
60 static gboolean mono_threads_inited = FALSE;
62 static MonoSemType suspend_semaphore;
63 static size_t pending_suspends;
65 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
68 #define SLEEP_DURATION_BEFORE_WARNING (10)
70 #define SLEEP_DURATION_BEFORE_ABORT 200
72 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
75 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
77 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
78 InterlockedIncrement (&abort_posts);
79 MONO_SEM_POST (&suspend_semaphore);
83 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
85 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
86 InterlockedIncrement (&suspend_posts);
87 MONO_SEM_POST (&suspend_semaphore);
91 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
93 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
94 InterlockedIncrement (&resume_posts);
95 MONO_SEM_POST (&suspend_semaphore);
99 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
101 if (mono_threads_is_coop_enabled ()) {
102 /* There's nothing else to do after we async request the thread to suspend */
103 mono_threads_add_to_pending_operation_set (info);
107 return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
111 check_async_suspend (MonoThreadInfo *info)
113 if (mono_threads_is_coop_enabled ()) {
114 /* Async suspend can't async fail on coop */
118 return mono_threads_core_check_suspend_result (info);
122 resume_async_suspended (MonoThreadInfo *info)
124 if (mono_threads_is_coop_enabled ())
125 g_assert_not_reached ();
127 g_assert (mono_threads_core_begin_async_resume (info));
131 resume_self_suspended (MonoThreadInfo* info)
133 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
134 MONO_SEM_POST (&info->resume_semaphore);
138 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
140 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
141 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
145 resume_blocking_suspended (MonoThreadInfo* info)
147 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
148 MONO_SEM_POST (&info->resume_semaphore);
152 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
154 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
156 InterlockedIncrement (&pending_ops);
160 mono_threads_begin_global_suspend (void)
162 size_t ps = pending_suspends;
163 if (G_UNLIKELY (ps != 0))
164 g_error ("pending_suspends = %d, but must be 0", ps);
165 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,
166 abort_posts, waits_done, pending_ops);
167 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
168 mono_threads_coop_begin_global_suspend ();
172 mono_threads_end_global_suspend (void)
174 size_t ps = pending_suspends;
175 if (G_UNLIKELY (ps != 0))
176 g_error ("pending_suspends = %d, but must be 0", ps);
177 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
178 abort_posts, waits_done, pending_ops);
179 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
180 mono_threads_coop_end_global_suspend ();
186 MonoThreadInfo *info;
187 MonoThreadInfo *cur = mono_thread_info_current ();
189 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
190 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
191 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
192 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
193 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
194 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
195 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
196 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
197 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
198 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
200 FOREACH_THREAD_SAFE (info) {
202 char thread_name [256] = { 0 };
203 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
205 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" : "" );
207 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" : "" );
210 } END_FOREACH_THREAD_SAFE
214 mono_threads_wait_pending_operations (void)
217 int c = pending_suspends;
219 /* Wait threads to park */
220 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
221 if (pending_suspends) {
222 MonoStopwatch suspension_time;
223 mono_stopwatch_start (&suspension_time);
224 for (i = 0; i < pending_suspends; ++i) {
225 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
226 InterlockedIncrement (&waits_done);
227 if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
229 mono_stopwatch_stop (&suspension_time);
233 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
234 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);
236 mono_stopwatch_stop (&suspension_time);
237 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
241 pending_suspends = 0;
247 //Thread initialization code
249 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
252 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
255 mono_hazard_pointer_clear (hp, 0);
257 mono_hazard_pointer_clear (hp, 1);
259 mono_hazard_pointer_clear (hp, 2);
263 If return non null Hazard Pointer 1 holds the return value.
266 mono_thread_info_lookup (MonoNativeThreadId id)
268 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
270 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
271 mono_hazard_pointer_clear_all (hp, -1);
275 mono_hazard_pointer_clear_all (hp, 1);
276 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
280 mono_thread_info_insert (MonoThreadInfo *info)
282 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
284 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
285 mono_hazard_pointer_clear_all (hp, -1);
289 mono_hazard_pointer_clear_all (hp, -1);
294 mono_thread_info_remove (MonoThreadInfo *info)
296 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
299 THREADS_DEBUG ("removing info %p\n", info);
300 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
301 mono_hazard_pointer_clear_all (hp, -1);
306 free_thread_info (gpointer mem)
308 MonoThreadInfo *info = (MonoThreadInfo *) mem;
310 MONO_SEM_DESTROY (&info->resume_semaphore);
311 mono_threads_platform_free (info);
317 mono_thread_info_register_small_id (void)
319 int small_id = mono_thread_small_id_alloc ();
320 #ifdef HAVE_KW_THREAD
321 tls_small_id = small_id;
323 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
329 register_thread (MonoThreadInfo *info, gpointer baseptr)
332 guint8 *staddr = NULL;
333 int small_id = mono_thread_info_register_small_id ();
335 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
336 info->small_id = small_id;
338 MONO_SEM_INIT (&info->resume_semaphore, 0);
340 /*set TLS early so SMR works */
341 mono_native_tls_set_value (thread_info_key, info);
343 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
345 if (threads_callbacks.thread_register) {
346 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
347 // g_warning ("thread registation failed\n");
353 mono_thread_info_get_stack_bounds (&staddr, &stsize);
356 info->stack_start_limit = staddr;
357 info->stack_end = staddr + stsize;
359 info->stackdata = g_byte_array_new ();
361 mono_threads_platform_register (info);
364 Transition it before taking any locks or publishing itself to reduce the chance
365 of others witnessing a detached thread.
366 We can reasonably expect that until this thread gets published, no other thread will
367 try to manipulate it.
369 mono_threads_transition_attach (info);
370 mono_thread_info_suspend_lock ();
371 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
372 result = mono_thread_info_insert (info);
374 mono_thread_info_suspend_unlock ();
379 unregister_thread (void *arg)
381 MonoThreadInfo *info = (MonoThreadInfo *) arg;
382 int small_id = info->small_id;
385 THREADS_DEBUG ("unregistering info %p\n", info);
387 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
389 mono_threads_core_unregister (info);
392 * TLS destruction order is not reliable so small_id might be cleaned up
395 #ifndef HAVE_KW_THREAD
396 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
400 First perform the callback that requires no locks.
401 This callback has the potential of taking other locks, so we do it before.
402 After it completes, the thread remains functional.
404 if (threads_callbacks.thread_detach)
405 threads_callbacks.thread_detach (info);
407 mono_thread_info_suspend_lock ();
410 Now perform the callback that must be done under locks.
411 This will render the thread useless and non-suspendable, so it must
412 be done while holding the suspend lock to give no other thread chance
415 if (threads_callbacks.thread_unregister)
416 threads_callbacks.thread_unregister (info);
417 mono_threads_unregister_current_thread (info);
418 mono_threads_transition_detach (info);
420 mono_thread_info_suspend_unlock ();
422 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
424 /*now it's safe to free the thread info.*/
425 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
426 mono_thread_small_id_free (small_id);
430 thread_exited_dtor (void *arg)
432 #if defined(__MACH__)
434 * Since we use pthread dtors to clean up thread data, if a thread
435 * is attached to the runtime by another pthread dtor after our dtor
436 * has ran, it will never be detached, leading to various problems
437 * since the thread ids etc. will be reused while they are still in
438 * the threads hashtables etc.
439 * Dtors are called in a loop until all user tls entries are 0,
440 * but the loop has a maximum count (4), so if we set the tls
441 * variable every time, it will remain set when system tls dtors
442 * are ran. This allows mono_thread_info_is_exiting () to detect
443 * whenever the thread is exiting, even if it is executed from a
444 * system tls dtor (i.e. obj-c dealloc methods).
446 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
451 * Removes the current thread from the thread list.
452 * This must be called from the thread unregister callback and nowhere else.
453 * The current thread must be passed as TLS might have already been cleaned up.
456 mono_threads_unregister_current_thread (MonoThreadInfo *info)
459 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
460 result = mono_thread_info_remove (info);
465 mono_thread_info_current_unchecked (void)
467 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
472 mono_thread_info_current (void)
474 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
478 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
481 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
482 The way to distinguish between before, during and after cleanup is the following:
484 -If the TLS key is set, cleanup has not begun;
485 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
486 -If the thread is nowhere to be found, cleanup has finished.
488 We cannot function after cleanup since there's no way to ensure what will happen.
492 /*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 */
493 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
499 mono_thread_info_get_small_id (void)
501 #ifdef HAVE_KW_THREAD
504 gpointer val = mono_native_tls_get_value (small_id_key);
507 return GPOINTER_TO_INT (val) - 1;
512 mono_thread_info_list_head (void)
518 * mono_threads_attach_tools_thread
520 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
522 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
523 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
526 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
527 * doing things like resolving backtraces in their background processing thread.
530 mono_threads_attach_tools_thread (void)
533 MonoThreadInfo *info;
535 /* Must only be called once */
536 g_assert (!mono_native_tls_get_value (thread_info_key));
538 while (!mono_threads_inited) {
542 info = mono_thread_info_attach (&dummy);
545 info->tools_thread = TRUE;
549 mono_thread_info_attach (void *baseptr)
551 MonoThreadInfo *info;
552 if (!mono_threads_inited)
555 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
556 * thread is created before an embedding API user initialized Mono. */
557 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
560 g_assert (mono_threads_inited);
563 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
565 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
566 THREADS_DEBUG ("attaching %p\n", info);
567 if (!register_thread (info, baseptr))
569 } else if (threads_callbacks.thread_attach) {
570 threads_callbacks.thread_attach (info);
576 mono_thread_info_detach (void)
578 MonoThreadInfo *info;
579 if (!mono_threads_inited)
581 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
582 * is created before an embedding API user initialized Mono. */
583 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
586 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
588 THREADS_DEBUG ("detaching %p\n", info);
589 unregister_thread (info);
590 mono_native_tls_set_value (thread_info_key, NULL);
595 * mono_thread_info_is_exiting:
597 * Return whenever the current thread is exiting, i.e. it is running pthread
601 mono_thread_info_is_exiting (void)
603 #if defined(__MACH__)
604 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
611 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
614 threads_callbacks = *callbacks;
615 thread_info_size = info_size;
617 res = mono_native_tls_alloc (&thread_info_key, NULL);
618 res = mono_native_tls_alloc (&thread_exited_key, NULL);
620 res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
621 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
626 #ifndef HAVE_KW_THREAD
627 res = mono_native_tls_alloc (&small_id_key, NULL);
631 MONO_SEM_INIT (&global_suspend_semaphore, 1);
632 MONO_SEM_INIT (&suspend_semaphore, 0);
634 mono_lls_init (&thread_list, NULL);
635 mono_thread_smr_init ();
636 mono_threads_init_platform ();
637 mono_threads_init_coop ();
638 mono_threads_init_abort_syscall ();
640 #if defined(__MACH__)
641 mono_mach_init (thread_info_key);
644 mono_threads_inited = TRUE;
646 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
650 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
652 runtime_callbacks = *callbacks;
655 MonoThreadInfoRuntimeCallbacks *
656 mono_threads_get_runtime_callbacks (void)
658 return &runtime_callbacks;
662 The return value is only valid until a matching mono_thread_info_resume is called
664 static MonoThreadInfo*
665 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel, const char **error_condition)
667 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
668 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
670 *error_condition = "Thread not found";
674 switch (mono_threads_transition_request_async_suspension (info)) {
675 case AsyncSuspendAlreadySuspended:
676 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
678 case AsyncSuspendWait:
679 mono_threads_add_to_pending_operation_set (info);
681 case AsyncSuspendInitSuspend:
682 if (!begin_async_suspend (info, interrupt_kernel)) {
683 mono_hazard_pointer_clear (hp, 1);
684 *error_condition = "Could not suspend thread";
689 //Wait for the pending suspend to finish
690 mono_threads_wait_pending_operations ();
692 if (!check_async_suspend (info)) {
693 mono_hazard_pointer_clear (hp, 1);
694 *error_condition = "Post suspend failed";
701 Signal that the current thread wants to be suspended.
702 This function can be called without holding the suspend lock held.
703 To finish suspending, call mono_suspend_check.
706 mono_thread_info_begin_self_suspend (void)
708 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
712 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
713 mono_threads_transition_request_self_suspension (info);
717 mono_thread_info_end_self_suspend (void)
719 MonoThreadInfo *info;
721 info = mono_thread_info_current ();
724 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
726 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
728 /* commit the saved state and notify others if needed */
729 switch (mono_threads_transition_state_poll (info)) {
730 case SelfSuspendResumed:
732 case SelfSuspendWait:
733 mono_thread_info_wait_for_resume (info);
735 case SelfSuspendNotifyAndWait:
736 mono_threads_notify_initiator_of_suspend (info);
737 mono_thread_info_wait_for_resume (info);
738 mono_threads_notify_initiator_of_resume (info);
744 mono_thread_info_core_resume (MonoThreadInfo *info)
746 gboolean res = FALSE;
747 if (info->create_suspended) {
748 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
749 /* 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 */
750 info->create_suspended = FALSE;
751 mono_threads_core_resume_created (info, tid);
755 switch (mono_threads_transition_request_resume (info)) {
762 case ResumeInitSelfResume:
763 resume_self_suspended (info);
766 case ResumeInitAsyncResume:
767 resume_async_suspended (info);
770 case ResumeInitBlockingResume:
771 resume_blocking_suspended (info);
780 mono_thread_info_resume (MonoNativeThreadId tid)
782 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
783 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
784 MonoThreadInfo *info;
786 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
788 mono_thread_info_suspend_lock ();
790 info = mono_thread_info_lookup (tid); /*info on HP1*/
796 result = mono_thread_info_core_resume (info);
798 //Wait for the pending resume to finish
799 mono_threads_wait_pending_operations ();
802 mono_thread_info_suspend_unlock ();
803 mono_hazard_pointer_clear (hp, 1);
808 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
810 switch (mono_threads_transition_request_async_suspension (info)) {
811 case AsyncSuspendAlreadySuspended:
813 case AsyncSuspendWait:
814 mono_threads_add_to_pending_operation_set (info);
816 case AsyncSuspendInitSuspend:
817 return begin_async_suspend (info, interrupt_kernel);
819 g_assert_not_reached ();
824 mono_thread_info_begin_resume (MonoThreadInfo *info)
826 return mono_thread_info_core_resume (info);
830 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
832 return check_async_suspend (info);
836 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
837 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
840 is_thread_in_critical_region (MonoThreadInfo *info)
844 gpointer stack_start;
845 MonoThreadUnwindState *state;
847 /* Are we inside a system critical region? */
848 if (info->inside_critical_region)
851 /* Are we inside a GC critical region? */
852 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
856 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
857 state = mono_thread_info_get_suspend_state (info);
858 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
861 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
862 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
863 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
866 ji = mono_jit_info_table_find (
867 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
868 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
873 method = mono_jit_info_get_method (ji);
875 return threads_callbacks.mono_method_is_critical (method);
879 mono_thread_info_in_critical_location (MonoThreadInfo *info)
881 return is_thread_in_critical_region (info);
884 static MonoThreadInfo*
885 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
887 MonoThreadInfo *info = NULL;
888 int sleep_duration = 0;
890 const char *suspend_error = "Unknown error";
891 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
892 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
896 /*WARNING: We now are in interrupt context until we resume the thread. */
897 if (!is_thread_in_critical_region (info))
900 if (!mono_thread_info_core_resume (info)) {
901 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
904 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
906 /* Wait for the pending resume to finish */
907 mono_threads_wait_pending_operations ();
909 if (!sleep_duration) {
917 g_usleep (sleep_duration);
919 sleep_duration += 10;
925 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
928 MonoThreadInfo *info = NULL;
929 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
931 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
932 /*FIXME: unify this with self-suspend*/
933 g_assert (id != mono_native_thread_id_get ());
935 /* This can block during stw */
936 mono_thread_info_suspend_lock ();
937 mono_threads_begin_global_suspend ();
939 info = suspend_sync_nolock (id, interrupt_kernel);
943 switch (result = callback (info, user_data)) {
944 case MonoResumeThread:
945 mono_hazard_pointer_set (hp, 1, info);
946 mono_thread_info_core_resume (info);
947 mono_threads_wait_pending_operations ();
952 g_error ("Invalid suspend_and_run callback return value %d", result);
956 mono_hazard_pointer_clear (hp, 1);
957 mono_threads_end_global_suspend ();
958 mono_thread_info_suspend_unlock ();
963 If we are trying to suspend a target that is on a critical region
964 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
965 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
967 Info is not put on a hazard pointer as a suspended thread cannot exit and be freed.
969 This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume
972 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
974 MonoThreadInfo *info = NULL;
976 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
977 /*FIXME: unify this with self-suspend*/
978 g_assert (id != mono_native_thread_id_get ());
980 mono_thread_info_suspend_lock ();
981 mono_threads_begin_global_suspend ();
983 info = suspend_sync_nolock (id, interrupt_kernel);
985 /* XXX this clears HP 1, so we restated it again */
986 // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
987 mono_threads_end_global_suspend ();
988 mono_thread_info_suspend_unlock ();
994 Inject an assynchronous call into the target thread. The target thread must be suspended and
995 only a single async call can be setup for a given suspend cycle.
996 This async call must cause stack unwinding as the current implementation doesn't save enough state
997 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
998 currently used only to deliver exceptions.
1001 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1003 /* An async call can only be setup on an async suspended thread */
1004 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1005 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1006 g_assert (!info->async_target);
1007 info->async_target = target_func;
1008 /* This is not GC tracked */
1009 info->user_data = user_data;
1013 The suspend lock is held during any suspend in progress.
1014 A GC that has safepoints must take this lock as part of its
1015 STW to make sure no unsafe pending suspend is in progress.
1018 mono_thread_info_suspend_lock (void)
1021 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
1022 MONO_FINISH_TRY_BLOCKING;
1026 mono_thread_info_suspend_unlock (void)
1028 MONO_SEM_POST (&global_suspend_semaphore);
1032 * This is a very specific function whose only purpose is to
1033 * break a given thread from socket syscalls.
1035 * This only exists because linux won't fail a call to connect
1036 * if the underlying is closed.
1038 * TODO We should cleanup and unify this with the other syscall abort
1042 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1044 MonoThreadHazardPointers *hp;
1045 MonoThreadInfo *info;
1047 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1050 hp = mono_hazard_pointer_get ();
1051 info = mono_thread_info_lookup (tid);
1055 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1056 mono_hazard_pointer_clear (hp, 1);
1060 mono_thread_info_suspend_lock ();
1061 mono_threads_begin_global_suspend ();
1063 mono_threads_core_abort_syscall (info);
1064 mono_threads_wait_pending_operations ();
1066 mono_hazard_pointer_clear (hp, 1);
1068 mono_threads_end_global_suspend ();
1069 mono_thread_info_suspend_unlock ();
1073 * mono_thread_info_set_is_async_context:
1075 * Set whenever the current thread is in an async context. Some runtime functions might behave
1076 * differently while in an async context in order to be async safe.
1079 mono_thread_info_set_is_async_context (gboolean async_context)
1081 MonoThreadInfo *info = mono_thread_info_current ();
1084 info->is_async_context = async_context;
1088 mono_thread_info_is_async_context (void)
1090 MonoThreadInfo *info = mono_thread_info_current ();
1093 return info->is_async_context;
1099 * mono_threads_create_thread:
1101 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1102 * Returns: a windows or io-layer handle for the thread.
1105 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1107 return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1111 * mono_thread_info_get_stack_bounds:
1113 * Return the address and size of the current threads stack. Return NULL as the
1114 * stack address if the stack address cannot be determined.
1117 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1119 guint8 *current = (guint8 *)&stsize;
1120 mono_threads_core_get_stack_bounds (staddr, stsize);
1124 /* Sanity check the result */
1125 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1127 /* When running under emacs, sometimes staddr is not aligned to a page size */
1128 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1132 mono_thread_info_yield (void)
1134 return mono_threads_core_yield ();
1136 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1137 static mono_mutex_t sleep_mutex;
1138 static mono_cond_t sleep_cond;
1141 sleep_initialize (void)
1143 mono_mutex_init (&sleep_mutex);
1144 mono_cond_init (&sleep_cond, NULL);
1148 sleep_interrupt (gpointer data)
1150 mono_mutex_lock (&sleep_mutex);
1151 mono_cond_broadcast (&sleep_cond);
1152 mono_mutex_unlock (&sleep_mutex);
1155 static inline guint32
1156 sleep_interruptable (guint32 ms, gboolean *alerted)
1158 guint32 start, now, end;
1160 g_assert (INFINITE == G_MAXUINT32);
1165 start = mono_msec_ticks ();
1167 if (start < G_MAXUINT32 - ms) {
1170 /* start + ms would overflow guint32 */
1174 mono_lazy_initialize (&sleep_init, sleep_initialize);
1176 mono_mutex_lock (&sleep_mutex);
1178 for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
1179 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1181 mono_mutex_unlock (&sleep_mutex);
1182 return WAIT_IO_COMPLETION;
1186 mono_cond_timedwait_ms (&sleep_cond, &sleep_mutex, end - now);
1188 mono_cond_wait (&sleep_cond, &sleep_mutex);
1190 mono_thread_info_uninstall_interrupt (alerted);
1192 mono_mutex_unlock (&sleep_mutex);
1193 return WAIT_IO_COMPLETION;
1197 mono_mutex_unlock (&sleep_mutex);
1203 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1206 MonoThreadInfo *info;
1208 mono_thread_info_yield ();
1210 info = mono_thread_info_current ();
1211 if (info && mono_thread_info_is_interrupt_state (info))
1212 return WAIT_IO_COMPLETION;
1218 return sleep_interruptable (ms, alerted);
1220 if (ms == INFINITE) {
1223 Sleep (G_MAXUINT32);
1225 sleep (G_MAXUINT32);
1230 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1231 struct timespec start, target;
1233 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1234 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1235 g_assert (ret == 0);
1238 target.tv_sec += ms / 1000;
1239 target.tv_nsec += (ms % 1000) * 1000000;
1240 if (target.tv_nsec > 999999999) {
1241 target.tv_nsec -= 999999999;
1246 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1251 struct timespec req, rem;
1253 req.tv_sec = ms / 1000;
1254 req.tv_nsec = (ms % 1000) * 1000000;
1257 memset (&rem, 0, sizeof (rem));
1258 ret = nanosleep (&req, &rem);
1260 #endif /* __linux__ */
1267 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1269 return ((MonoThreadInfo*)info)->tls [key];
1273 * mono_threads_info_tls_set:
1275 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1276 * values of TLS variables for threads other than the current thread.
1277 * This should only be used for infrequently changing TLS variables, and it should
1278 * be paired with setting the real TLS variable since this provides no GC tracking.
1281 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1283 ((MonoThreadInfo*)info)->tls [key] = value;
1287 * mono_thread_info_exit:
1289 * Exit the current thread.
1290 * This function doesn't return.
1293 mono_thread_info_exit (void)
1295 mono_threads_core_exit (0);
1299 * mono_thread_info_open_handle:
1301 * Return a io-layer/win32 handle for the current thread.
1302 * The handle need to be closed by calling CloseHandle () when it is no
1306 mono_thread_info_open_handle (void)
1308 return mono_threads_core_open_handle ();
1312 * mono_threads_open_thread_handle:
1314 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1315 * The handle need to be closed by calling CloseHandle () when it is no
1319 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1321 return mono_threads_core_open_thread_handle (handle, tid);
1325 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1327 mono_threads_core_set_name (tid, name);
1330 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1332 struct _MonoThreadInfoInterruptToken {
1333 void (*callback) (gpointer data);
1338 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1340 * - @callback: must be able to be called from another thread and always cancel the wait
1341 * - @data: passed to the callback
1342 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1343 * if set to TRUE, it must mean that the thread is in interrupted state
1346 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1348 MonoThreadInfo *info;
1349 MonoThreadInfoInterruptToken *previous_token, *token;
1351 g_assert (callback);
1353 g_assert (interrupted);
1354 *interrupted = FALSE;
1356 info = mono_thread_info_current ();
1359 /* The memory of this token can be freed at 2 places:
1360 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1361 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1362 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1363 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1364 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1365 token->callback = callback;
1368 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1370 if (previous_token) {
1371 if (previous_token != INTERRUPT_STATE)
1372 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1376 *interrupted = TRUE;
1379 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1380 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1384 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1386 MonoThreadInfo *info;
1387 MonoThreadInfoInterruptToken *previous_token;
1389 g_assert (interrupted);
1390 *interrupted = FALSE;
1392 info = mono_thread_info_current ();
1395 previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1397 /* only the installer can uninstall the token */
1398 g_assert (previous_token);
1400 if (previous_token == INTERRUPT_STATE) {
1401 /* if it is interrupted, then it is going to be freed in finish interrupt */
1402 *interrupted = TRUE;
1404 g_free (previous_token);
1407 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1408 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1411 static MonoThreadInfoInterruptToken*
1412 set_interrupt_state (MonoThreadInfo *info)
1414 MonoThreadInfoInterruptToken *token, *previous_token;
1418 /* Atomically obtain the token the thread is
1419 * waiting on, and change it to a flag value. */
1422 previous_token = info->interrupt_token;
1424 /* Already interrupted */
1425 if (previous_token == INTERRUPT_STATE) {
1430 token = previous_token;
1431 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1437 * mono_thread_info_prepare_interrupt:
1439 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1440 * - if the thread calls one of the WaitFor functions, the function will return with
1441 * WAIT_IO_COMPLETION instead of waiting
1442 * - if the thread was waiting when this function was called, the wait will be broken
1444 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1445 * didn't receive the interrupt signal yet, in this case it should call the wait function
1446 * again. This essentially means that the target thread will busy wait until it is ready to
1447 * process the interruption.
1449 MonoThreadInfoInterruptToken*
1450 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1452 MonoThreadInfoInterruptToken *token;
1454 token = set_interrupt_state (info);
1456 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1457 mono_thread_info_get_tid (info), token);
1463 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1465 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1470 g_assert (token->callback);
1472 token->callback (token->data);
1478 mono_thread_info_self_interrupt (void)
1480 MonoThreadInfo *info;
1481 MonoThreadInfoInterruptToken *token;
1483 info = mono_thread_info_current ();
1486 token = set_interrupt_state (info);
1489 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1490 mono_thread_info_get_tid (info));
1493 /* Clear the interrupted flag of the current thread, set with
1494 * mono_thread_info_self_interrupt, so it can wait again */
1496 mono_thread_info_clear_self_interrupt ()
1498 MonoThreadInfo *info;
1499 MonoThreadInfoInterruptToken *previous_token;
1501 info = mono_thread_info_current ();
1504 previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1505 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1507 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1511 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1514 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1518 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1522 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1523 g_string_append_printf (text, "not waiting");
1524 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1525 g_string_append_printf (text, "interrupted state");
1527 g_string_append_printf (text, "waiting");
1530 /* info must be self or be held in a hazard pointer. */
1532 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1534 MonoAsyncJob old_job;
1536 old_job = (MonoAsyncJob) info->service_requests;
1539 } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1544 mono_threads_consume_async_jobs (void)
1546 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1549 return (MonoAsyncJob) 0;
1551 return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);