Merge pull request #2025 from sawachika-kenji/patch-1
[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 <config.h>
11
12 /* enable pthread extensions */
13 #ifdef TARGET_MACH
14 #define _DARWIN_C_SOURCE
15 #endif
16
17 #include <mono/utils/mono-compiler.h>
18 #include <mono/utils/mono-semaphore.h>
19 #include <mono/utils/mono-threads.h>
20 #include <mono/utils/mono-tls.h>
21 #include <mono/utils/hazard-pointer.h>
22 #include <mono/utils/mono-memory-model.h>
23 #include <mono/utils/mono-mmap.h>
24 #include <mono/utils/atomic.h>
25 #include <mono/utils/mono-time.h>
26 #include <mono/utils/mono-counters.h>
27
28 #ifdef TARGET_OSX
29 #include <mono/utils/mach-support.h>
30 #endif
31
32 #ifdef USE_COOP_BACKEND
33
34 volatile size_t mono_polling_required;
35
36 static int coop_reset_blocking_count, coop_try_blocking_count, coop_do_blocking_count, coop_do_polling_count, coop_save_count;
37
38 void
39 mono_threads_state_poll (void)
40 {
41         MonoThreadInfo *info;
42         ++coop_do_polling_count;
43
44         info = mono_thread_info_current_unchecked ();
45         if (!info)
46                 return;
47         THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
48
49         /* Fast check for pending suspend requests */
50         if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
51                 return;
52
53         ++coop_save_count;
54         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
55
56         /* commit the saved state and notify others if needed */
57         switch (mono_threads_transition_state_poll (info)) {
58         case SelfSuspendResumed:
59                 return;
60         case SelfSuspendWait:
61                 mono_thread_info_wait_for_resume (info);
62                 break;
63         case SelfSuspendNotifyAndWait:
64                 mono_threads_notify_initiator_of_suspend (info);
65                 mono_thread_info_wait_for_resume (info);
66                 break;
67         }
68 }
69
70 void*
71 mono_threads_prepare_blocking (void)
72 {
73         MonoThreadInfo *info;
74         ++coop_do_blocking_count;
75
76         info = mono_thread_info_current_unchecked ();
77         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
78         if (!info || !mono_thread_info_is_live (info)) {
79                 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
80                 return NULL;
81         }
82
83 retry:
84         ++coop_save_count;
85         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
86
87         switch (mono_threads_transition_do_blocking (info)) {
88         case DoBlockingContinue:
89                 break;
90         case DoBlockingPollAndRetry:
91                 mono_threads_state_poll ();
92                 goto retry;
93         }
94
95         return info;
96 }
97
98 void
99 mono_threads_finish_blocking (void *cookie)
100 {
101         static gboolean warned_about_bad_transition;
102         MonoThreadInfo *info = cookie;
103
104         if (!info)
105                 return;
106
107         g_assert (info == mono_thread_info_current_unchecked ());
108
109         switch (mono_threads_transition_done_blocking (info)) {
110         case DoneBlockingAborted:
111                 if (!warned_about_bad_transition) {
112                         warned_about_bad_transition = TRUE;
113                         g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
114                 }
115                 mono_threads_state_poll ();
116                 break;
117         case DoneBlockingOk:
118                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
119                 break;
120         case DoneBlockingWait:
121                 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
122                 mono_thread_info_wait_for_resume (info);
123                 break;
124         default:
125                 g_error ("Unknown thread state");
126         }
127 }
128
129
130 void*
131 mono_threads_reset_blocking_start (void)
132 {
133         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
134         ++coop_reset_blocking_count;
135
136         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
137         if (!info || !mono_thread_info_is_live (info))
138                 return NULL;
139
140         switch (mono_threads_transition_abort_blocking (info)) {
141         case AbortBlockingIgnore:
142                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
143                 return NULL;
144         case AbortBlockingIgnoreAndPoll:
145                 mono_threads_state_poll ();
146                 return NULL;
147         case AbortBlockingOk:
148                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
149                 return info;
150         case AbortBlockingOkAndPool:
151                 mono_threads_state_poll ();
152                 return info;
153         default:
154                 g_error ("Unknown thread state");
155         }
156 }
157
158 void
159 mono_threads_reset_blocking_end (void *cookie)
160 {
161         MonoThreadInfo *info = cookie;
162
163         if (!info)
164                 return;
165
166         g_assert (info == mono_thread_info_current_unchecked ());
167         mono_threads_prepare_blocking ();
168 }
169
170 void*
171 mono_threads_try_prepare_blocking (void)
172 {
173         MonoThreadInfo *info;
174         ++coop_try_blocking_count;
175
176         info = mono_thread_info_current_unchecked ();
177         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
178         if (!info || !mono_thread_info_is_live (info) || mono_thread_info_current_state (info) == STATE_BLOCKING) {
179                 THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
180                 return NULL;
181         }
182
183 retry:
184         ++coop_save_count;
185         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
186
187         switch (mono_threads_transition_do_blocking (info)) {
188         case DoBlockingContinue:
189                 break;
190         case DoBlockingPollAndRetry:
191                 mono_threads_state_poll ();
192                 goto retry;
193         }
194
195         return info;
196 }
197
198 void
199 mono_threads_finish_try_blocking (void* cookie)
200 {
201         mono_threads_finish_blocking (cookie);
202 }
203
204 gboolean
205 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
206 {
207         g_error ("FIXME");
208         return FALSE;
209 }
210
211 gboolean
212 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
213 {
214         mono_threads_add_to_pending_operation_set (info);
215         /* There's nothing else to do after we async request the thread to suspend */
216         return TRUE;
217 }
218
219 gboolean
220 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
221 {
222         /* Async suspend can't async fail on coop */
223         return TRUE;
224 }
225
226 void
227 mono_threads_init_platform (void)
228 {
229         mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
230         mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
231         mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
232         mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
233         mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
234         //See the above for what's wrong here.
235 }
236
237 void
238 mono_threads_platform_free (MonoThreadInfo *info)
239 {
240 #ifdef TARGET_MACH
241         mach_port_deallocate (current_task (), info->native_handle);
242 #endif
243
244         //See the above for what's wrong here.
245 }
246
247 void
248 mono_threads_platform_register (MonoThreadInfo *info)
249 {
250 #ifdef TARGET_MACH
251         char thread_name [64];
252
253         info->native_handle = mach_thread_self ();
254         snprintf (thread_name, 64, "tid_%x", (int)info->native_handle);
255         pthread_setname_np (thread_name);
256 #endif
257
258         //See the above for what's wrong here.
259 }
260
261 void
262 mono_threads_core_begin_global_suspend (void)
263 {
264         mono_polling_required = 1;
265 }
266
267 void
268 mono_threads_core_end_global_suspend (void)
269 {
270         mono_polling_required = 0;
271 }
272
273 void*
274 mono_threads_enter_gc_unsafe_region (void)
275 {
276         return mono_threads_reset_blocking_start ();
277 }
278
279 void
280 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
281 {
282         mono_threads_reset_blocking_end (regions_cookie);
283 }
284
285 #else
286
287 void*
288 mono_threads_enter_gc_unsafe_region (void)
289 {
290         return NULL;
291 }
292
293 void
294 mono_threads_exit_gc_unsafe_region (void *regions_cookie)
295 {
296 }
297
298 #endif