Merge pull request #350 from robwilkens/bug1089
[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         g_assert (ret);
97
98         MONO_SEM_POST (&current->suspend_semaphore);
99                 
100         while (MONO_SEM_WAIT (&current->resume_semaphore) != 0) {
101                 /*if (EINTR != errno) ABORT("sem_wait failed"); */
102         }
103
104         if (current->async_target) {
105 #if MONO_ARCH_HAS_MONO_CONTEXT
106                 MonoContext tmp = current->suspend_state.ctx;
107                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
108                 current->async_target = current->user_data = NULL;
109                 mono_monoctx_to_sigctx (&tmp, context);
110 #else
111                 g_error ("The new interruption machinery requires a working mono-context");
112 #endif
113         }
114
115         MONO_SEM_POST (&current->finish_resume_semaphore);
116 }
117 #endif
118
119 static void
120 mono_posix_add_signal_handler (int signo, gpointer handler)
121 {
122 #if !defined(__native_client__)
123         /*FIXME, move the code from mini to utils and do the right thing!*/
124         struct sigaction sa;
125         struct sigaction previous_sa;
126         int ret;
127
128         sa.sa_sigaction = handler;
129         sigemptyset (&sa.sa_mask);
130         sa.sa_flags = SA_SIGINFO;
131         ret = sigaction (signo, &sa, &previous_sa);
132
133         g_assert (ret != -1);
134 #endif
135 }
136
137 void
138 mono_threads_init_platform (void)
139 {
140 #if !defined(__native_client__)
141         /*
142         FIXME we should use all macros from mini to make this more portable
143         FIXME it would be very sweet if sgen could end up using this too.
144         */
145         if (mono_thread_info_new_interrupt_enabled ())
146                 mono_posix_add_signal_handler (mono_thread_get_abort_signal (), suspend_signal_handler);
147 #endif
148 }
149
150 /*nothing to be done here since suspend always abort syscalls due using signals*/
151 void
152 mono_threads_core_interrupt (MonoThreadInfo *info)
153 {
154 }
155
156 int
157 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
158 {
159 #if defined (PLATFORM_ANDROID)
160         int result, old_errno = errno;
161         result = tkill (info->native_handle, signum);
162         if (result < 0) {
163                 result = errno;
164                 errno = old_errno;
165         }
166         return result;
167 #else
168         return pthread_kill (mono_thread_info_get_tid (info), signum);
169 #endif
170
171 }
172
173 void
174 mono_threads_core_abort_syscall (MonoThreadInfo *info)
175 {
176         /*
177         We signal a thread to break it from the urrent syscall.
178         This signal should not be interpreted as a suspend request.
179         */
180         info->syscall_break_signal = TRUE;
181         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
182 }
183
184 gboolean
185 mono_threads_core_needs_abort_syscall (void)
186 {
187         return TRUE;
188 }
189
190 gboolean
191 mono_threads_core_suspend (MonoThreadInfo *info)
192 {
193         /*FIXME, check return value*/
194         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
195         while (MONO_SEM_WAIT (&info->suspend_semaphore) != 0) {
196                 /* g_assert (errno == EINTR); */
197         }
198         return TRUE;
199 }
200
201 gboolean
202 mono_threads_core_resume (MonoThreadInfo *info)
203 {
204         MONO_SEM_POST (&info->resume_semaphore);
205         while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
206                 /* g_assert (errno == EINTR); */
207         }
208
209         return TRUE;
210 }
211
212 void
213 mono_threads_platform_register (MonoThreadInfo *info)
214 {
215         MONO_SEM_INIT (&info->suspend_semaphore, 0);
216
217 #if defined (PLATFORM_ANDROID)
218         info->native_handle = (gpointer) gettid ();
219 #endif
220 }
221
222 void
223 mono_threads_platform_free (MonoThreadInfo *info)
224 {
225         MONO_SEM_DESTROY (&info->suspend_semaphore);
226 }
227
228 MonoNativeThreadId
229 mono_native_thread_id_get (void)
230 {
231         return pthread_self ();
232 }
233
234 gboolean
235 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
236 {
237         return pthread_equal (id1, id2);
238 }
239
240 /*
241  * mono_native_thread_create:
242  *
243  *   Low level thread creation function without any GC wrappers.
244  */
245 gboolean
246 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
247 {
248         return pthread_create (tid, NULL, func, arg) == 0;
249 }
250
251 #endif /*!defined (__MACH__)*/
252
253 #endif