2 * mono-threads.c: Coop threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
10 #include <mono/utils/mono-compiler.h>
11 #include <mono/utils/mono-semaphore.h>
12 #include <mono/utils/mono-threads.h>
13 #include <mono/utils/mono-tls.h>
14 #include <mono/utils/hazard-pointer.h>
15 #include <mono/utils/mono-memory-model.h>
16 #include <mono/utils/mono-mmap.h>
17 #include <mono/utils/atomic.h>
18 #include <mono/utils/mono-time.h>
19 #include <mono/utils/mono-counters.h>
22 #include <mono/utils/mach-support.h>
25 #ifdef USE_COOP_BACKEND
27 volatile size_t mono_polling_required;
29 static int coop_reset_blocking_count, coop_try_blocking_count, coop_do_blocking_count, coop_do_polling_count, coop_save_count;
32 mono_threads_state_poll (void)
35 ++coop_do_polling_count;
37 info = mono_thread_info_current_unchecked ();
40 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
42 /* Fast check for pending suspend requests */
43 if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
47 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
49 /* commit the saved state and notify others if needed */
50 switch (mono_threads_transition_state_poll (info)) {
51 case SelfSuspendResumed:
54 mono_thread_info_wait_for_resume (info);
56 case SelfSuspendNotifyAndWait:
57 mono_threads_notify_initiator_of_suspend (info);
58 mono_thread_info_wait_for_resume (info);
64 mono_threads_prepare_blocking (void)
67 ++coop_do_blocking_count;
69 info = mono_thread_info_current_unchecked ();
70 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
71 if (!info || !mono_thread_info_is_live (info)) {
72 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
78 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
80 switch (mono_threads_transition_do_blocking (info)) {
81 case DoBlockingContinue:
83 case DoBlockingPollAndRetry:
84 mono_threads_state_poll ();
92 mono_threads_finish_blocking (void *cookie)
94 static gboolean warned_about_bad_transition;
95 MonoThreadInfo *info = cookie;
100 g_assert (info == mono_thread_info_current_unchecked ());
102 switch (mono_threads_transition_done_blocking (info)) {
103 case DoneBlockingAborted:
104 if (!warned_about_bad_transition) {
105 warned_about_bad_transition = TRUE;
106 g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
108 mono_threads_state_poll ();
111 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
113 case DoneBlockingWait:
114 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
115 mono_thread_info_wait_for_resume (info);
118 g_error ("Unknown thread state");
124 mono_threads_reset_blocking_start (void)
126 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
127 ++coop_reset_blocking_count;
129 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
130 if (!info || !mono_thread_info_is_live (info))
133 switch (mono_threads_transition_abort_blocking (info)) {
134 case AbortBlockingIgnore:
135 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
137 case AbortBlockingIgnoreAndPoll:
138 mono_threads_state_poll ();
140 case AbortBlockingOk:
141 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
143 case AbortBlockingOkAndPool:
144 mono_threads_state_poll ();
147 g_error ("Unknown thread state");
152 mono_threads_reset_blocking_end (void *cookie)
154 MonoThreadInfo *info = cookie;
159 g_assert (info == mono_thread_info_current_unchecked ());
160 mono_threads_prepare_blocking ();
164 mono_threads_try_prepare_blocking (void)
166 MonoThreadInfo *info;
167 ++coop_try_blocking_count;
169 info = mono_thread_info_current_unchecked ();
170 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
171 if (!info || !mono_thread_info_is_live (info) || mono_thread_info_current_state (info) == STATE_BLOCKING) {
172 THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
178 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
180 switch (mono_threads_transition_do_blocking (info)) {
181 case DoBlockingContinue:
183 case DoBlockingPollAndRetry:
184 mono_threads_state_poll ();
192 mono_threads_finish_try_blocking (void* cookie)
194 mono_threads_finish_blocking (cookie);
198 mono_threads_core_abort_syscall (MonoThreadInfo *info)
204 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
211 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
213 mono_threads_add_to_pending_operation_set (info);
214 /* There's nothing else to do after we async request the thread to suspend */
219 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
221 /* Async suspend can't async fail on coop */
226 mono_threads_core_needs_abort_syscall (void)
230 Syscall abort can't be handled by the suspend machinery even though it's kind of implemented
231 in a similar way (with, like, signals).
233 So, having it here is wrong, it should be on mono-threads-(mach|posix|windows).
234 Ideally we would slice this in (coop|preemp) and target. Then have this file set:
235 mono-threads-mach, mono-threads-mach-preempt and mono-threads-mach-coop.
236 More files, less ifdef hell.
242 mono_threads_init_platform (void)
244 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
245 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
246 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
247 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
248 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
249 //See the above for what's wrong here.
253 mono_threads_platform_free (MonoThreadInfo *info)
256 mach_port_deallocate (current_task (), info->native_handle);
259 //See the above for what's wrong here.
263 mono_threads_platform_register (MonoThreadInfo *info)
266 info->native_handle = mach_thread_self ();
269 //See the above for what's wrong here.
273 mono_threads_core_begin_global_suspend (void)
275 mono_polling_required = 1;
279 mono_threads_core_end_global_suspend (void)
281 mono_polling_required = 0;
285 mono_threads_enter_gc_unsafe_region (void)
287 return mono_threads_reset_blocking_start ();
291 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
293 mono_threads_reset_blocking_end (regions_cookie);
299 mono_threads_enter_gc_unsafe_region (void)
305 mono_threads_exit_gc_unsafe_region (void *regions_cookie)