2 * mono-threads.c: Low-level threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 /* enable pthread extensions */
16 #define _DARWIN_C_SOURCE
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-os-semaphore.h>
21 #include <mono/utils/mono-threads.h>
22 #include <mono/utils/mono-tls.h>
23 #include <mono/utils/hazard-pointer.h>
24 #include <mono/utils/mono-memory-model.h>
25 #include <mono/utils/mono-mmap.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-time.h>
28 #include <mono/utils/mono-lazy-init.h>
29 #include <mono/utils/mono-coop-mutex.h>
30 #include <mono/utils/mono-coop-semaphore.h>
31 #include <mono/utils/mono-threads-coop.h>
36 #include <mono/utils/mach-support.h>
40 Mutex that makes sure only a single thread can be suspending others.
41 Suspend is a very racy operation since it requires restarting until
42 the target thread is not on an unsafe region.
44 We could implement this using critical regions, but would be much much
45 harder for an operation that is hardly performance critical.
47 The GC has to acquire this lock before starting a STW to make sure
48 a runtime suspend won't make it wronly see a thread in a safepoint
49 when it is in fact not.
51 This has to be a naked locking primitive, and not a coop aware one, as
52 it needs to be usable when destroying thread_info_key, the TLS key for
53 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
54 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
55 to an assertion error. We then simply switch state manually in
56 mono_thread_info_suspend_lock_with_info.
58 static MonoSemType global_suspend_semaphore;
60 static size_t thread_info_size;
61 static MonoThreadInfoCallbacks threads_callbacks;
62 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
63 static MonoNativeTlsKey thread_info_key, thread_exited_key;
65 static __thread guint32 tls_small_id MONO_TLS_FAST;
67 static MonoNativeTlsKey small_id_key;
69 static MonoLinkedListSet thread_list;
70 static gboolean mono_threads_inited = FALSE;
72 static MonoSemType suspend_semaphore;
73 static size_t pending_suspends;
74 static gboolean unified_suspend_enabled;
76 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
79 #define SLEEP_DURATION_BEFORE_WARNING (50)
81 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
83 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
84 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
86 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
89 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
91 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
92 InterlockedIncrement (&abort_posts);
93 mono_os_sem_post (&suspend_semaphore);
97 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
99 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
100 InterlockedIncrement (&suspend_posts);
101 mono_os_sem_post (&suspend_semaphore);
105 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
107 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
108 InterlockedIncrement (&resume_posts);
109 mono_os_sem_post (&suspend_semaphore);
113 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
115 if (mono_threads_is_coop_enabled ()) {
116 /* There's nothing else to do after we async request the thread to suspend */
117 mono_threads_add_to_pending_operation_set (info);
121 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
125 check_async_suspend (MonoThreadInfo *info)
127 if (mono_threads_is_coop_enabled ()) {
128 /* Async suspend can't async fail on coop */
132 return mono_threads_suspend_check_suspend_result (info);
136 resume_async_suspended (MonoThreadInfo *info)
138 if (mono_threads_is_coop_enabled ())
139 g_assert_not_reached ();
141 g_assert (mono_threads_suspend_begin_async_resume (info));
145 resume_self_suspended (MonoThreadInfo* info)
147 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
148 mono_os_sem_post (&info->resume_semaphore);
152 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
155 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
156 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
157 g_assert (res != -1);
161 resume_blocking_suspended (MonoThreadInfo* info)
163 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
164 mono_os_sem_post (&info->resume_semaphore);
168 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
170 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
172 InterlockedIncrement (&pending_ops);
176 mono_threads_begin_global_suspend (void)
178 size_t ps = pending_suspends;
179 if (G_UNLIKELY (ps != 0))
180 g_error ("pending_suspends = %d, but must be 0", ps);
181 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,
182 abort_posts, waits_done, pending_ops);
183 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
184 mono_threads_coop_begin_global_suspend ();
188 mono_threads_end_global_suspend (void)
190 size_t ps = pending_suspends;
191 if (G_UNLIKELY (ps != 0))
192 g_error ("pending_suspends = %d, but must be 0", ps);
193 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
194 abort_posts, waits_done, pending_ops);
195 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
196 mono_threads_coop_end_global_suspend ();
202 MonoThreadInfo *cur = mono_thread_info_current ();
204 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
205 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
206 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
207 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
215 FOREACH_THREAD_SAFE (info) {
217 char thread_name [256] = { 0 };
218 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
220 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" : "" );
222 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" : "" );
224 } FOREACH_THREAD_SAFE_END
228 mono_threads_wait_pending_operations (void)
231 int c = pending_suspends;
233 /* Wait threads to park */
234 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
235 if (pending_suspends) {
236 MonoStopwatch suspension_time;
237 mono_stopwatch_start (&suspension_time);
238 for (i = 0; i < pending_suspends; ++i) {
239 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
240 InterlockedIncrement (&waits_done);
241 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
243 mono_stopwatch_stop (&suspension_time);
247 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
248 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
250 mono_stopwatch_stop (&suspension_time);
251 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
255 pending_suspends = 0;
261 //Thread initialization code
263 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
266 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
269 mono_hazard_pointer_clear (hp, 0);
271 mono_hazard_pointer_clear (hp, 1);
273 mono_hazard_pointer_clear (hp, 2);
277 If return non null Hazard Pointer 1 holds the return value.
280 mono_thread_info_lookup (MonoNativeThreadId id)
282 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
284 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
285 mono_hazard_pointer_clear_all (hp, -1);
289 mono_hazard_pointer_clear_all (hp, 1);
290 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
294 mono_thread_info_insert (MonoThreadInfo *info)
296 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
298 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
299 mono_hazard_pointer_clear_all (hp, -1);
303 mono_hazard_pointer_clear_all (hp, -1);
308 mono_thread_info_remove (MonoThreadInfo *info)
310 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
313 THREADS_DEBUG ("removing info %p\n", info);
314 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
315 mono_hazard_pointer_clear_all (hp, -1);
320 free_thread_info (gpointer mem)
322 MonoThreadInfo *info = (MonoThreadInfo *) mem;
324 mono_os_sem_destroy (&info->resume_semaphore);
325 mono_threads_suspend_free (info);
331 mono_thread_info_register_small_id (void)
333 int small_id = mono_thread_small_id_alloc ();
334 #ifdef HAVE_KW_THREAD
335 tls_small_id = small_id;
337 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
343 register_thread (MonoThreadInfo *info, gpointer baseptr)
346 guint8 *staddr = NULL;
347 int small_id = mono_thread_info_register_small_id ();
349 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
350 info->small_id = small_id;
352 mono_os_sem_init (&info->resume_semaphore, 0);
354 /*set TLS early so SMR works */
355 mono_native_tls_set_value (thread_info_key, info);
357 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
359 if (threads_callbacks.thread_register) {
360 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
361 // g_warning ("thread registation failed\n");
362 mono_native_tls_set_value (thread_info_key, NULL);
368 mono_thread_info_get_stack_bounds (&staddr, &stsize);
371 info->stack_start_limit = staddr;
372 info->stack_end = staddr + stsize;
374 info->stackdata = g_byte_array_new ();
376 mono_threads_platform_register (info);
377 mono_threads_suspend_register (info);
380 Transition it before taking any locks or publishing itself to reduce the chance
381 of others witnessing a detached thread.
382 We can reasonably expect that until this thread gets published, no other thread will
383 try to manipulate it.
385 mono_threads_transition_attach (info);
386 mono_thread_info_suspend_lock ();
387 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
388 result = mono_thread_info_insert (info);
390 mono_thread_info_suspend_unlock ();
395 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
398 unregister_thread (void *arg)
400 gpointer gc_unsafe_stackdata;
401 MonoThreadInfo *info;
404 info = (MonoThreadInfo *) arg;
406 g_assert (mono_thread_info_is_current (info));
407 g_assert (mono_thread_info_is_live (info));
409 small_id = info->small_id;
411 /* We only enter the GC unsafe region, as when exiting this function, the thread
412 * will be detached, and the current MonoThreadInfo* will be destroyed. */
413 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
415 THREADS_DEBUG ("unregistering info %p\n", info);
417 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
419 mono_threads_platform_unregister (info);
422 * TLS destruction order is not reliable so small_id might be cleaned up
425 #ifndef HAVE_KW_THREAD
426 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
430 First perform the callback that requires no locks.
431 This callback has the potential of taking other locks, so we do it before.
432 After it completes, the thread remains functional.
434 if (threads_callbacks.thread_detach)
435 threads_callbacks.thread_detach (info);
437 mono_thread_info_suspend_lock_with_info (info);
440 Now perform the callback that must be done under locks.
441 This will render the thread useless and non-suspendable, so it must
442 be done while holding the suspend lock to give no other thread chance
445 if (threads_callbacks.thread_unregister)
446 threads_callbacks.thread_unregister (info);
447 mono_threads_unregister_current_thread (info);
448 mono_threads_transition_detach (info);
450 mono_thread_info_suspend_unlock ();
452 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
454 /*now it's safe to free the thread info.*/
455 mono_thread_hazardous_try_free (info, free_thread_info);
456 /* Pump the HP queue */
457 mono_thread_hazardous_try_free_some ();
459 mono_thread_small_id_free (small_id);
463 thread_exited_dtor (void *arg)
465 #if defined(__MACH__)
467 * Since we use pthread dtors to clean up thread data, if a thread
468 * is attached to the runtime by another pthread dtor after our dtor
469 * has ran, it will never be detached, leading to various problems
470 * since the thread ids etc. will be reused while they are still in
471 * the threads hashtables etc.
472 * Dtors are called in a loop until all user tls entries are 0,
473 * but the loop has a maximum count (4), so if we set the tls
474 * variable every time, it will remain set when system tls dtors
475 * are ran. This allows mono_thread_info_is_exiting () to detect
476 * whenever the thread is exiting, even if it is executed from a
477 * system tls dtor (i.e. obj-c dealloc methods).
479 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
484 * Removes the current thread from the thread list.
485 * This must be called from the thread unregister callback and nowhere else.
486 * The current thread must be passed as TLS might have already been cleaned up.
489 mono_threads_unregister_current_thread (MonoThreadInfo *info)
492 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
493 result = mono_thread_info_remove (info);
498 mono_thread_info_current_unchecked (void)
500 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
505 mono_thread_info_current (void)
507 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
511 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
514 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
515 The way to distinguish between before, during and after cleanup is the following:
517 -If the TLS key is set, cleanup has not begun;
518 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
519 -If the thread is nowhere to be found, cleanup has finished.
521 We cannot function after cleanup since there's no way to ensure what will happen.
525 /*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 */
526 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
532 mono_thread_info_get_small_id (void)
534 #ifdef HAVE_KW_THREAD
537 gpointer val = mono_native_tls_get_value (small_id_key);
540 return GPOINTER_TO_INT (val) - 1;
545 mono_thread_info_list_head (void)
551 * mono_threads_attach_tools_thread
553 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
555 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
556 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
559 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
560 * doing things like resolving backtraces in their background processing thread.
563 mono_threads_attach_tools_thread (void)
566 MonoThreadInfo *info;
568 /* Must only be called once */
569 g_assert (!mono_native_tls_get_value (thread_info_key));
571 while (!mono_threads_inited) {
572 mono_thread_info_usleep (10);
575 info = mono_thread_info_attach (&dummy);
578 info->tools_thread = TRUE;
582 mono_thread_info_attach (void *baseptr)
584 MonoThreadInfo *info;
585 if (!mono_threads_inited)
588 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
589 * thread is created before an embedding API user initialized Mono. */
590 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
593 g_assert (mono_threads_inited);
596 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
598 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
599 THREADS_DEBUG ("attaching %p\n", info);
600 if (!register_thread (info, baseptr))
602 } else if (threads_callbacks.thread_attach) {
603 threads_callbacks.thread_attach (info);
609 mono_thread_info_detach (void)
611 MonoThreadInfo *info;
612 if (!mono_threads_inited)
614 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
615 * is created before an embedding API user initialized Mono. */
616 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
619 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
621 THREADS_DEBUG ("detaching %p\n", info);
622 unregister_thread (info);
623 mono_native_tls_set_value (thread_info_key, NULL);
628 * mono_thread_info_is_exiting:
630 * Return whenever the current thread is exiting, i.e. it is running pthread
634 mono_thread_info_is_exiting (void)
636 #if defined(__MACH__)
637 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
645 thread_info_key_dtor (void *arg)
647 /* Put the MonoThreadInfo back for the duration of the
648 * unregister code. In some circumstances the thread needs to
649 * take the GC lock which may block which requires a coop
650 * state transition. */
651 mono_native_tls_set_value (thread_info_key, arg);
652 unregister_thread (arg);
653 mono_native_tls_set_value (thread_info_key, NULL);
658 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
661 threads_callbacks = *callbacks;
662 thread_info_size = info_size;
663 const char *sleepLimit;
665 res = mono_native_tls_alloc (&thread_info_key, NULL);
666 res = mono_native_tls_alloc (&thread_exited_key, NULL);
668 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
669 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
674 #ifndef HAVE_KW_THREAD
675 res = mono_native_tls_alloc (&small_id_key, NULL);
679 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
681 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
683 long threshold = strtol(sleepLimit, NULL, 10);
684 if ((errno == 0) && (threshold >= 40)) {
685 sleepAbortDuration = threshold;
686 sleepWarnDuration = threshold / 20;
688 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
691 mono_os_sem_init (&global_suspend_semaphore, 1);
692 mono_os_sem_init (&suspend_semaphore, 0);
694 mono_lls_init (&thread_list, NULL);
695 mono_thread_smr_init ();
696 mono_threads_platform_init ();
697 mono_threads_suspend_init ();
698 mono_threads_coop_init ();
699 mono_threads_abort_syscall_init ();
701 #if defined(__MACH__)
702 mono_mach_init (thread_info_key);
705 mono_threads_inited = TRUE;
707 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
711 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
713 runtime_callbacks = *callbacks;
716 MonoThreadInfoRuntimeCallbacks *
717 mono_threads_get_runtime_callbacks (void)
719 return &runtime_callbacks;
723 Signal that the current thread wants to be suspended.
724 This function can be called without holding the suspend lock held.
725 To finish suspending, call mono_suspend_check.
728 mono_thread_info_begin_self_suspend (void)
730 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
734 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
735 mono_threads_transition_request_self_suspension (info);
739 mono_thread_info_end_self_suspend (void)
741 MonoThreadInfo *info;
743 info = mono_thread_info_current ();
746 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
748 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
750 /* commit the saved state and notify others if needed */
751 switch (mono_threads_transition_state_poll (info)) {
752 case SelfSuspendResumed:
754 case SelfSuspendWait:
755 mono_thread_info_wait_for_resume (info);
757 case SelfSuspendNotifyAndWait:
758 mono_threads_notify_initiator_of_suspend (info);
759 mono_thread_info_wait_for_resume (info);
760 mono_threads_notify_initiator_of_resume (info);
766 mono_thread_info_core_resume (MonoThreadInfo *info)
768 gboolean res = FALSE;
770 switch (mono_threads_transition_request_resume (info)) {
777 case ResumeInitSelfResume:
778 resume_self_suspended (info);
781 case ResumeInitAsyncResume:
782 resume_async_suspended (info);
785 case ResumeInitBlockingResume:
786 resume_blocking_suspended (info);
795 mono_thread_info_resume (MonoNativeThreadId tid)
797 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
798 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
799 MonoThreadInfo *info;
801 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
803 mono_thread_info_suspend_lock ();
805 info = mono_thread_info_lookup (tid); /*info on HP1*/
811 result = mono_thread_info_core_resume (info);
813 //Wait for the pending resume to finish
814 mono_threads_wait_pending_operations ();
817 mono_thread_info_suspend_unlock ();
818 mono_hazard_pointer_clear (hp, 1);
823 mono_thread_info_begin_suspend (MonoThreadInfo *info)
825 switch (mono_threads_transition_request_async_suspension (info)) {
826 case AsyncSuspendAlreadySuspended:
827 case AsyncSuspendBlocking:
829 case AsyncSuspendWait:
830 mono_threads_add_to_pending_operation_set (info);
832 case AsyncSuspendInitSuspend:
833 return begin_async_suspend (info, FALSE);
835 g_assert_not_reached ();
840 mono_thread_info_begin_resume (MonoThreadInfo *info)
842 return mono_thread_info_core_resume (info);
846 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
847 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
850 is_thread_in_critical_region (MonoThreadInfo *info)
854 gpointer stack_start;
855 MonoThreadUnwindState *state;
857 /* Are we inside a system critical region? */
858 if (info->inside_critical_region)
861 /* Are we inside a GC critical region? */
862 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
866 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
867 state = mono_thread_info_get_suspend_state (info);
868 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
871 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
872 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
873 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
876 ji = mono_jit_info_table_find (
877 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
878 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
883 method = mono_jit_info_get_method (ji);
885 return threads_callbacks.mono_method_is_critical (method);
889 mono_thread_info_in_critical_location (MonoThreadInfo *info)
891 return is_thread_in_critical_region (info);
895 The return value is only valid until a matching mono_thread_info_resume is called
897 static MonoThreadInfo*
898 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
900 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
901 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
905 switch (mono_threads_transition_request_async_suspension (info)) {
906 case AsyncSuspendAlreadySuspended:
907 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
909 case AsyncSuspendWait:
910 mono_threads_add_to_pending_operation_set (info);
912 case AsyncSuspendInitSuspend:
913 if (!begin_async_suspend (info, interrupt_kernel)) {
914 mono_hazard_pointer_clear (hp, 1);
918 case AsyncSuspendBlocking:
919 if (interrupt_kernel)
920 mono_threads_suspend_abort_syscall (info);
924 g_assert_not_reached ();
927 //Wait for the pending suspend to finish
928 mono_threads_wait_pending_operations ();
930 if (!check_async_suspend (info)) {
931 mono_thread_info_core_resume (info);
932 mono_threads_wait_pending_operations ();
933 mono_hazard_pointer_clear (hp, 1);
939 static MonoThreadInfo*
940 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
942 MonoThreadInfo *info = NULL;
943 int sleep_duration = 0;
945 if (!(info = suspend_sync (id, interrupt_kernel))) {
946 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
950 /*WARNING: We now are in interrupt context until we resume the thread. */
951 if (!is_thread_in_critical_region (info))
954 if (!mono_thread_info_core_resume (info)) {
955 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
958 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
960 /* Wait for the pending resume to finish */
961 mono_threads_wait_pending_operations ();
963 if (sleep_duration == 0)
964 mono_thread_info_yield ();
966 g_usleep (sleep_duration);
968 sleep_duration += 10;
974 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
977 MonoThreadInfo *info = NULL;
978 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
980 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
981 /*FIXME: unify this with self-suspend*/
982 g_assert (id != mono_native_thread_id_get ());
984 /* This can block during stw */
985 mono_thread_info_suspend_lock ();
986 mono_threads_begin_global_suspend ();
988 info = suspend_sync_nolock (id, interrupt_kernel);
992 switch (result = callback (info, user_data)) {
993 case MonoResumeThread:
994 mono_hazard_pointer_set (hp, 1, info);
995 mono_thread_info_core_resume (info);
996 mono_threads_wait_pending_operations ();
1001 g_error ("Invalid suspend_and_run callback return value %d", result);
1005 mono_hazard_pointer_clear (hp, 1);
1006 mono_threads_end_global_suspend ();
1007 mono_thread_info_suspend_unlock ();
1011 Inject an assynchronous call into the target thread. The target thread must be suspended and
1012 only a single async call can be setup for a given suspend cycle.
1013 This async call must cause stack unwinding as the current implementation doesn't save enough state
1014 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1015 currently used only to deliver exceptions.
1018 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1020 /* An async call can only be setup on an async suspended thread */
1021 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1022 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1023 g_assert (!info->async_target);
1024 info->async_target = target_func;
1025 /* This is not GC tracked */
1026 info->user_data = user_data;
1030 The suspend lock is held during any suspend in progress.
1031 A GC that has safepoints must take this lock as part of its
1032 STW to make sure no unsafe pending suspend is in progress.
1036 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1039 g_assert (mono_thread_info_is_current (info));
1040 g_assert (mono_thread_info_is_live (info));
1042 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1044 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1045 g_assert (res != -1);
1047 MONO_EXIT_GC_SAFE_WITH_INFO;
1051 mono_thread_info_suspend_lock (void)
1053 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1057 mono_thread_info_suspend_unlock (void)
1059 mono_os_sem_post (&global_suspend_semaphore);
1063 * This is a very specific function whose only purpose is to
1064 * break a given thread from socket syscalls.
1066 * This only exists because linux won't fail a call to connect
1067 * if the underlying is closed.
1069 * TODO We should cleanup and unify this with the other syscall abort
1073 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1075 MonoThreadHazardPointers *hp;
1076 MonoThreadInfo *info;
1078 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1081 hp = mono_hazard_pointer_get ();
1082 info = mono_thread_info_lookup (tid);
1086 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1087 mono_hazard_pointer_clear (hp, 1);
1091 mono_thread_info_suspend_lock ();
1092 mono_threads_begin_global_suspend ();
1094 mono_threads_suspend_abort_syscall (info);
1095 mono_threads_wait_pending_operations ();
1097 mono_hazard_pointer_clear (hp, 1);
1099 mono_threads_end_global_suspend ();
1100 mono_thread_info_suspend_unlock ();
1104 mono_thread_info_unified_management_enabled (void)
1106 return unified_suspend_enabled;
1110 * mono_thread_info_set_is_async_context:
1112 * Set whenever the current thread is in an async context. Some runtime functions might behave
1113 * differently while in an async context in order to be async safe.
1116 mono_thread_info_set_is_async_context (gboolean async_context)
1118 MonoThreadInfo *info = mono_thread_info_current ();
1121 info->is_async_context = async_context;
1125 mono_thread_info_is_async_context (void)
1127 MonoThreadInfo *info = mono_thread_info_current ();
1130 return info->is_async_context;
1137 MonoThreadStart start_routine;
1138 gpointer start_routine_arg;
1140 MonoCoopSem registered;
1141 MonoThreadInfo *info;
1145 inner_start_thread (gpointer data)
1147 CreateThreadData *thread_data;
1148 MonoThreadInfo *info;
1149 MonoThreadStart start_routine;
1150 gpointer start_routine_arg;
1151 guint32 start_routine_res;
1155 thread_data = (CreateThreadData*) data;
1156 g_assert (thread_data);
1158 start_routine = thread_data->start_routine;
1159 start_routine_arg = thread_data->start_routine_arg;
1161 priority = thread_data->priority;
1163 info = mono_thread_info_attach (&dummy);
1164 info->runtime_thread = TRUE;
1166 mono_threads_platform_set_priority (info, priority);
1168 thread_data->info = info;
1170 mono_coop_sem_post (&thread_data->registered);
1172 if (InterlockedDecrement (&thread_data->ref) == 0) {
1173 mono_coop_sem_destroy (&thread_data->registered);
1174 g_free (thread_data);
1177 /* thread_data is not valid anymore */
1180 /* Run the actual main function of the thread */
1181 start_routine_res = start_routine (start_routine_arg);
1183 mono_threads_platform_exit (start_routine_res);
1185 g_assert_not_reached ();
1189 * mono_threads_create_thread:
1191 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1192 * Returns: a windows or io-layer handle for the thread.
1195 mono_threads_create_thread (MonoThreadStart start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
1197 CreateThreadData *thread_data;
1198 MonoThreadInfo *info;
1202 thread_data = g_new0 (CreateThreadData, 1);
1203 thread_data->ref = 2;
1204 thread_data->start_routine = start;
1205 thread_data->start_routine_arg = arg;
1206 thread_data->priority = tp->priority;
1207 mono_coop_sem_init (&thread_data->registered, 0);
1209 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, tp->stack_size, out_tid);
1211 /* ref is not going to be decremented in inner_start_thread */
1212 InterlockedDecrement (&thread_data->ref);
1217 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1218 g_assert (res == 0);
1220 info = thread_data->info;
1227 if (InterlockedDecrement (&thread_data->ref) == 0) {
1228 mono_coop_sem_destroy (&thread_data->registered);
1229 g_free (thread_data);
1236 * mono_thread_info_get_stack_bounds:
1238 * Return the address and size of the current threads stack. Return NULL as the
1239 * stack address if the stack address cannot be determined.
1242 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1244 guint8 *current = (guint8 *)&stsize;
1245 mono_threads_platform_get_stack_bounds (staddr, stsize);
1249 /* Sanity check the result */
1250 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1252 /* When running under emacs, sometimes staddr is not aligned to a page size */
1253 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1257 mono_thread_info_yield (void)
1259 return mono_threads_platform_yield ();
1261 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1262 static MonoCoopMutex sleep_mutex;
1263 static MonoCoopCond sleep_cond;
1266 sleep_initialize (void)
1268 mono_coop_mutex_init (&sleep_mutex);
1269 mono_coop_cond_init (&sleep_cond);
1273 sleep_interrupt (gpointer data)
1275 mono_coop_mutex_lock (&sleep_mutex);
1276 mono_coop_cond_broadcast (&sleep_cond);
1277 mono_coop_mutex_unlock (&sleep_mutex);
1280 static inline guint32
1281 sleep_interruptable (guint32 ms, gboolean *alerted)
1285 g_assert (INFINITE == G_MAXUINT32);
1291 end = mono_100ns_ticks () + (ms * 1000 * 10);
1293 mono_lazy_initialize (&sleep_init, sleep_initialize);
1295 mono_coop_mutex_lock (&sleep_mutex);
1298 if (ms != INFINITE) {
1299 now = mono_100ns_ticks ();
1304 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1306 mono_coop_mutex_unlock (&sleep_mutex);
1307 return WAIT_IO_COMPLETION;
1311 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1313 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1315 mono_thread_info_uninstall_interrupt (alerted);
1317 mono_coop_mutex_unlock (&sleep_mutex);
1318 return WAIT_IO_COMPLETION;
1322 mono_coop_mutex_unlock (&sleep_mutex);
1328 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1331 MonoThreadInfo *info;
1333 mono_thread_info_yield ();
1335 info = mono_thread_info_current ();
1336 if (info && mono_thread_info_is_interrupt_state (info))
1337 return WAIT_IO_COMPLETION;
1343 return sleep_interruptable (ms, alerted);
1347 if (ms == INFINITE) {
1350 Sleep (G_MAXUINT32);
1352 sleep (G_MAXUINT32);
1357 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1358 struct timespec start, target;
1360 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1361 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1362 g_assert (ret == 0);
1365 target.tv_sec += ms / 1000;
1366 target.tv_nsec += (ms % 1000) * 1000000;
1367 if (target.tv_nsec > 999999999) {
1368 target.tv_nsec -= 999999999;
1373 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1378 struct timespec req, rem;
1380 req.tv_sec = ms / 1000;
1381 req.tv_nsec = (ms % 1000) * 1000000;
1384 memset (&rem, 0, sizeof (rem));
1385 ret = nanosleep (&req, &rem);
1387 #endif /* __linux__ */
1396 mono_thread_info_usleep (guint64 us)
1405 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1407 return ((MonoThreadInfo*)info)->tls [key];
1411 * mono_threads_info_tls_set:
1413 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1414 * values of TLS variables for threads other than the current thread.
1415 * This should only be used for infrequently changing TLS variables, and it should
1416 * be paired with setting the real TLS variable since this provides no GC tracking.
1419 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1421 ((MonoThreadInfo*)info)->tls [key] = value;
1425 * mono_thread_info_exit:
1427 * Exit the current thread.
1428 * This function doesn't return.
1431 mono_thread_info_exit (void)
1433 mono_threads_platform_exit (0);
1437 * mono_threads_open_thread_handle:
1439 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1440 * The handle need to be closed by calling CloseHandle () when it is no
1444 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1446 return mono_threads_platform_open_thread_handle (handle, tid);
1450 mono_threads_close_thread_handle (HANDLE handle)
1452 return mono_threads_platform_close_thread_handle (handle);
1455 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1457 struct _MonoThreadInfoInterruptToken {
1458 void (*callback) (gpointer data);
1463 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1465 * - @callback: must be able to be called from another thread and always cancel the wait
1466 * - @data: passed to the callback
1467 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1468 * if set to TRUE, it must mean that the thread is in interrupted state
1471 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1473 MonoThreadInfo *info;
1474 MonoThreadInfoInterruptToken *previous_token, *token;
1476 g_assert (callback);
1478 g_assert (interrupted);
1479 *interrupted = FALSE;
1481 info = mono_thread_info_current ();
1484 /* The memory of this token can be freed at 2 places:
1485 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1486 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1487 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1488 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1489 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1490 token->callback = callback;
1493 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1495 if (previous_token) {
1496 if (previous_token != INTERRUPT_STATE)
1497 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1501 *interrupted = TRUE;
1504 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1505 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1509 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1511 MonoThreadInfo *info;
1512 MonoThreadInfoInterruptToken *previous_token;
1514 g_assert (interrupted);
1515 *interrupted = FALSE;
1517 info = mono_thread_info_current ();
1520 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1522 /* only the installer can uninstall the token */
1523 g_assert (previous_token);
1525 if (previous_token == INTERRUPT_STATE) {
1526 /* if it is interrupted, then it is going to be freed in finish interrupt */
1527 *interrupted = TRUE;
1529 g_free (previous_token);
1532 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1533 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1536 static MonoThreadInfoInterruptToken*
1537 set_interrupt_state (MonoThreadInfo *info)
1539 MonoThreadInfoInterruptToken *token, *previous_token;
1543 /* Atomically obtain the token the thread is
1544 * waiting on, and change it to a flag value. */
1547 previous_token = info->interrupt_token;
1549 /* Already interrupted */
1550 if (previous_token == INTERRUPT_STATE) {
1555 token = previous_token;
1556 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1562 * mono_thread_info_prepare_interrupt:
1564 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1565 * - if the thread calls one of the WaitFor functions, the function will return with
1566 * WAIT_IO_COMPLETION instead of waiting
1567 * - if the thread was waiting when this function was called, the wait will be broken
1569 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1570 * didn't receive the interrupt signal yet, in this case it should call the wait function
1571 * again. This essentially means that the target thread will busy wait until it is ready to
1572 * process the interruption.
1574 MonoThreadInfoInterruptToken*
1575 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1577 MonoThreadInfoInterruptToken *token;
1579 token = set_interrupt_state (info);
1581 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1582 mono_thread_info_get_tid (info), token);
1588 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1590 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1595 g_assert (token->callback);
1597 token->callback (token->data);
1603 mono_thread_info_self_interrupt (void)
1605 MonoThreadInfo *info;
1606 MonoThreadInfoInterruptToken *token;
1608 info = mono_thread_info_current ();
1611 token = set_interrupt_state (info);
1614 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1615 mono_thread_info_get_tid (info));
1618 /* Clear the interrupted flag of the current thread, set with
1619 * mono_thread_info_self_interrupt, so it can wait again */
1621 mono_thread_info_clear_self_interrupt ()
1623 MonoThreadInfo *info;
1624 MonoThreadInfoInterruptToken *previous_token;
1626 info = mono_thread_info_current ();
1629 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1630 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1632 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1636 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1639 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1643 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1647 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1648 g_string_append_printf (text, "not waiting");
1649 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1650 g_string_append_printf (text, "interrupted state");
1652 g_string_append_printf (text, "waiting");
1656 mono_thread_info_is_current (MonoThreadInfo *info)
1658 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1662 mono_thread_info_set_exited (THREAD_INFO_TYPE *info)
1664 g_assert (mono_thread_info_is_current (info));
1665 mono_threads_platform_set_exited (info);
1669 mono_thread_info_get_handle (THREAD_INFO_TYPE *info)
1671 g_assert (info->handle);
1672 return info->handle;
1676 mono_thread_info_describe (MonoThreadInfo *info, GString *text)
1678 mono_threads_platform_describe (info, text);
1682 mono_thread_info_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
1684 mono_threads_platform_own_mutex (info, mutex_handle);
1688 mono_thread_info_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
1690 mono_threads_platform_disown_mutex (info, mutex_handle);
1694 mono_thread_info_get_priority (MonoThreadInfo *info)
1696 return mono_threads_platform_get_priority (info);
1700 mono_thread_info_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
1702 mono_threads_platform_set_priority (info, priority);