6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 /* enable pthread extensions */
17 #define _DARWIN_C_SOURCE
20 #include <mono/utils/mono-compiler.h>
21 #include <mono/utils/mono-os-semaphore.h>
22 #include <mono/utils/mono-threads.h>
23 #include <mono/utils/mono-tls.h>
24 #include <mono/utils/hazard-pointer.h>
25 #include <mono/utils/mono-memory-model.h>
26 #include <mono/utils/mono-mmap.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-lazy-init.h>
30 #include <mono/utils/mono-coop-mutex.h>
31 #include <mono/utils/mono-coop-semaphore.h>
32 #include <mono/utils/mono-threads-coop.h>
33 #include <mono/utils/mono-threads-debug.h>
34 #include <mono/utils/os-event.h>
35 #include <mono/utils/w32api.h>
40 #include <mono/utils/mach-support.h>
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
62 static MonoSemType global_suspend_semaphore;
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
69 static __thread guint32 tls_small_id;
71 static MonoNativeTlsKey small_id_key;
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
79 static mono_mutex_t join_mutex;
81 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
84 #define SLEEP_DURATION_BEFORE_WARNING (50)
86 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
88 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
89 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
91 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
94 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
96 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
97 InterlockedIncrement (&abort_posts);
98 mono_os_sem_post (&suspend_semaphore);
102 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
104 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
105 InterlockedIncrement (&suspend_posts);
106 mono_os_sem_post (&suspend_semaphore);
110 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
112 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
113 InterlockedIncrement (&resume_posts);
114 mono_os_sem_post (&suspend_semaphore);
118 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
120 if (mono_threads_is_coop_enabled ()) {
121 /* There's nothing else to do after we async request the thread to suspend */
122 mono_threads_add_to_pending_operation_set (info);
126 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
130 check_async_suspend (MonoThreadInfo *info)
132 if (mono_threads_is_coop_enabled ()) {
133 /* Async suspend can't async fail on coop */
137 return mono_threads_suspend_check_suspend_result (info);
141 resume_async_suspended (MonoThreadInfo *info)
143 if (mono_threads_is_coop_enabled ())
144 g_assert_not_reached ();
146 g_assert (mono_threads_suspend_begin_async_resume (info));
150 resume_self_suspended (MonoThreadInfo* info)
152 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
153 mono_os_sem_post (&info->resume_semaphore);
157 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
160 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
161 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
162 g_assert (res != -1);
166 resume_blocking_suspended (MonoThreadInfo* info)
168 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
169 mono_os_sem_post (&info->resume_semaphore);
173 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
175 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
177 InterlockedIncrement (&pending_ops);
181 mono_threads_begin_global_suspend (void)
183 size_t ps = pending_suspends;
184 if (G_UNLIKELY (ps != 0))
185 g_error ("pending_suspends = %d, but must be 0", ps);
186 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
187 abort_posts, waits_done, pending_ops);
188 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
189 mono_threads_coop_begin_global_suspend ();
193 mono_threads_end_global_suspend (void)
195 size_t ps = pending_suspends;
196 if (G_UNLIKELY (ps != 0))
197 g_error ("pending_suspends = %d, but must be 0", ps);
198 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
199 abort_posts, waits_done, pending_ops);
200 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
201 mono_threads_coop_end_global_suspend ();
207 MonoThreadInfo *cur = mono_thread_info_current ();
209 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
217 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
218 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
220 FOREACH_THREAD_SAFE (info) {
222 char thread_name [256] = { 0 };
223 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
225 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
227 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
229 } FOREACH_THREAD_SAFE_END
233 mono_threads_wait_pending_operations (void)
236 int c = pending_suspends;
238 /* Wait threads to park */
239 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
240 if (pending_suspends) {
241 MonoStopwatch suspension_time;
242 mono_stopwatch_start (&suspension_time);
243 for (i = 0; i < pending_suspends; ++i) {
244 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
245 InterlockedIncrement (&waits_done);
246 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
248 mono_stopwatch_stop (&suspension_time);
252 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
253 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
255 mono_stopwatch_stop (&suspension_time);
256 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
260 pending_suspends = 0;
266 //Thread initialization code
269 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
272 mono_hazard_pointer_clear (hp, 0);
274 mono_hazard_pointer_clear (hp, 1);
276 mono_hazard_pointer_clear (hp, 2);
280 If return non null Hazard Pointer 1 holds the return value.
283 mono_thread_info_lookup (MonoNativeThreadId id)
285 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
287 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
288 mono_hazard_pointer_clear_all (hp, -1);
292 mono_hazard_pointer_clear_all (hp, 1);
293 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
297 mono_thread_info_insert (MonoThreadInfo *info)
299 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
301 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
302 mono_hazard_pointer_clear_all (hp, -1);
306 mono_hazard_pointer_clear_all (hp, -1);
311 mono_thread_info_remove (MonoThreadInfo *info)
313 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
316 THREADS_DEBUG ("removing info %p\n", info);
317 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
318 mono_hazard_pointer_clear_all (hp, -1);
323 free_thread_info (gpointer mem)
325 MonoThreadInfo *info = (MonoThreadInfo *) mem;
327 mono_os_sem_destroy (&info->resume_semaphore);
328 mono_threads_suspend_free (info);
334 mono_thread_info_register_small_id (void)
336 int small_id = mono_thread_small_id_alloc ();
337 #ifdef HAVE_KW_THREAD
338 tls_small_id = small_id;
340 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
346 thread_handle_destroy (gpointer data)
348 MonoThreadHandle *thread_handle;
350 thread_handle = (MonoThreadHandle*) data;
352 mono_os_event_destroy (&thread_handle->event);
353 g_free (thread_handle);
357 register_thread (MonoThreadInfo *info)
360 guint8 *staddr = NULL;
363 info->small_id = mono_thread_info_register_small_id ();
364 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
366 info->handle = g_new0 (MonoThreadHandle, 1);
367 mono_refcount_init (info->handle, thread_handle_destroy);
368 mono_os_event_init (&info->handle->event, FALSE);
370 mono_os_sem_init (&info->resume_semaphore, 0);
372 /*set TLS early so SMR works */
373 mono_native_tls_set_value (thread_info_key, info);
375 mono_thread_info_get_stack_bounds (&staddr, &stsize);
378 info->stack_start_limit = staddr;
379 info->stack_end = staddr + stsize;
381 info->stackdata = g_byte_array_new ();
383 mono_threads_suspend_register (info);
385 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
387 if (threads_callbacks.thread_attach) {
388 if (!threads_callbacks.thread_attach (info)) {
389 // g_warning ("thread registation failed\n");
390 mono_native_tls_set_value (thread_info_key, NULL);
396 Transition it before taking any locks or publishing itself to reduce the chance
397 of others witnessing a detached thread.
398 We can reasonably expect that until this thread gets published, no other thread will
399 try to manipulate it.
401 mono_threads_transition_attach (info);
402 mono_thread_info_suspend_lock ();
403 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
404 result = mono_thread_info_insert (info);
406 mono_thread_info_suspend_unlock ();
412 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
415 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
418 unregister_thread (void *arg)
420 gpointer gc_unsafe_stackdata;
421 MonoThreadInfo *info;
426 info = (MonoThreadInfo *) arg;
428 g_assert (mono_thread_info_is_current (info));
429 g_assert (mono_thread_info_is_live (info));
431 /* Pump the HP queue while the thread is alive.*/
432 mono_thread_hazardous_try_free_some ();
434 small_id = info->small_id;
436 /* We only enter the GC unsafe region, as when exiting this function, the thread
437 * will be detached, and the current MonoThreadInfo* will be destroyed. */
438 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
440 THREADS_DEBUG ("unregistering info %p\n", info);
442 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
445 * TLS destruction order is not reliable so small_id might be cleaned up
448 #ifndef HAVE_KW_THREAD
449 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
452 /* we need to duplicate it, as the info->handle is going
453 * to be closed when unregistering from the platform */
454 handle = mono_threads_open_thread_handle (info->handle);
457 First perform the callback that requires no locks.
458 This callback has the potential of taking other locks, so we do it before.
459 After it completes, the thread remains functional.
461 if (threads_callbacks.thread_detach)
462 threads_callbacks.thread_detach (info);
464 mono_thread_info_suspend_lock_with_info (info);
467 Now perform the callback that must be done under locks.
468 This will render the thread useless and non-suspendable, so it must
469 be done while holding the suspend lock to give no other thread chance
472 if (threads_callbacks.thread_detach_with_lock)
473 threads_callbacks.thread_detach_with_lock (info);
475 /* The thread is no longer active, so unref its handle */
476 mono_threads_close_thread_handle (info->handle);
479 result = mono_thread_info_remove (info);
481 mono_threads_transition_detach (info);
483 mono_thread_info_suspend_unlock ();
485 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
487 /*now it's safe to free the thread info.*/
488 mono_thread_hazardous_try_free (info, free_thread_info);
490 mono_thread_small_id_free (small_id);
492 mono_threads_signal_thread_handle (handle);
494 mono_threads_close_thread_handle (handle);
496 mono_native_tls_set_value (thread_info_key, NULL);
500 thread_exited_dtor (void *arg)
502 #if defined(__MACH__)
504 * Since we use pthread dtors to clean up thread data, if a thread
505 * is attached to the runtime by another pthread dtor after our dtor
506 * has ran, it will never be detached, leading to various problems
507 * since the thread ids etc. will be reused while they are still in
508 * the threads hashtables etc.
509 * Dtors are called in a loop until all user tls entries are 0,
510 * but the loop has a maximum count (4), so if we set the tls
511 * variable every time, it will remain set when system tls dtors
512 * are ran. This allows mono_thread_info_is_exiting () to detect
513 * whenever the thread is exiting, even if it is executed from a
514 * system tls dtor (i.e. obj-c dealloc methods).
516 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
521 mono_thread_info_current_unchecked (void)
523 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
528 mono_thread_info_current (void)
530 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
534 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
537 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
538 The way to distinguish between before, during and after cleanup is the following:
540 -If the TLS key is set, cleanup has not begun;
541 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
542 -If the thread is nowhere to be found, cleanup has finished.
544 We cannot function after cleanup since there's no way to ensure what will happen.
548 /*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 */
549 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
555 mono_thread_info_get_small_id (void)
557 #ifdef HAVE_KW_THREAD
560 gpointer val = mono_native_tls_get_value (small_id_key);
563 return GPOINTER_TO_INT (val) - 1;
568 mono_thread_info_list_head (void)
574 * mono_threads_attach_tools_thread
576 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
578 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
579 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
582 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
583 * doing things like resolving backtraces in their background processing thread.
586 mono_threads_attach_tools_thread (void)
588 MonoThreadInfo *info;
590 /* Must only be called once */
591 g_assert (!mono_native_tls_get_value (thread_info_key));
593 while (!mono_threads_inited) {
594 mono_thread_info_usleep (10);
597 info = mono_thread_info_attach ();
600 info->tools_thread = TRUE;
604 mono_thread_info_attach (void)
606 MonoThreadInfo *info;
609 if (!mono_threads_inited)
611 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
612 * thread is created before an embedding API user initialized Mono. */
613 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
618 g_assert (mono_threads_inited);
620 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
622 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
623 THREADS_DEBUG ("attaching %p\n", info);
624 if (!register_thread (info)) {
634 mono_thread_info_detach (void)
636 MonoThreadInfo *info;
639 if (!mono_threads_inited)
641 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
642 * is created before an embedding API user initialized Mono. */
643 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
648 g_assert (mono_threads_inited);
650 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
652 THREADS_DEBUG ("detaching %p\n", info);
653 unregister_thread (info);
658 * mono_thread_info_is_exiting:
660 * Return whenever the current thread is exiting, i.e. it is running pthread
664 mono_thread_info_is_exiting (void)
666 #if defined(__MACH__)
667 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
675 thread_info_key_dtor (void *arg)
677 /* Put the MonoThreadInfo back for the duration of the
678 * unregister code. In some circumstances the thread needs to
679 * take the GC lock which may block which requires a coop
680 * state transition. */
681 mono_native_tls_set_value (thread_info_key, arg);
682 unregister_thread (arg);
683 mono_native_tls_set_value (thread_info_key, NULL);
688 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
691 threads_callbacks = *callbacks;
692 thread_info_size = info_size;
695 res = mono_native_tls_alloc (&thread_info_key, NULL);
696 res = mono_native_tls_alloc (&thread_exited_key, NULL);
698 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
699 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
704 #ifndef HAVE_KW_THREAD
705 res = mono_native_tls_alloc (&small_id_key, NULL);
709 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
711 long threshold = strtol(sleepLimit, NULL, 10);
712 if ((errno == 0) && (threshold >= 40)) {
713 sleepAbortDuration = threshold;
714 sleepWarnDuration = threshold / 20;
716 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
720 mono_os_sem_init (&global_suspend_semaphore, 1);
721 mono_os_sem_init (&suspend_semaphore, 0);
722 mono_os_mutex_init (&join_mutex);
724 mono_lls_init (&thread_list, NULL);
725 mono_thread_smr_init ();
726 mono_threads_suspend_init ();
727 mono_threads_coop_init ();
728 mono_threads_platform_init ();
730 #if defined(__MACH__)
731 mono_mach_init (thread_info_key);
734 mono_threads_inited = TRUE;
736 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
740 mono_threads_signals_init (void)
742 mono_threads_suspend_init_signals ();
746 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
748 runtime_callbacks = *callbacks;
751 MonoThreadInfoRuntimeCallbacks *
752 mono_threads_get_runtime_callbacks (void)
754 return &runtime_callbacks;
758 mono_thread_info_core_resume (MonoThreadInfo *info)
760 gboolean res = FALSE;
762 switch (mono_threads_transition_request_resume (info)) {
769 case ResumeInitSelfResume:
770 resume_self_suspended (info);
773 case ResumeInitAsyncResume:
774 resume_async_suspended (info);
777 case ResumeInitBlockingResume:
778 resume_blocking_suspended (info);
787 mono_thread_info_resume (MonoNativeThreadId tid)
789 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
790 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
791 MonoThreadInfo *info;
793 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
795 mono_thread_info_suspend_lock ();
797 info = mono_thread_info_lookup (tid); /*info on HP1*/
803 result = mono_thread_info_core_resume (info);
805 //Wait for the pending resume to finish
806 mono_threads_wait_pending_operations ();
809 mono_thread_info_suspend_unlock ();
810 mono_hazard_pointer_clear (hp, 1);
815 mono_thread_info_begin_suspend (MonoThreadInfo *info)
817 switch (mono_threads_transition_request_async_suspension (info)) {
818 case AsyncSuspendAlreadySuspended:
819 case AsyncSuspendBlocking:
821 case AsyncSuspendWait:
822 mono_threads_add_to_pending_operation_set (info);
824 case AsyncSuspendInitSuspend:
825 return begin_async_suspend (info, FALSE);
827 g_assert_not_reached ();
832 mono_thread_info_begin_resume (MonoThreadInfo *info)
834 return mono_thread_info_core_resume (info);
838 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
839 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
842 is_thread_in_critical_region (MonoThreadInfo *info)
846 gpointer stack_start;
847 MonoThreadUnwindState *state;
849 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
852 /* Are we inside a system critical region? */
853 if (info->inside_critical_region)
856 /* Are we inside a GC critical region? */
857 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
861 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
862 state = mono_thread_info_get_suspend_state (info);
863 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
866 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
867 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
868 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
871 if (threads_callbacks.ip_in_critical_region)
872 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
874 ji = mono_jit_info_table_find (
875 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
876 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
881 method = mono_jit_info_get_method (ji);
883 return threads_callbacks.mono_method_is_critical (method);
887 mono_thread_info_in_critical_location (MonoThreadInfo *info)
889 return is_thread_in_critical_region (info);
893 The return value is only valid until a matching mono_thread_info_resume is called
895 static MonoThreadInfo*
896 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
898 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
899 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
903 switch (mono_threads_transition_request_async_suspension (info)) {
904 case AsyncSuspendAlreadySuspended:
905 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
907 case AsyncSuspendWait:
908 mono_threads_add_to_pending_operation_set (info);
910 case AsyncSuspendInitSuspend:
911 if (!begin_async_suspend (info, interrupt_kernel)) {
912 mono_hazard_pointer_clear (hp, 1);
916 case AsyncSuspendBlocking:
917 if (interrupt_kernel)
918 mono_threads_suspend_abort_syscall (info);
922 g_assert_not_reached ();
925 //Wait for the pending suspend to finish
926 mono_threads_wait_pending_operations ();
928 if (!check_async_suspend (info)) {
929 mono_thread_info_core_resume (info);
930 mono_threads_wait_pending_operations ();
931 mono_hazard_pointer_clear (hp, 1);
937 static MonoThreadInfo*
938 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
940 MonoThreadInfo *info = NULL;
941 int sleep_duration = 0;
943 if (!(info = suspend_sync (id, interrupt_kernel))) {
944 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
948 /*WARNING: We now are in interrupt context until we resume the thread. */
949 if (!is_thread_in_critical_region (info))
952 if (!mono_thread_info_core_resume (info)) {
953 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
956 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
958 /* Wait for the pending resume to finish */
959 mono_threads_wait_pending_operations ();
961 if (sleep_duration == 0)
962 mono_thread_info_yield ();
964 g_usleep (sleep_duration);
966 sleep_duration += 10;
972 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
975 MonoThreadInfo *info = NULL;
976 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
978 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
979 /*FIXME: unify this with self-suspend*/
980 g_assert (id != mono_native_thread_id_get ());
982 /* This can block during stw */
983 mono_thread_info_suspend_lock ();
984 mono_threads_begin_global_suspend ();
986 info = suspend_sync_nolock (id, interrupt_kernel);
990 switch (result = callback (info, user_data)) {
991 case MonoResumeThread:
992 mono_hazard_pointer_set (hp, 1, info);
993 mono_thread_info_core_resume (info);
994 mono_threads_wait_pending_operations ();
997 g_assert (!mono_threads_is_coop_enabled ());
1000 g_error ("Invalid suspend_and_run callback return value %d", result);
1004 mono_hazard_pointer_clear (hp, 1);
1005 mono_threads_end_global_suspend ();
1006 mono_thread_info_suspend_unlock ();
1010 Inject an assynchronous call into the target thread. The target thread must be suspended and
1011 only a single async call can be setup for a given suspend cycle.
1012 This async call must cause stack unwinding as the current implementation doesn't save enough state
1013 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1014 currently used only to deliver exceptions.
1017 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1019 if (!mono_threads_is_coop_enabled ()) {
1020 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1021 * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1022 * region or entering a gc unsafe region */
1023 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1025 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1026 g_assert (!info->async_target);
1027 info->async_target = target_func;
1028 /* This is not GC tracked */
1029 info->user_data = user_data;
1033 The suspend lock is held during any suspend in progress.
1034 A GC that has safepoints must take this lock as part of its
1035 STW to make sure no unsafe pending suspend is in progress.
1039 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1041 if (mono_threads_is_coop_enabled ()) {
1043 g_assert (mono_thread_info_is_current (info));
1044 g_assert (mono_thread_info_is_live (info));
1046 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1048 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1049 g_assert (res != -1);
1051 MONO_EXIT_GC_SAFE_WITH_INFO;
1053 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1054 g_assert (res != -1);
1059 mono_thread_info_suspend_lock (void)
1061 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1065 mono_thread_info_suspend_unlock (void)
1067 mono_os_sem_post (&global_suspend_semaphore);
1071 * This is a very specific function whose only purpose is to
1072 * break a given thread from socket syscalls.
1074 * This only exists because linux won't fail a call to connect
1075 * if the underlying is closed.
1077 * TODO We should cleanup and unify this with the other syscall abort
1081 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1083 MonoThreadHazardPointers *hp;
1084 MonoThreadInfo *info;
1086 if (tid == mono_native_thread_id_get ())
1089 hp = mono_hazard_pointer_get ();
1090 info = mono_thread_info_lookup (tid);
1094 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1095 mono_hazard_pointer_clear (hp, 1);
1099 mono_thread_info_suspend_lock ();
1100 mono_threads_begin_global_suspend ();
1102 mono_threads_suspend_abort_syscall (info);
1103 mono_threads_wait_pending_operations ();
1105 mono_hazard_pointer_clear (hp, 1);
1107 mono_threads_end_global_suspend ();
1108 mono_thread_info_suspend_unlock ();
1112 * mono_thread_info_set_is_async_context:
1114 * Set whenever the current thread is in an async context. Some runtime functions might behave
1115 * differently while in an async context in order to be async safe.
1118 mono_thread_info_set_is_async_context (gboolean async_context)
1120 MonoThreadInfo *info = mono_thread_info_current ();
1123 info->is_async_context = async_context;
1127 mono_thread_info_is_async_context (void)
1129 MonoThreadInfo *info = mono_thread_info_current ();
1132 return info->is_async_context;
1138 * mono_thread_info_get_stack_bounds:
1140 * Return the address and size of the current threads stack. Return NULL as the
1141 * stack address if the stack address cannot be determined.
1144 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1146 guint8 *current = (guint8 *)&stsize;
1147 mono_threads_platform_get_stack_bounds (staddr, stsize);
1151 /* Sanity check the result */
1152 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1154 /* When running under emacs, sometimes staddr is not aligned to a page size */
1155 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1159 mono_thread_info_yield (void)
1161 return mono_threads_platform_yield ();
1164 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1165 static MonoCoopMutex sleep_mutex;
1166 static MonoCoopCond sleep_cond;
1169 sleep_initialize (void)
1171 mono_coop_mutex_init (&sleep_mutex);
1172 mono_coop_cond_init (&sleep_cond);
1176 sleep_interrupt (gpointer data)
1178 mono_coop_mutex_lock (&sleep_mutex);
1179 mono_coop_cond_broadcast (&sleep_cond);
1180 mono_coop_mutex_unlock (&sleep_mutex);
1183 static inline guint32
1184 sleep_interruptable (guint32 ms, gboolean *alerted)
1188 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1193 if (ms != MONO_INFINITE_WAIT)
1194 end = mono_msec_ticks() + ms;
1196 mono_lazy_initialize (&sleep_init, sleep_initialize);
1198 mono_coop_mutex_lock (&sleep_mutex);
1201 if (ms != MONO_INFINITE_WAIT) {
1202 now = mono_msec_ticks();
1207 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1209 mono_coop_mutex_unlock (&sleep_mutex);
1210 return WAIT_IO_COMPLETION;
1213 if (ms != MONO_INFINITE_WAIT)
1214 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1216 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1218 mono_thread_info_uninstall_interrupt (alerted);
1220 mono_coop_mutex_unlock (&sleep_mutex);
1221 return WAIT_IO_COMPLETION;
1225 mono_coop_mutex_unlock (&sleep_mutex);
1231 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1234 MonoThreadInfo *info;
1236 mono_thread_info_yield ();
1238 info = mono_thread_info_current ();
1239 if (info && mono_thread_info_is_interrupt_state (info))
1240 return WAIT_IO_COMPLETION;
1246 return sleep_interruptable (ms, alerted);
1250 if (ms == MONO_INFINITE_WAIT) {
1253 Sleep (G_MAXUINT32);
1255 sleep (G_MAXUINT32);
1260 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1261 struct timespec start, target;
1263 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1264 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1265 g_assert (ret == 0);
1268 target.tv_sec += ms / 1000;
1269 target.tv_nsec += (ms % 1000) * 1000000;
1270 if (target.tv_nsec > 999999999) {
1271 target.tv_nsec -= 999999999;
1276 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1281 struct timespec req, rem;
1283 req.tv_sec = ms / 1000;
1284 req.tv_nsec = (ms % 1000) * 1000000;
1287 memset (&rem, 0, sizeof (rem));
1288 ret = nanosleep (&req, &rem);
1290 #endif /* __linux__ */
1299 mono_thread_info_usleep (guint64 us)
1308 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1310 return ((MonoThreadInfo*)info)->tls [key];
1314 * mono_threads_info_tls_set:
1316 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1317 * values of TLS variables for threads other than the current thread.
1318 * This should only be used for infrequently changing TLS variables, and it should
1319 * be paired with setting the real TLS variable since this provides no GC tracking.
1322 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1324 ((MonoThreadInfo*)info)->tls [key] = value;
1328 * mono_thread_info_exit:
1330 * Exit the current thread.
1331 * This function doesn't return.
1334 mono_thread_info_exit (gsize exit_code)
1336 mono_thread_info_detach ();
1338 mono_threads_platform_exit (0);
1342 * mono_threads_open_thread_handle:
1344 * Duplicate the handle. The handle needs to be closed by calling
1345 * mono_threads_close_thread_handle () when it is no longer needed.
1348 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1350 return mono_refcount_inc (thread_handle);
1354 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1356 mono_refcount_dec (thread_handle);
1360 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1362 mono_os_event_set (&thread_handle->event);
1365 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1367 struct _MonoThreadInfoInterruptToken {
1368 void (*callback) (gpointer data);
1373 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1375 * - @callback: must be able to be called from another thread and always cancel the wait
1376 * - @data: passed to the callback
1377 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1378 * if set to TRUE, it must mean that the thread is in interrupted state
1381 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1383 MonoThreadInfo *info;
1384 MonoThreadInfoInterruptToken *previous_token, *token;
1386 g_assert (callback);
1388 g_assert (interrupted);
1389 *interrupted = FALSE;
1391 info = mono_thread_info_current ();
1394 /* The memory of this token can be freed at 2 places:
1395 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1396 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1397 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1398 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1399 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1400 token->callback = callback;
1403 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1405 if (previous_token) {
1406 if (previous_token != INTERRUPT_STATE)
1407 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1411 *interrupted = TRUE;
1414 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1415 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1419 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1421 MonoThreadInfo *info;
1422 MonoThreadInfoInterruptToken *previous_token;
1424 g_assert (interrupted);
1425 *interrupted = FALSE;
1427 info = mono_thread_info_current ();
1430 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1432 /* only the installer can uninstall the token */
1433 g_assert (previous_token);
1435 if (previous_token == INTERRUPT_STATE) {
1436 /* if it is interrupted, then it is going to be freed in finish interrupt */
1437 *interrupted = TRUE;
1439 g_free (previous_token);
1442 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1443 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1446 static MonoThreadInfoInterruptToken*
1447 set_interrupt_state (MonoThreadInfo *info)
1449 MonoThreadInfoInterruptToken *token, *previous_token;
1453 /* Atomically obtain the token the thread is
1454 * waiting on, and change it to a flag value. */
1457 previous_token = info->interrupt_token;
1459 /* Already interrupted */
1460 if (previous_token == INTERRUPT_STATE) {
1465 token = previous_token;
1466 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1472 * mono_thread_info_prepare_interrupt:
1474 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1475 * - if the thread calls one of the WaitFor functions, the function will return with
1476 * WAIT_IO_COMPLETION instead of waiting
1477 * - if the thread was waiting when this function was called, the wait will be broken
1479 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1480 * didn't receive the interrupt signal yet, in this case it should call the wait function
1481 * again. This essentially means that the target thread will busy wait until it is ready to
1482 * process the interruption.
1484 MonoThreadInfoInterruptToken*
1485 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1487 MonoThreadInfoInterruptToken *token;
1489 token = set_interrupt_state (info);
1491 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1492 mono_thread_info_get_tid (info), token);
1498 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1500 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1505 g_assert (token->callback);
1507 token->callback (token->data);
1513 mono_thread_info_self_interrupt (void)
1515 MonoThreadInfo *info;
1516 MonoThreadInfoInterruptToken *token;
1518 info = mono_thread_info_current ();
1521 token = set_interrupt_state (info);
1524 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1525 mono_thread_info_get_tid (info));
1528 /* Clear the interrupted flag of the current thread, set with
1529 * mono_thread_info_self_interrupt, so it can wait again */
1531 mono_thread_info_clear_self_interrupt ()
1533 MonoThreadInfo *info;
1534 MonoThreadInfoInterruptToken *previous_token;
1536 info = mono_thread_info_current ();
1539 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1540 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1542 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1546 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1549 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1553 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1557 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1558 g_string_append_printf (text, "not waiting");
1559 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1560 g_string_append_printf (text, "interrupted state");
1562 g_string_append_printf (text, "waiting");
1566 mono_thread_info_is_current (MonoThreadInfo *info)
1568 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1571 MonoThreadInfoWaitRet
1572 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1574 MonoOSEventWaitRet res;
1576 res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1577 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1578 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1579 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1580 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1581 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1582 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1584 g_error ("%s: unknown res value %d", __func__, res);
1587 MonoThreadInfoWaitRet
1588 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1590 MonoOSEventWaitRet res;
1591 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1594 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1595 if (background_change_event)
1596 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1598 for (i = 0; i < nhandles; ++i)
1599 thread_events [i] = &thread_handles [i]->event;
1601 if (background_change_event)
1602 thread_events [nhandles ++] = background_change_event;
1604 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1605 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1606 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1607 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1608 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1609 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1610 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1612 g_error ("%s: unknown res value %d", __func__, res);
1616 * mono_threads_join_mutex:
1618 * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1619 * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1620 * The code inside the lock should not block.
1623 mono_threads_join_lock (void)
1626 mono_os_mutex_lock (&join_mutex);
1631 mono_threads_join_unlock (void)
1634 mono_os_mutex_unlock (&join_mutex);