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-threads.h>
19 #include <mono/utils/mono-tls.h>
20 #include <mono/utils/hazard-pointer.h>
21 #include <mono/utils/mono-memory-model.h>
22 #include <mono/utils/mono-mmap.h>
23 #include <mono/utils/atomic.h>
24 #include <mono/utils/mono-time.h>
25 #include <mono/utils/mono-counters.h>
26 #include <mono/utils/mono-threads-coop.h>
27 #include <mono/utils/mono-threads-api.h>
30 #include <mono/utils/mach-support.h>
34 // TODO: Find MSVC replacement for __builtin_unwind_init
35 #define SAVE_REGS_ON_STACK g_assert_not_reached ();
37 #define SAVE_REGS_ON_STACK __builtin_unwind_init ();
40 volatile size_t mono_polling_required;
42 static int coop_reset_blocking_count;
43 static int coop_try_blocking_count;
44 static int coop_do_blocking_count;
45 static int coop_do_polling_count;
46 static int coop_save_count;
49 mono_threads_state_poll (void)
53 g_assert (mono_threads_is_coop_enabled ());
55 ++coop_do_polling_count;
57 info = mono_thread_info_current_unchecked ();
60 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
62 /* Fast check for pending suspend requests */
63 if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
67 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
69 /* commit the saved state and notify others if needed */
70 switch (mono_threads_transition_state_poll (info)) {
71 case SelfSuspendResumed:
74 mono_thread_info_wait_for_resume (info);
76 case SelfSuspendNotifyAndWait:
77 mono_threads_notify_initiator_of_suspend (info);
78 mono_thread_info_wait_for_resume (info);
91 copy_stack_data (MonoThreadInfo *info, void* stackdata_begin)
93 MonoThreadUnwindState *state;
95 void* stackdata_end = return_stack_ptr ();
99 state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
101 stackdata_size = (char*)stackdata_begin - (char*)stackdata_end;
103 if (((gsize) stackdata_begin & (SIZEOF_VOID_P - 1)) != 0)
104 g_error ("stackdata_begin (%p) must be %d-byte aligned", stackdata_begin, SIZEOF_VOID_P);
105 if (((gsize) stackdata_end & (SIZEOF_VOID_P - 1)) != 0)
106 g_error ("stackdata_end (%p) must be %d-byte aligned", stackdata_end, SIZEOF_VOID_P);
108 if (stackdata_size <= 0)
109 g_error ("stackdata_size = %d, but must be > 0, stackdata_begin = %p, stackdata_end = %p", stackdata_size, stackdata_begin, stackdata_end);
111 g_byte_array_set_size (info->stackdata, stackdata_size);
112 state->gc_stackdata = info->stackdata->data;
113 memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
115 state->gc_stackdata_size = stackdata_size;
119 mono_threads_prepare_blocking (void* stackdata)
121 MonoThreadInfo *info;
123 if (!mono_threads_is_coop_enabled ())
126 ++coop_do_blocking_count;
128 info = mono_thread_info_current_unchecked ();
129 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
130 if (!info || !mono_thread_info_is_live (info)) {
131 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
135 copy_stack_data (info, stackdata);
139 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
141 switch (mono_threads_transition_do_blocking (info)) {
142 case DoBlockingContinue:
144 case DoBlockingPollAndRetry:
145 mono_threads_state_poll ();
153 mono_threads_finish_blocking (void *cookie, void* stackdata)
155 static gboolean warned_about_bad_transition;
156 MonoThreadInfo *info;
158 if (!mono_threads_is_coop_enabled ())
161 info = (MonoThreadInfo *)cookie;
165 g_assert (info == mono_thread_info_current_unchecked ());
167 switch (mono_threads_transition_done_blocking (info)) {
168 case DoneBlockingAborted:
169 if (!warned_about_bad_transition) {
170 warned_about_bad_transition = TRUE;
171 g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
173 mono_threads_state_poll ();
176 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
178 case DoneBlockingWait:
179 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
180 mono_thread_info_wait_for_resume (info);
183 g_error ("Unknown thread state");
189 mono_threads_reset_blocking_start (void* stackdata)
191 MonoThreadInfo *info;
193 if (!mono_threads_is_coop_enabled ())
196 ++coop_reset_blocking_count;
198 info = mono_thread_info_current_unchecked ();
199 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
200 if (!info || !mono_thread_info_is_live (info))
203 copy_stack_data (info, stackdata);
205 switch (mono_threads_transition_abort_blocking (info)) {
206 case AbortBlockingIgnore:
207 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
209 case AbortBlockingIgnoreAndPoll:
210 mono_threads_state_poll ();
212 case AbortBlockingOk:
213 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
215 case AbortBlockingOkAndPool:
216 mono_threads_state_poll ();
219 g_error ("Unknown thread state");
224 mono_threads_reset_blocking_end (void *cookie, void* stackdata)
226 MonoThreadInfo *info;
228 if (!mono_threads_is_coop_enabled ())
231 info = (MonoThreadInfo *)cookie;
235 g_assert (info == mono_thread_info_current_unchecked ());
236 mono_threads_prepare_blocking (stackdata);
240 mono_threads_init_coop (void)
242 if (!mono_threads_is_coop_enabled ())
245 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
246 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
247 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
248 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
249 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
250 //See the above for what's wrong here.
254 mono_threads_coop_begin_global_suspend (void)
256 if (mono_threads_is_coop_enabled ())
257 mono_polling_required = 1;
261 mono_threads_coop_end_global_suspend (void)
263 if (mono_threads_is_coop_enabled ())
264 mono_polling_required = 0;
268 mono_threads_enter_gc_unsafe_region (gpointer* stackdata)
270 if (!mono_threads_is_coop_enabled ())
273 return mono_threads_reset_blocking_start (stackdata);
277 mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer* stackdata)
279 if (!mono_threads_is_coop_enabled ())
282 mono_threads_reset_blocking_end (cookie, stackdata);
286 mono_threads_assert_gc_unsafe_region (void)
288 MONO_REQ_GC_UNSAFE_MODE;
292 mono_threads_enter_gc_safe_region (gpointer *stackdata)
294 if (!mono_threads_is_coop_enabled ())
297 return mono_threads_prepare_blocking (stackdata);
301 mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackdata)
303 if (!mono_threads_is_coop_enabled ())
306 mono_threads_finish_blocking (cookie, stackdata);
310 mono_threads_assert_gc_safe_region (void)
312 MONO_REQ_GC_SAFE_MODE;