[coop] Move syscall abort mechanism to dedicated files
[mono.git] / mono / utils / mono-threads-coop.c
1  /*
2  * mono-threads.c: Coop threading
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
8  */
9
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>
20
21 #ifdef TARGET_OSX
22 #include <mono/utils/mach-support.h>
23 #endif
24
25 #ifdef USE_COOP_BACKEND
26
27 volatile size_t mono_polling_required;
28
29 static int coop_reset_blocking_count, coop_try_blocking_count, coop_do_blocking_count, coop_do_polling_count, coop_save_count;
30
31 void
32 mono_threads_state_poll (void)
33 {
34         MonoThreadInfo *info;
35         ++coop_do_polling_count;
36
37         info = mono_thread_info_current_unchecked ();
38         if (!info)
39                 return;
40         THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
41
42         /* Fast check for pending suspend requests */
43         if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
44                 return;
45
46         ++coop_save_count;
47         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
48
49         /* commit the saved state and notify others if needed */
50         switch (mono_threads_transition_state_poll (info)) {
51         case SelfSuspendResumed:
52                 return;
53         case SelfSuspendWait:
54                 mono_thread_info_wait_for_resume (info);
55                 break;
56         case SelfSuspendNotifyAndWait:
57                 mono_threads_notify_initiator_of_suspend (info);
58                 mono_thread_info_wait_for_resume (info);
59                 break;
60         }
61 }
62
63 void*
64 mono_threads_prepare_blocking (void)
65 {
66         MonoThreadInfo *info;
67         ++coop_do_blocking_count;
68
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));
73                 return NULL;
74         }
75
76 retry:
77         ++coop_save_count;
78         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
79
80         switch (mono_threads_transition_do_blocking (info)) {
81         case DoBlockingContinue:
82                 break;
83         case DoBlockingPollAndRetry:
84                 mono_threads_state_poll ();
85                 goto retry;
86         }
87
88         return info;
89 }
90
91 void
92 mono_threads_finish_blocking (void *cookie)
93 {
94         static gboolean warned_about_bad_transition;
95         MonoThreadInfo *info = cookie;
96
97         if (!info)
98                 return;
99
100         g_assert (info == mono_thread_info_current_unchecked ());
101
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));
107                 }
108                 mono_threads_state_poll ();
109                 break;
110         case DoneBlockingOk:
111                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
112                 break;
113         case DoneBlockingWait:
114                 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
115                 mono_thread_info_wait_for_resume (info);
116                 break;
117         default:
118                 g_error ("Unknown thread state");
119         }
120 }
121
122
123 void*
124 mono_threads_reset_blocking_start (void)
125 {
126         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
127         ++coop_reset_blocking_count;
128
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                 return NULL;
132
133         switch (mono_threads_transition_abort_blocking (info)) {
134         case AbortBlockingIgnore:
135                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
136                 return NULL;
137         case AbortBlockingIgnoreAndPoll:
138                 mono_threads_state_poll ();
139                 return NULL;
140         case AbortBlockingOk:
141                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
142                 return info;
143         case AbortBlockingOkAndPool:
144                 mono_threads_state_poll ();
145                 return info;
146         default:
147                 g_error ("Unknown thread state");
148         }
149 }
150
151 void
152 mono_threads_reset_blocking_end (void *cookie)
153 {
154         MonoThreadInfo *info = cookie;
155
156         if (!info)
157                 return;
158
159         g_assert (info == mono_thread_info_current_unchecked ());
160         mono_threads_prepare_blocking ();
161 }
162
163 void*
164 mono_threads_try_prepare_blocking (void)
165 {
166         MonoThreadInfo *info;
167         ++coop_try_blocking_count;
168
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));
173                 return NULL;
174         }
175
176 retry:
177         ++coop_save_count;
178         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
179
180         switch (mono_threads_transition_do_blocking (info)) {
181         case DoBlockingContinue:
182                 break;
183         case DoBlockingPollAndRetry:
184                 mono_threads_state_poll ();
185                 goto retry;
186         }
187
188         return info;
189 }
190
191 void
192 mono_threads_finish_try_blocking (void* cookie)
193 {
194         mono_threads_finish_blocking (cookie);
195 }
196
197 gboolean
198 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
199 {
200         g_error ("FIXME");
201         return FALSE;
202 }
203
204 gboolean
205 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
206 {
207         mono_threads_add_to_pending_operation_set (info);
208         /* There's nothing else to do after we async request the thread to suspend */
209         return TRUE;
210 }
211
212 gboolean
213 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
214 {
215         /* Async suspend can't async fail on coop */
216         return TRUE;
217 }
218
219 void
220 mono_threads_init_platform (void)
221 {
222         mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
223         mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
224         mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
225         mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
226         mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
227         //See the above for what's wrong here.
228 }
229
230 void
231 mono_threads_platform_free (MonoThreadInfo *info)
232 {
233 #ifdef TARGET_OSX
234         mach_port_deallocate (current_task (), info->native_handle);
235 #endif
236
237         //See the above for what's wrong here.
238 }
239
240 void
241 mono_threads_platform_register (MonoThreadInfo *info)
242 {
243 #ifdef TARGET_OSX
244         info->native_handle = mach_thread_self ();
245 #endif
246
247         //See the above for what's wrong here.
248 }
249
250 void
251 mono_threads_core_begin_global_suspend (void)
252 {
253         mono_polling_required = 1;
254 }
255
256 void
257 mono_threads_core_end_global_suspend (void)
258 {
259         mono_polling_required = 0;
260 }
261
262 void*
263 mono_threads_enter_gc_unsafe_region (void)
264 {
265         return mono_threads_reset_blocking_start ();
266 }
267
268 void
269 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
270 {
271         mono_threads_reset_blocking_end (regions_cookie);
272 }
273
274 #else
275
276 void*
277 mono_threads_enter_gc_unsafe_region (void)
278 {
279         return NULL;
280 }
281
282 void
283 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
284 {
285 }
286
287 #endif