Merge pull request #1870 from saper/langinfo_h
[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 USE_COOP_BACKEND
22
23 volatile size_t mono_polling_required;
24
25 static int coop_reset_blocking_count, coop_try_blocking_count, coop_do_blocking_count, coop_do_polling_count, coop_save_count;
26
27 void
28 mono_threads_state_poll (void)
29 {
30         MonoThreadInfo *info;
31         ++coop_do_polling_count;
32
33         info = mono_thread_info_current_unchecked ();
34         if (!info)
35                 return;
36         THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
37
38         /* Fast check for pending suspend requests */
39         if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
40                 return;
41
42         ++coop_save_count;
43         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
44
45         /* commit the saved state and notify others if needed */
46         switch (mono_threads_transition_state_poll (info)) {
47         case SelfSuspendResumed:
48                 return;
49         case SelfSuspendWait:
50                 mono_thread_info_wait_for_resume (info);
51                 break;
52         case SelfSuspendNotifyAndWait:
53                 mono_threads_notify_initiator_of_suspend (info);
54                 mono_thread_info_wait_for_resume (info);
55                 break;
56         }
57 }
58
59 void*
60 mono_threads_prepare_blocking (void)
61 {
62         MonoThreadInfo *info;
63         ++coop_do_blocking_count;
64
65         info = mono_thread_info_current_unchecked ();
66         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
67         if (!info || !mono_thread_info_is_live (info)) {
68                 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
69                 return NULL;
70         }
71
72 retry:
73         ++coop_save_count;
74         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
75
76         switch (mono_threads_transition_do_blocking (info)) {
77         case DoBlockingContinue:
78                 break;
79         case DoBlockingPollAndRetry:
80                 mono_threads_state_poll ();
81                 goto retry;
82         }
83
84         return info;
85 }
86
87 void
88 mono_threads_finish_blocking (void *cookie)
89 {
90         static gboolean warned_about_bad_transition;
91         MonoThreadInfo *info = cookie;
92
93         if (!info)
94                 return;
95
96         g_assert (info == mono_thread_info_current_unchecked ());
97
98         switch (mono_threads_transition_done_blocking (info)) {
99         case DoneBlockingAborted:
100                 if (!warned_about_bad_transition) {
101                         warned_about_bad_transition = TRUE;
102                         g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
103                 }
104                 mono_threads_state_poll ();
105                 break;
106         case DoneBlockingOk:
107                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
108                 break;
109         case DoneBlockingWait:
110                 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
111                 mono_thread_info_wait_for_resume (info);
112                 break;
113         default:
114                 g_error ("Unknown thread state");
115         }
116 }
117
118
119 void*
120 mono_threads_reset_blocking_start (void)
121 {
122         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
123         ++coop_reset_blocking_count;
124
125         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
126         if (!info || !mono_thread_info_is_live (info))
127                 return NULL;
128
129         switch (mono_threads_transition_abort_blocking (info)) {
130         case AbortBlockingIgnore:
131                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
132                 return NULL;
133         case AbortBlockingIgnoreAndPoll:
134                 mono_threads_state_poll ();
135                 return NULL;
136         case AbortBlockingOk:
137                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
138                 return info;
139         case AbortBlockingOkAndPool:
140                 mono_threads_state_poll ();
141                 return info;
142         default:
143                 g_error ("Unknown thread state");
144         }
145 }
146
147 void
148 mono_threads_reset_blocking_end (void *cookie)
149 {
150         MonoThreadInfo *info = cookie;
151
152         if (!info)
153                 return;
154
155         g_assert (info == mono_thread_info_current_unchecked ());
156         mono_threads_prepare_blocking ();
157 }
158
159 void*
160 mono_threads_try_prepare_blocking (void)
161 {
162         MonoThreadInfo *info;
163         ++coop_try_blocking_count;
164
165         info = mono_thread_info_current_unchecked ();
166         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
167         if (!info || !mono_thread_info_is_live (info) || mono_thread_info_current_state (info) == STATE_BLOCKING) {
168                 THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
169                 return NULL;
170         }
171
172 retry:
173         ++coop_save_count;
174         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
175
176         switch (mono_threads_transition_do_blocking (info)) {
177         case DoBlockingContinue:
178                 break;
179         case DoBlockingPollAndRetry:
180                 mono_threads_state_poll ();
181                 goto retry;
182         }
183
184         return info;
185 }
186
187 void
188 mono_threads_finish_try_blocking (void* cookie)
189 {
190         mono_threads_finish_blocking (cookie);
191 }
192
193 void
194 mono_threads_core_abort_syscall (MonoThreadInfo *info)
195 {
196         g_error ("FIXME");
197 }
198
199 gboolean
200 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
201 {
202         g_error ("FIXME");
203         return FALSE;
204 }
205
206 gboolean
207 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
208 {
209         mono_threads_add_to_pending_operation_set (info);
210         /* There's nothing else to do after we async request the thread to suspend */
211         return TRUE;
212 }
213
214 gboolean
215 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
216 {
217         /* Async suspend can't async fail on coop */
218         return TRUE;
219 }
220
221 gboolean
222 mono_threads_core_needs_abort_syscall (void)
223 {
224         /*
225         Small digression.
226         Syscall abort can't be handled by the suspend machinery even though it's kind of implemented
227         in a similar way (with, like, signals).
228
229         So, having it here is wrong, it should be on mono-threads-(mach|posix|windows).
230         Ideally we would slice this in (coop|preemp) and target. Then have this file set:
231         mono-threads-mach, mono-threads-mach-preempt and mono-threads-mach-coop.
232         More files, less ifdef hell.
233         */
234         return FALSE;
235 }
236
237 void
238 mono_threads_init_platform (void)
239 {
240         mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
241         mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
242         mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
243         mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
244         mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
245         //See the above for what's wrong here.
246 }
247
248 void
249 mono_threads_platform_free (MonoThreadInfo *info)
250 {
251         //See the above for what's wrong here.
252 }
253
254 void
255 mono_threads_platform_register (MonoThreadInfo *info)
256 {
257         //See the above for what's wrong here.
258 }
259
260 void
261 mono_threads_core_begin_global_suspend (void)
262 {
263         mono_polling_required = 1;
264 }
265
266 void
267 mono_threads_core_end_global_suspend (void)
268 {
269         mono_polling_required = 0;
270 }
271
272
273 #endif