Handle ENETDOWN error if defined.
[mono.git] / mono / utils / mono-threads-posix.c
1 /*
2  * mono-threads-posix.c: Low-level threading, posix version
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2011 Novell, Inc
8  */
9
10 #include <config.h>
11
12 #include <mono/utils/mono-compiler.h>
13 #include <mono/utils/mono-semaphore.h>
14 #include <mono/utils/mono-threads.h>
15 #include <mono/utils/mono-tls.h>
16 #include <mono/metadata/threads-types.h>
17
18 #include <errno.h>
19
20 #if defined(PLATFORM_ANDROID)
21 extern int tkill (pid_t tid, int signal);
22 #endif
23
24 #if defined(_POSIX_VERSION) || defined(__native_client__)
25 #include <signal.h>
26
27 typedef struct {
28         void *(*start_routine)(void*);
29         void *arg;
30         int flags;
31         MonoSemType registered;
32 } ThreadStartInfo;
33
34
35 static void*
36 inner_start_thread (void *arg)
37 {
38         ThreadStartInfo *start_info = arg;
39         void *t_arg = start_info->arg;
40         int post_result;
41         void *(*start_func)(void*) = start_info->start_routine;
42         void *result;
43
44         mono_thread_info_attach (&result);
45
46         post_result = MONO_SEM_POST (&(start_info->registered));
47         g_assert (!post_result);
48
49         result = start_func (t_arg);
50         g_assert (!mono_domain_get ());
51
52
53         return result;
54 }
55
56 int
57 mono_threads_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
58 {
59         ThreadStartInfo *start_info;
60         int result;
61
62         start_info = g_malloc0 (sizeof (ThreadStartInfo));
63         if (!start_info)
64                 return ENOMEM;
65         MONO_SEM_INIT (&(start_info->registered), 0);
66         start_info->arg = arg;
67         start_info->start_routine = start_routine;
68
69         result = mono_threads_get_callbacks ()->mono_gc_pthread_create (new_thread, attr, inner_start_thread, start_info);
70         if (result == 0) {
71                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
72                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
73                 }
74         }
75         MONO_SEM_DESTROY (&(start_info->registered));
76         g_free (start_info);
77         return result;
78 }
79
80 #if !defined (__MACH__)
81
82 #if !defined(__native_client__)
83 static void
84 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
85 {
86         MonoThreadInfo *current = mono_thread_info_current ();
87         gboolean ret;
88         
89         if (current->syscall_break_signal) {
90                 current->syscall_break_signal = FALSE;
91                 return;
92         }
93
94         ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->suspend_state, context);
95
96         /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
97         current->suspend_can_continue = ret;
98
99         MONO_SEM_POST (&current->suspend_semaphore);
100
101         /* This thread is doomed, all we can do is give up and let the suspender recover. */
102         if (!ret)
103                 return;
104
105         while (MONO_SEM_WAIT (&current->resume_semaphore) != 0) {
106                 /*if (EINTR != errno) ABORT("sem_wait failed"); */
107         }
108
109         if (current->async_target) {
110 #if MONO_ARCH_HAS_MONO_CONTEXT
111                 MonoContext tmp = current->suspend_state.ctx;
112                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
113                 current->async_target = current->user_data = NULL;
114                 mono_monoctx_to_sigctx (&tmp, context);
115 #else
116                 g_error ("The new interruption machinery requires a working mono-context");
117 #endif
118         }
119
120         MONO_SEM_POST (&current->finish_resume_semaphore);
121 }
122 #endif
123
124 static void
125 mono_posix_add_signal_handler (int signo, gpointer handler)
126 {
127 #if !defined(__native_client__)
128         /*FIXME, move the code from mini to utils and do the right thing!*/
129         struct sigaction sa;
130         struct sigaction previous_sa;
131         int ret;
132
133         sa.sa_sigaction = handler;
134         sigemptyset (&sa.sa_mask);
135         sa.sa_flags = SA_SIGINFO;
136         ret = sigaction (signo, &sa, &previous_sa);
137
138         g_assert (ret != -1);
139 #endif
140 }
141
142 void
143 mono_threads_init_platform (void)
144 {
145 #if !defined(__native_client__)
146         /*
147         FIXME we should use all macros from mini to make this more portable
148         FIXME it would be very sweet if sgen could end up using this too.
149         */
150         if (mono_thread_info_new_interrupt_enabled ())
151                 mono_posix_add_signal_handler (mono_thread_get_abort_signal (), suspend_signal_handler);
152 #endif
153 }
154
155 /*nothing to be done here since suspend always abort syscalls due using signals*/
156 void
157 mono_threads_core_interrupt (MonoThreadInfo *info)
158 {
159 }
160
161 int
162 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
163 {
164 #if defined (PLATFORM_ANDROID)
165         int result, old_errno = errno;
166         result = tkill (info->native_handle, signum);
167         if (result < 0) {
168                 result = errno;
169                 errno = old_errno;
170         }
171         return result;
172 #elif defined(__native_client__)
173         /* Workaround pthread_kill abort() in NaCl glibc. */
174         return 0;
175 #else
176         return pthread_kill (mono_thread_info_get_tid (info), signum);
177 #endif
178
179 }
180
181 void
182 mono_threads_core_abort_syscall (MonoThreadInfo *info)
183 {
184         /*
185         We signal a thread to break it from the urrent syscall.
186         This signal should not be interpreted as a suspend request.
187         */
188         info->syscall_break_signal = TRUE;
189         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
190 }
191
192 gboolean
193 mono_threads_core_needs_abort_syscall (void)
194 {
195         return TRUE;
196 }
197
198 gboolean
199 mono_threads_core_suspend (MonoThreadInfo *info)
200 {
201         /*FIXME, check return value*/
202         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
203         while (MONO_SEM_WAIT (&info->suspend_semaphore) != 0) {
204                 /* g_assert (errno == EINTR); */
205         }
206         return info->suspend_can_continue;
207 }
208
209 gboolean
210 mono_threads_core_resume (MonoThreadInfo *info)
211 {
212         MONO_SEM_POST (&info->resume_semaphore);
213         while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
214                 /* g_assert (errno == EINTR); */
215         }
216
217         return TRUE;
218 }
219
220 void
221 mono_threads_platform_register (MonoThreadInfo *info)
222 {
223         MONO_SEM_INIT (&info->suspend_semaphore, 0);
224
225 #if defined (PLATFORM_ANDROID)
226         info->native_handle = (gpointer) gettid ();
227 #endif
228 }
229
230 void
231 mono_threads_platform_free (MonoThreadInfo *info)
232 {
233         MONO_SEM_DESTROY (&info->suspend_semaphore);
234 }
235
236 MonoNativeThreadId
237 mono_native_thread_id_get (void)
238 {
239         return pthread_self ();
240 }
241
242 gboolean
243 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
244 {
245         return pthread_equal (id1, id2);
246 }
247
248 /*
249  * mono_native_thread_create:
250  *
251  *   Low level thread creation function without any GC wrappers.
252  */
253 gboolean
254 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
255 {
256         return pthread_create (tid, NULL, func, arg) == 0;
257 }
258
259 #endif /*!defined (__MACH__)*/
260
261 #endif