2 * mono-threads.c: Coop threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
12 /* enable pthread extensions */
14 #define _DARWIN_C_SOURCE
17 #include <mono/utils/mono-compiler.h>
18 #include <mono/utils/mono-semaphore.h>
19 #include <mono/utils/mono-threads.h>
20 #include <mono/utils/mono-tls.h>
21 #include <mono/utils/hazard-pointer.h>
22 #include <mono/utils/mono-memory-model.h>
23 #include <mono/utils/mono-mmap.h>
24 #include <mono/utils/atomic.h>
25 #include <mono/utils/mono-time.h>
26 #include <mono/utils/mono-counters.h>
29 #include <mono/utils/mach-support.h>
32 #ifdef USE_COOP_BACKEND
34 volatile size_t mono_polling_required;
36 static int coop_reset_blocking_count, coop_try_blocking_count, coop_do_blocking_count, coop_do_polling_count, coop_save_count;
39 mono_threads_state_poll (void)
42 ++coop_do_polling_count;
44 info = mono_thread_info_current_unchecked ();
47 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
49 /* Fast check for pending suspend requests */
50 if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
54 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
56 /* commit the saved state and notify others if needed */
57 switch (mono_threads_transition_state_poll (info)) {
58 case SelfSuspendResumed:
61 mono_thread_info_wait_for_resume (info);
63 case SelfSuspendNotifyAndWait:
64 mono_threads_notify_initiator_of_suspend (info);
65 mono_thread_info_wait_for_resume (info);
71 mono_threads_prepare_blocking (void)
74 ++coop_do_blocking_count;
76 info = mono_thread_info_current_unchecked ();
77 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
78 if (!info || !mono_thread_info_is_live (info)) {
79 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
85 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
87 switch (mono_threads_transition_do_blocking (info)) {
88 case DoBlockingContinue:
90 case DoBlockingPollAndRetry:
91 mono_threads_state_poll ();
99 mono_threads_finish_blocking (void *cookie)
101 static gboolean warned_about_bad_transition;
102 MonoThreadInfo *info = cookie;
107 g_assert (info == mono_thread_info_current_unchecked ());
109 switch (mono_threads_transition_done_blocking (info)) {
110 case DoneBlockingAborted:
111 if (!warned_about_bad_transition) {
112 warned_about_bad_transition = TRUE;
113 g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
115 mono_threads_state_poll ();
118 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
120 case DoneBlockingWait:
121 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
122 mono_thread_info_wait_for_resume (info);
125 g_error ("Unknown thread state");
131 mono_threads_reset_blocking_start (void)
133 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
134 ++coop_reset_blocking_count;
136 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
137 if (!info || !mono_thread_info_is_live (info))
140 switch (mono_threads_transition_abort_blocking (info)) {
141 case AbortBlockingIgnore:
142 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
144 case AbortBlockingIgnoreAndPoll:
145 mono_threads_state_poll ();
147 case AbortBlockingOk:
148 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
150 case AbortBlockingOkAndPool:
151 mono_threads_state_poll ();
154 g_error ("Unknown thread state");
159 mono_threads_reset_blocking_end (void *cookie)
161 MonoThreadInfo *info = cookie;
166 g_assert (info == mono_thread_info_current_unchecked ());
167 mono_threads_prepare_blocking ();
171 mono_threads_try_prepare_blocking (void)
173 MonoThreadInfo *info;
174 ++coop_try_blocking_count;
176 info = mono_thread_info_current_unchecked ();
177 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
178 if (!info || !mono_thread_info_is_live (info) || mono_thread_info_current_state (info) == STATE_BLOCKING) {
179 THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
185 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
187 switch (mono_threads_transition_do_blocking (info)) {
188 case DoBlockingContinue:
190 case DoBlockingPollAndRetry:
191 mono_threads_state_poll ();
199 mono_threads_finish_try_blocking (void* cookie)
201 mono_threads_finish_blocking (cookie);
205 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
212 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
214 mono_threads_add_to_pending_operation_set (info);
215 /* There's nothing else to do after we async request the thread to suspend */
220 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
222 /* Async suspend can't async fail on coop */
227 mono_threads_init_platform (void)
229 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
230 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
231 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
232 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
233 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
234 //See the above for what's wrong here.
238 mono_threads_platform_free (MonoThreadInfo *info)
241 mach_port_deallocate (current_task (), info->native_handle);
244 //See the above for what's wrong here.
248 mono_threads_platform_register (MonoThreadInfo *info)
251 char thread_name [64];
253 info->native_handle = mach_thread_self ();
254 snprintf (thread_name, 64, "tid_%x", (int)info->native_handle);
255 pthread_setname_np (thread_name);
258 //See the above for what's wrong here.
262 mono_threads_core_begin_global_suspend (void)
264 mono_polling_required = 1;
268 mono_threads_core_end_global_suspend (void)
270 mono_polling_required = 0;
274 mono_threads_enter_gc_unsafe_region (void)
276 return mono_threads_reset_blocking_start ();
280 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
282 mono_threads_reset_blocking_end (regions_cookie);
288 mono_threads_enter_gc_unsafe_region (void)
294 mono_threads_exit_gc_unsafe_region (void *regions_cookie)