Merge pull request #2001 from esdrubal/get_module_filename
[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 void
198 mono_threads_core_abort_syscall (MonoThreadInfo *info)
199 {
200         g_error ("FIXME");
201 }
202
203 gboolean
204 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
205 {
206         g_error ("FIXME");
207         return FALSE;
208 }
209
210 gboolean
211 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
212 {
213         mono_threads_add_to_pending_operation_set (info);
214         /* There's nothing else to do after we async request the thread to suspend */
215         return TRUE;
216 }
217
218 gboolean
219 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
220 {
221         /* Async suspend can't async fail on coop */
222         return TRUE;
223 }
224
225 gboolean
226 mono_threads_core_needs_abort_syscall (void)
227 {
228         /*
229         Small digression.
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).
232
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.
237         */
238         return FALSE;
239 }
240
241 void
242 mono_threads_init_platform (void)
243 {
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.
250 }
251
252 void
253 mono_threads_platform_free (MonoThreadInfo *info)
254 {
255 #ifdef TARGET_OSX
256         mach_port_deallocate (current_task (), info->native_handle);
257 #endif
258
259         //See the above for what's wrong here.
260 }
261
262 void
263 mono_threads_platform_register (MonoThreadInfo *info)
264 {
265 #ifdef TARGET_OSX
266         info->native_handle = mach_thread_self ();
267 #endif
268
269         //See the above for what's wrong here.
270 }
271
272 void
273 mono_threads_core_begin_global_suspend (void)
274 {
275         mono_polling_required = 1;
276 }
277
278 void
279 mono_threads_core_end_global_suspend (void)
280 {
281         mono_polling_required = 0;
282 }
283
284 void*
285 mono_threads_enter_gc_unsafe_region (void)
286 {
287         return mono_threads_reset_blocking_start ();
288 }
289
290 void*
291 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
292 {
293         mono_threads_reset_blocking_end (regions_cookie);
294 }
295
296 #else
297
298 void*
299 mono_threads_enter_gc_unsafe_region (void)
300 {
301         return NULL;
302 }
303
304 void*
305 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
306 {
307 }
308
309 #endif