[runtime] Get rid of SuspendThread()/ResumeThread(), integrate its functionality...
[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/utils/gc_wrapper.h>
17 #include <mono/metadata/threads-types.h>
18
19 #include <errno.h>
20
21 #if defined(PLATFORM_ANDROID)
22 extern int tkill (pid_t tid, int signal);
23 #endif
24
25 #if defined(_POSIX_VERSION) || defined(__native_client__)
26 #include <signal.h>
27
28 #if defined(__native_client__)
29 void nacl_shutdown_gc_thread(void);
30 #endif
31
32 typedef struct {
33         void *(*start_routine)(void*);
34         void *arg;
35         int flags;
36         MonoSemType registered;
37         HANDLE handle;
38 } StartInfo;
39
40 static void*
41 inner_start_thread (void *arg)
42 {
43         StartInfo *start_info = arg;
44         void *t_arg = start_info->arg;
45         int res;
46         void *(*start_func)(void*) = start_info->start_routine;
47         guint32 flags = start_info->flags;
48         void *result;
49         HANDLE handle;
50         MonoThreadInfo *info;
51
52         /* Register the thread with the io-layer */
53         handle = wapi_create_thread_handle ();
54         if (!handle) {
55                 res = MONO_SEM_POST (&(start_info->registered));
56                 g_assert (!res);
57                 return NULL;
58         }
59         start_info->handle = handle;
60
61         if (!(flags & CREATE_NO_DETACH)) {
62                 res = mono_gc_pthread_detach (pthread_self ());
63                 g_assert (!res);
64         }
65
66         info = mono_thread_info_attach (&result);
67         info->runtime_thread = TRUE;
68
69         if (flags & CREATE_SUSPENDED) {
70                 info->create_suspended = TRUE;
71                 MONO_SEM_INIT (&info->create_suspended_sem, 0);
72         }
73
74         /* start_info is not valid after this */
75         res = MONO_SEM_POST (&(start_info->registered));
76         g_assert (!res);
77         start_info = NULL;
78
79         if (flags & CREATE_SUSPENDED) {
80                 while (MONO_SEM_WAIT (&info->create_suspended_sem) != 0 &&
81                            errno == EINTR);
82                 MONO_SEM_DESTROY (&info->create_suspended_sem);
83         }
84
85         /* Run the actual main function of the thread */
86         result = start_func (t_arg);
87
88         /*
89         g_assert (!mono_domain_get ());
90         mono_thread_info_dettach ();
91         */
92
93 #if defined(__native_client__)
94         nacl_shutdown_gc_thread();
95 #endif
96
97         wapi_thread_set_exit_code (GPOINTER_TO_UINT (result), handle);
98
99         // FIXME: Why is this needed ?
100         mono_gc_pthread_exit (NULL);
101
102         g_assert_not_reached ();
103         return result;
104 }
105
106 HANDLE
107 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
108 {
109         pthread_attr_t attr;
110         int res;
111         pthread_t thread;
112         StartInfo start_info;
113
114         res = pthread_attr_init (&attr);
115         g_assert (!res);
116
117         if (stack_size == 0) {
118 #if HAVE_VALGRIND_MEMCHECK_H
119                 if (RUNNING_ON_VALGRIND)
120                         stack_size = 1 << 20;
121                 else
122                         stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
123 #else
124                 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
125 #endif
126         }
127
128 #ifdef PTHREAD_STACK_MIN
129         if (stack_size < PTHREAD_STACK_MIN)
130                 stack_size = PTHREAD_STACK_MIN;
131 #endif
132
133 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
134         res = pthread_attr_setstacksize (&attr, stack_size);
135         g_assert (!res);
136 #endif
137
138         memset (&start_info, 0, sizeof (StartInfo));
139         start_info.start_routine = (gpointer)start_routine;
140         start_info.arg = arg;
141         start_info.flags = creation_flags;
142         MONO_SEM_INIT (&(start_info.registered), 0);
143
144         /* Actually start the thread */
145         res = mono_threads_get_callbacks ()->mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
146         if (res) {
147                 // FIXME:
148                 g_assert_not_reached ();
149         }
150
151         /* Wait until the thread register itself in various places */
152         while (MONO_SEM_WAIT (&(start_info.registered)) != 0) {
153                 /*if (EINTR != errno) ABORT("sem_wait failed"); */
154         }
155         MONO_SEM_DESTROY (&(start_info.registered));
156
157         if (out_tid)
158                 *out_tid = thread;
159
160         return start_info.handle;
161 }
162
163 /*
164  * mono_threads_core_resume_created:
165  *
166  *   Resume a newly created thread created using CREATE_SUSPENDED.
167  */
168 void
169 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
170 {
171         MONO_SEM_POST (&info->create_suspended_sem);
172 }
173
174 #if !defined (__MACH__)
175
176 #if !defined(__native_client__)
177 static void
178 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
179 {
180         MonoThreadInfo *current = mono_thread_info_current ();
181         gboolean ret;
182         
183         if (current->syscall_break_signal) {
184                 current->syscall_break_signal = FALSE;
185                 return;
186         }
187
188         ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->suspend_state, context);
189
190         /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
191         current->suspend_can_continue = ret;
192
193         MONO_SEM_POST (&current->begin_suspend_semaphore);
194
195         /* This thread is doomed, all we can do is give up and let the suspender recover. */
196         if (!ret)
197                 return;
198
199         while (MONO_SEM_WAIT (&current->resume_semaphore) != 0) {
200                 /*if (EINTR != errno) ABORT("sem_wait failed"); */
201         }
202
203         if (current->async_target) {
204 #if MONO_ARCH_HAS_MONO_CONTEXT
205                 MonoContext tmp = current->suspend_state.ctx;
206                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
207                 current->async_target = current->user_data = NULL;
208                 mono_monoctx_to_sigctx (&tmp, context);
209 #else
210                 g_error ("The new interruption machinery requires a working mono-context");
211 #endif
212         }
213
214         MONO_SEM_POST (&current->finish_resume_semaphore);
215 }
216 #endif
217
218 static void
219 mono_posix_add_signal_handler (int signo, gpointer handler)
220 {
221 #if !defined(__native_client__)
222         /*FIXME, move the code from mini to utils and do the right thing!*/
223         struct sigaction sa;
224         struct sigaction previous_sa;
225         int ret;
226
227         sa.sa_sigaction = handler;
228         sigemptyset (&sa.sa_mask);
229         sa.sa_flags = SA_SIGINFO;
230         ret = sigaction (signo, &sa, &previous_sa);
231
232         g_assert (ret != -1);
233 #endif
234 }
235
236 void
237 mono_threads_init_platform (void)
238 {
239 #if !defined(__native_client__)
240         /*
241         FIXME we should use all macros from mini to make this more portable
242         FIXME it would be very sweet if sgen could end up using this too.
243         */
244         if (mono_thread_info_new_interrupt_enabled ())
245                 mono_posix_add_signal_handler (mono_thread_get_abort_signal (), suspend_signal_handler);
246 #endif
247 }
248
249 /*nothing to be done here since suspend always abort syscalls due using signals*/
250 void
251 mono_threads_core_interrupt (MonoThreadInfo *info)
252 {
253 }
254
255 int
256 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
257 {
258 #if defined (PLATFORM_ANDROID)
259         int result, old_errno = errno;
260         result = tkill (info->native_handle, signum);
261         if (result < 0) {
262                 result = errno;
263                 errno = old_errno;
264         }
265         return result;
266 #elif defined(__native_client__)
267         /* Workaround pthread_kill abort() in NaCl glibc. */
268         return 0;
269 #else
270         return pthread_kill (mono_thread_info_get_tid (info), signum);
271 #endif
272
273 }
274
275 void
276 mono_threads_core_abort_syscall (MonoThreadInfo *info)
277 {
278         /*
279         We signal a thread to break it from the urrent syscall.
280         This signal should not be interpreted as a suspend request.
281         */
282         info->syscall_break_signal = TRUE;
283         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
284 }
285
286 gboolean
287 mono_threads_core_needs_abort_syscall (void)
288 {
289         return TRUE;
290 }
291
292 gboolean
293 mono_threads_core_suspend (MonoThreadInfo *info)
294 {
295         /*FIXME, check return value*/
296         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
297         while (MONO_SEM_WAIT (&info->begin_suspend_semaphore) != 0) {
298                 /* g_assert (errno == EINTR); */
299         }
300         return info->suspend_can_continue;
301 }
302
303 gboolean
304 mono_threads_core_resume (MonoThreadInfo *info)
305 {
306         MONO_SEM_POST (&info->resume_semaphore);
307         while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
308                 /* g_assert (errno == EINTR); */
309         }
310
311         return TRUE;
312 }
313
314 void
315 mono_threads_platform_register (MonoThreadInfo *info)
316 {
317         MONO_SEM_INIT (&info->begin_suspend_semaphore, 0);
318
319 #if defined (PLATFORM_ANDROID)
320         info->native_handle = (gpointer) gettid ();
321 #endif
322 }
323
324 void
325 mono_threads_platform_free (MonoThreadInfo *info)
326 {
327         MONO_SEM_DESTROY (&info->begin_suspend_semaphore);
328 }
329
330 MonoNativeThreadId
331 mono_native_thread_id_get (void)
332 {
333         return pthread_self ();
334 }
335
336 gboolean
337 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
338 {
339         return pthread_equal (id1, id2);
340 }
341
342 /*
343  * mono_native_thread_create:
344  *
345  *   Low level thread creation function without any GC wrappers.
346  */
347 gboolean
348 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
349 {
350         return pthread_create (tid, NULL, func, arg) == 0;
351 }
352
353 #endif /*!defined (__MACH__)*/
354
355 #endif