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