[System] Process.WaitForExit now triggers event Exited.
[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
20 #ifdef USE_COOP_BACKEND
21
22
23 void
24 mono_threads_state_poll (void)
25 {
26         MonoThreadInfo *info;
27
28         info = mono_thread_info_current_unchecked ();
29         if (!info)
30                 return;
31         THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
32
33         /* Fast check for pending suspend requests */
34         if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
35                 return;
36
37         g_assert (mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL));
38
39         /* commit the saved state and notify others if needed */
40         switch (mono_threads_transition_state_poll (info)) {
41         case SelfSuspendResumed:
42                 return;
43         case SelfSuspendWait:
44                 mono_thread_info_wait_for_resume (info);
45                 break;
46         case SelfSuspendNotifyAndWait:
47                 mono_threads_notify_initiator_of_suspend (info);
48                 mono_thread_info_wait_for_resume (info);
49                 break;
50         }
51 }
52
53 void*
54 mono_threads_prepare_blocking (void)
55 {
56         MonoThreadInfo *info;
57
58         info = mono_thread_info_current_unchecked ();
59         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
60         if (!info || !mono_thread_info_is_live (info)) {
61                 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
62                 return NULL;
63         }
64
65 retry:
66         /*The JIT might not be able to save*/
67         if (!mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL)) {
68                 THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p to save thread state\n", mono_thread_info_get_tid (info));
69                 return NULL;
70         }
71
72         switch (mono_threads_transition_do_blocking (info)) {
73         case DoBlockingContinue:
74                 break;
75         case DoBlockingPollAndRetry:
76                 mono_threads_state_poll ();
77                 goto retry;
78         }
79
80         return info;
81 }
82
83 void
84 mono_threads_finish_blocking (void *cookie)
85 {
86         static gboolean warned_about_bad_transition;
87         MonoThreadInfo *info = cookie;
88
89         if (!info)
90                 return;
91
92         g_assert (info == mono_thread_info_current_unchecked ());
93
94         switch (mono_threads_transition_done_blocking (info)) {
95         case DoneBlockingAborted:
96                 if (!warned_about_bad_transition) {
97                         warned_about_bad_transition = TRUE;
98                         g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info));
99                 }
100                 mono_threads_state_poll ();
101                 break;
102         case DoneBlockingOk:
103                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
104                 break;
105         case DoneBlockingWait:
106                 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
107                 mono_thread_info_wait_for_resume (info);
108                 break;
109         default:
110                 g_error ("Unknown thread state");
111         }
112 }
113
114
115 void*
116 mono_threads_reset_blocking_start (void)
117 {
118         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
119
120         /* If the thread is not attached, it doesn't make sense prepare for suspend. */
121         if (!info || !mono_thread_info_is_live (info))
122                 return NULL;
123
124         switch (mono_threads_transition_abort_blocking (info)) {
125         case AbortBlockingIgnore:
126                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
127                 return NULL;
128         case AbortBlockingIgnoreAndPoll:
129                 mono_threads_state_poll ();
130                 return NULL;
131         case AbortBlockingOk:
132                 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
133                 return info;
134         case AbortBlockingOkAndPool:
135                 mono_threads_state_poll ();
136                 return info;
137         default:
138                 g_error ("Unknown thread state");
139         }
140 }
141
142 void
143 mono_threads_reset_blocking_end (void *cookie)
144 {
145         MonoThreadInfo *info = cookie;
146
147         if (!info)
148                 return;
149
150         g_assert (info == mono_thread_info_current_unchecked ());
151         mono_threads_prepare_blocking ();
152 }
153
154
155 void
156 mono_threads_core_abort_syscall (MonoThreadInfo *info)
157 {
158         g_error ("FIXME");
159 }
160
161 gboolean
162 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
163 {
164         g_error ("FIXME");
165         return FALSE;
166 }
167
168 gboolean
169 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
170 {
171         mono_threads_add_to_pending_operation_set (info);
172         /* There's nothing else to do after we async request the thread to suspend */
173         return TRUE;
174 }
175
176 gboolean
177 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
178 {
179         /* Async suspend can't async fail on coop */
180         return TRUE;
181 }
182
183 gboolean
184 mono_threads_core_needs_abort_syscall (void)
185 {
186         /*
187         Small digression.
188         Syscall abort can't be handled by the suspend machinery even though it's kind of implemented
189         in a similar way (with, like, signals).
190
191         So, having it here is wrong, it should be on mono-threads-(mach|posix|windows).
192         Ideally we would slice this in (coop|preemp) and target. Then have this file set:
193         mono-threads-mach, mono-threads-mach-preempt and mono-threads-mach-coop.
194         More files, less ifdef hell.
195         */
196         return FALSE;
197 }
198
199 void
200 mono_threads_init_platform (void)
201 {
202         //See the above for what's wrong here.
203 }
204
205 void
206 mono_threads_platform_free (MonoThreadInfo *info)
207 {
208         //See the above for what's wrong here.
209 }
210
211 void
212 mono_threads_platform_register (MonoThreadInfo *info)
213 {
214         //See the above for what's wrong here.
215 }
216
217
218 #endif