Rewrite of core coder & decoder functions to fix several bugs and limitations, and...
[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 #else
173         return pthread_kill (mono_thread_info_get_tid (info), signum);
174 #endif
175
176 }
177
178 void
179 mono_threads_core_abort_syscall (MonoThreadInfo *info)
180 {
181         /*
182         We signal a thread to break it from the urrent syscall.
183         This signal should not be interpreted as a suspend request.
184         */
185         info->syscall_break_signal = TRUE;
186         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
187 }
188
189 gboolean
190 mono_threads_core_needs_abort_syscall (void)
191 {
192         return TRUE;
193 }
194
195 gboolean
196 mono_threads_core_suspend (MonoThreadInfo *info)
197 {
198         /*FIXME, check return value*/
199         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
200         while (MONO_SEM_WAIT (&info->suspend_semaphore) != 0) {
201                 /* g_assert (errno == EINTR); */
202         }
203         return info->suspend_can_continue;
204 }
205
206 gboolean
207 mono_threads_core_resume (MonoThreadInfo *info)
208 {
209         MONO_SEM_POST (&info->resume_semaphore);
210         while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
211                 /* g_assert (errno == EINTR); */
212         }
213
214         return TRUE;
215 }
216
217 void
218 mono_threads_platform_register (MonoThreadInfo *info)
219 {
220         MONO_SEM_INIT (&info->suspend_semaphore, 0);
221
222 #if defined (PLATFORM_ANDROID)
223         info->native_handle = (gpointer) gettid ();
224 #endif
225 }
226
227 void
228 mono_threads_platform_free (MonoThreadInfo *info)
229 {
230         MONO_SEM_DESTROY (&info->suspend_semaphore);
231 }
232
233 MonoNativeThreadId
234 mono_native_thread_id_get (void)
235 {
236         return pthread_self ();
237 }
238
239 gboolean
240 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
241 {
242         return pthread_equal (id1, id2);
243 }
244
245 /*
246  * mono_native_thread_create:
247  *
248  *   Low level thread creation function without any GC wrappers.
249  */
250 gboolean
251 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
252 {
253         return pthread_create (tid, NULL, func, arg) == 0;
254 }
255
256 #endif /*!defined (__MACH__)*/
257
258 #endif