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/metadata/mempool.h>
18 #include <mono/utils/mono-compiler.h>
19 #include <mono/utils/mono-semaphore.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-counters.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 #ifdef USE_COOP_BACKEND
42 volatile size_t mono_polling_required;
44 static int coop_reset_blocking_count, coop_try_blocking_count, coop_do_blocking_count, coop_do_polling_count, coop_save_count;
47 mono_threads_state_poll (void)
50 ++coop_do_polling_count;
52 info = mono_thread_info_current_unchecked ();
55 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
57 /* Fast check for pending suspend requests */
58 if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
62 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
64 /* commit the saved state and notify others if needed */
65 switch (mono_threads_transition_state_poll (info)) {
66 case SelfSuspendResumed:
69 mono_thread_info_wait_for_resume (info);
71 case SelfSuspendNotifyAndWait:
72 mono_threads_notify_initiator_of_suspend (info);
73 mono_thread_info_wait_for_resume (info);
86 copy_stack_data (MonoThreadInfo *info, void* stackdata_begin)
88 MonoThreadUnwindState *state;
90 void* stackdata_end = return_stack_ptr ();
94 state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
96 stackdata_size = (char*)stackdata_begin - (char*)stackdata_end;
97 g_assert (stackdata_size > 0);
99 g_byte_array_set_size (info->stackdata, stackdata_size);
100 state->gc_stackdata = info->stackdata->data;
101 memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
103 state->gc_stackdata_size = stackdata_size;
107 mono_threads_prepare_blocking (void* stackdata)
109 MonoThreadInfo *info;
110 ++coop_do_blocking_count;
112 info = mono_thread_info_current_unchecked ();
113 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
114 if (!info || !mono_thread_info_is_live (info)) {
115 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
119 copy_stack_data (info, stackdata);
123 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
125 switch (mono_threads_transition_do_blocking (info)) {
126 case DoBlockingContinue:
128 case DoBlockingPollAndRetry:
129 mono_threads_state_poll ();
137 mono_threads_finish_blocking (void *cookie, void* stackdata)
139 static gboolean warned_about_bad_transition;
140 MonoThreadInfo *info = cookie;
145 g_assert (info == mono_thread_info_current_unchecked ());
147 switch (mono_threads_transition_done_blocking (info)) {
148 case DoneBlockingAborted:
149 if (!warned_about_bad_transition) {
150 warned_about_bad_transition = TRUE;
151 g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
153 mono_threads_state_poll ();
156 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
158 case DoneBlockingWait:
159 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
160 mono_thread_info_wait_for_resume (info);
163 g_error ("Unknown thread state");
169 mono_threads_reset_blocking_start (void* stackdata)
171 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
172 ++coop_reset_blocking_count;
174 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
175 if (!info || !mono_thread_info_is_live (info))
178 copy_stack_data (info, stackdata);
180 switch (mono_threads_transition_abort_blocking (info)) {
181 case AbortBlockingIgnore:
182 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
184 case AbortBlockingIgnoreAndPoll:
185 mono_threads_state_poll ();
187 case AbortBlockingOk:
188 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
190 case AbortBlockingOkAndPool:
191 mono_threads_state_poll ();
194 g_error ("Unknown thread state");
199 mono_threads_reset_blocking_end (void *cookie, void* stackdata)
201 MonoThreadInfo *info = cookie;
206 g_assert (info == mono_thread_info_current_unchecked ());
207 mono_threads_prepare_blocking (stackdata);
211 mono_threads_try_prepare_blocking (void* stackdata)
213 MonoThreadInfo *info;
214 ++coop_try_blocking_count;
216 info = mono_thread_info_current_unchecked ();
217 /* If the thread is not attached, it doesn't make sense prepare for suspend. */
218 if (!info || !mono_thread_info_is_live (info) || mono_thread_info_current_state (info) == STATE_BLOCKING) {
219 THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
223 copy_stack_data (info, stackdata);
227 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
229 switch (mono_threads_transition_do_blocking (info)) {
230 case DoBlockingContinue:
232 case DoBlockingPollAndRetry:
233 mono_threads_state_poll ();
241 mono_threads_finish_try_blocking (void* cookie, void* stackdata)
243 mono_threads_finish_blocking (cookie, stackdata);
247 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
254 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
256 mono_threads_add_to_pending_operation_set (info);
257 /* There's nothing else to do after we async request the thread to suspend */
262 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
264 /* Async suspend can't async fail on coop */
269 mono_threads_init_platform (void)
271 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
272 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
273 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
274 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
275 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
276 //See the above for what's wrong here.
280 mono_threads_platform_free (MonoThreadInfo *info)
283 mach_port_deallocate (current_task (), info->native_handle);
286 //See the above for what's wrong here.
290 mono_threads_platform_register (MonoThreadInfo *info)
293 char thread_name [64];
295 info->native_handle = mach_thread_self ();
296 snprintf (thread_name, 64, "tid_%x", (int)info->native_handle);
297 pthread_setname_np (thread_name);
300 //See the above for what's wrong here.
304 mono_threads_core_begin_global_suspend (void)
306 mono_polling_required = 1;
310 mono_threads_core_end_global_suspend (void)
312 mono_polling_required = 0;
316 mono_threads_enter_gc_unsafe_region (void* stackdata)
318 return mono_threads_reset_blocking_start (stackdata);
322 mono_threads_exit_gc_unsafe_region (void *regions_cookie, void* stackdata)
324 mono_threads_reset_blocking_end (regions_cookie, stackdata);
330 mono_threads_enter_gc_unsafe_region (void* stackdata)
336 mono_threads_exit_gc_unsafe_region (void *regions_cookie, void* stackdata)