Merge pull request #876 from knocte/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 #if defined(__OpenBSD__) || defined(__FreeBSD__)
13 #include <pthread.h>
14 #include <pthread_np.h>
15 #endif
16
17 #include <mono/utils/mono-compiler.h>
18 #include <mono/utils/mono-semaphore.h>
19 #include <mono/utils/mono-threads.h>
20 #include <mono/utils/mono-tls.h>
21 #include <mono/utils/gc_wrapper.h>
22 #include <mono/utils/mono-mmap.h>
23 #include <mono/metadata/threads-types.h>
24
25 #include <errno.h>
26
27 #if defined(PLATFORM_ANDROID)
28 extern int tkill (pid_t tid, int signal);
29 #endif
30
31 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
32 void *pthread_get_stackaddr_np(pthread_t);
33 size_t pthread_get_stacksize_np(pthread_t);
34 #endif
35
36 #if defined(_POSIX_VERSION) || defined(__native_client__)
37 #include <signal.h>
38
39 #if defined(__native_client__)
40 void nacl_shutdown_gc_thread(void);
41 #endif
42
43 typedef struct {
44         void *(*start_routine)(void*);
45         void *arg;
46         int flags;
47         MonoSemType registered;
48         HANDLE handle;
49 } StartInfo;
50
51 static void*
52 inner_start_thread (void *arg)
53 {
54         StartInfo *start_info = arg;
55         void *t_arg = start_info->arg;
56         int res;
57         void *(*start_func)(void*) = start_info->start_routine;
58         guint32 flags = start_info->flags;
59         void *result;
60         HANDLE handle;
61         MonoThreadInfo *info;
62
63         /* Register the thread with the io-layer */
64         handle = wapi_create_thread_handle ();
65         if (!handle) {
66                 res = MONO_SEM_POST (&(start_info->registered));
67                 g_assert (!res);
68                 return NULL;
69         }
70         start_info->handle = handle;
71
72         if (!(flags & CREATE_NO_DETACH)) {
73                 res = mono_gc_pthread_detach (pthread_self ());
74                 g_assert (!res);
75         }
76
77         info = mono_thread_info_attach (&result);
78         info->runtime_thread = TRUE;
79
80         if (flags & CREATE_SUSPENDED) {
81                 info->create_suspended = TRUE;
82                 MONO_SEM_INIT (&info->create_suspended_sem, 0);
83         }
84
85         /* start_info is not valid after this */
86         res = MONO_SEM_POST (&(start_info->registered));
87         g_assert (!res);
88         start_info = NULL;
89
90         if (flags & CREATE_SUSPENDED) {
91                 while (MONO_SEM_WAIT (&info->create_suspended_sem) != 0 &&
92                            errno == EINTR);
93                 MONO_SEM_DESTROY (&info->create_suspended_sem);
94         }
95
96         /* Run the actual main function of the thread */
97         result = start_func (t_arg);
98
99         /*
100         g_assert (!mono_domain_get ());
101         mono_thread_info_dettach ();
102         */
103
104 #if defined(__native_client__)
105         nacl_shutdown_gc_thread();
106 #endif
107
108         wapi_thread_set_exit_code (GPOINTER_TO_UINT (result), handle);
109
110         // FIXME: Why is this needed ?
111         mono_gc_pthread_exit (NULL);
112
113         g_assert_not_reached ();
114         return result;
115 }
116
117 HANDLE
118 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
119 {
120         pthread_attr_t attr;
121         int res;
122         pthread_t thread;
123         StartInfo start_info;
124
125         res = pthread_attr_init (&attr);
126         g_assert (!res);
127
128         if (stack_size == 0) {
129 #if HAVE_VALGRIND_MEMCHECK_H
130                 if (RUNNING_ON_VALGRIND)
131                         stack_size = 1 << 20;
132                 else
133                         stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
134 #else
135                 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
136 #endif
137         }
138
139 #ifdef PTHREAD_STACK_MIN
140         if (stack_size < PTHREAD_STACK_MIN)
141                 stack_size = PTHREAD_STACK_MIN;
142 #endif
143
144 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
145         res = pthread_attr_setstacksize (&attr, stack_size);
146         g_assert (!res);
147 #endif
148
149         memset (&start_info, 0, sizeof (StartInfo));
150         start_info.start_routine = (gpointer)start_routine;
151         start_info.arg = arg;
152         start_info.flags = creation_flags;
153         MONO_SEM_INIT (&(start_info.registered), 0);
154
155         /* Actually start the thread */
156         res = mono_threads_get_callbacks ()->mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
157         if (res) {
158                 // FIXME:
159                 g_assert_not_reached ();
160         }
161
162         /* Wait until the thread register itself in various places */
163         while (MONO_SEM_WAIT (&(start_info.registered)) != 0) {
164                 /*if (EINTR != errno) ABORT("sem_wait failed"); */
165         }
166         MONO_SEM_DESTROY (&(start_info.registered));
167
168         if (out_tid)
169                 *out_tid = thread;
170
171         return start_info.handle;
172 }
173
174 /*
175  * mono_threads_core_resume_created:
176  *
177  *   Resume a newly created thread created using CREATE_SUSPENDED.
178  */
179 void
180 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
181 {
182         MONO_SEM_POST (&info->create_suspended_sem);
183 }
184
185 void
186 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
187 {
188 #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
189         /* Mac OS X */
190         *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self());
191         *stsize = pthread_get_stacksize_np (pthread_self());
192
193
194 #ifdef TARGET_OSX
195         /*
196          * Mavericks reports stack sizes as 512kb:
197          * http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.devel/11590
198          * https://bugs.openjdk.java.net/browse/JDK-8020753
199          */
200         if (*stsize == 512 * 1024)
201                 *stsize = 2048 * mono_pagesize ();
202 #endif
203
204         /* staddr points to the start of the stack, not the end */
205         *staddr -= *stsize;
206
207         /* When running under emacs, sometimes staddr is not aligned to a page size */
208         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize() - 1));
209         return;
210
211 #elif (defined(HAVE_PTHREAD_GETATTR_NP) || defined(HAVE_PTHREAD_ATTR_GET_NP)) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
212         /* Linux, BSD */
213
214         pthread_attr_t attr;
215         guint8 *current = (guint8*)&attr;
216
217         *staddr = NULL;
218         *stsize = (size_t)-1;
219
220         pthread_attr_init (&attr);
221
222 #if     defined(HAVE_PTHREAD_GETATTR_NP)
223         /* Linux */
224         pthread_getattr_np (pthread_self(), &attr);
225
226 #elif   defined(HAVE_PTHREAD_ATTR_GET_NP)
227         /* BSD */
228         pthread_attr_get_np (pthread_self(), &attr);
229
230 #else
231 #error  Cannot determine which API is needed to retrieve pthread attributes.
232 #endif
233
234         pthread_attr_getstack (&attr, (void**)staddr, stsize);
235         pthread_attr_destroy (&attr);
236
237         if (*staddr)
238                 g_assert ((current > *staddr) && (current < *staddr + *stsize));
239
240         /* When running under emacs, sometimes staddr is not aligned to a page size */
241         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
242         return;
243
244 #elif defined(__OpenBSD__)
245         /* OpenBSD */
246         /* TODO :   Determine if this code is actually still needed. It may already be covered by the case above. */
247
248         pthread_attr_t attr;
249         guint8 *current = (guint8*)&attr;
250
251         *staddr = NULL;
252         *stsize = (size_t)-1;
253
254         pthread_attr_init (&attr);
255
256         stack_t ss;
257         int rslt;
258
259         rslt = pthread_stackseg_np(pthread_self(), &ss);
260         g_assert (rslt == 0);
261
262         *staddr = (guint8*)((size_t)ss.ss_sp - ss.ss_size);
263         *stsize = ss.ss_size;
264
265         pthread_attr_destroy (&attr);
266
267         if (*staddr)
268                 g_assert ((current > *staddr) && (current < *staddr + *stsize));
269
270         /* When running under emacs, sometimes staddr is not aligned to a page size */
271         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
272         return;
273
274 #elif defined(sun) || defined(__native_client__)
275         /* Solaris/Illumos, NaCl */
276         pthread_attr_t attr;
277         pthread_attr_init (&attr);
278         pthread_attr_getstacksize (&attr, &stsize);
279         pthread_attr_destroy (&attr);
280         *staddr = NULL;
281         return;
282
283 #else
284         /* FIXME:   It'd be better to use the 'error' preprocessor macro here so we know
285                     at compile-time if the target platform isn't supported. */
286 #warning "Unable to determine how to retrieve a thread's stack-bounds for this platform in 'mono_thread_get_stack_bounds()'."
287         *staddr = NULL;
288         *stsize = 0;
289         return;
290 #endif
291 }
292
293 gboolean
294 mono_threads_core_yield (void)
295 {
296         return sched_yield () == 0;
297 }
298
299 #if !defined (__MACH__)
300
301 #if !defined(__native_client__)
302 static void
303 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
304 {
305         MonoThreadInfo *current = mono_thread_info_current ();
306         gboolean ret;
307         
308         if (current->syscall_break_signal) {
309                 current->syscall_break_signal = FALSE;
310                 return;
311         }
312
313         ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->suspend_state, context);
314
315         /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
316         current->suspend_can_continue = ret;
317
318         MONO_SEM_POST (&current->begin_suspend_semaphore);
319
320         /* This thread is doomed, all we can do is give up and let the suspender recover. */
321         if (!ret)
322                 return;
323
324         while (MONO_SEM_WAIT (&current->resume_semaphore) != 0) {
325                 /*if (EINTR != errno) ABORT("sem_wait failed"); */
326         }
327
328         if (current->async_target) {
329 #if MONO_ARCH_HAS_MONO_CONTEXT
330                 MonoContext tmp = current->suspend_state.ctx;
331                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
332                 current->async_target = current->user_data = NULL;
333                 mono_monoctx_to_sigctx (&tmp, context);
334 #else
335                 g_error ("The new interruption machinery requires a working mono-context");
336 #endif
337         }
338
339         MONO_SEM_POST (&current->finish_resume_semaphore);
340 }
341 #endif
342
343 static void
344 mono_posix_add_signal_handler (int signo, gpointer handler)
345 {
346 #if !defined(__native_client__)
347         /*FIXME, move the code from mini to utils and do the right thing!*/
348         struct sigaction sa;
349         struct sigaction previous_sa;
350         int ret;
351
352         sa.sa_sigaction = handler;
353         sigemptyset (&sa.sa_mask);
354         sa.sa_flags = SA_SIGINFO;
355         ret = sigaction (signo, &sa, &previous_sa);
356
357         g_assert (ret != -1);
358 #endif
359 }
360
361 void
362 mono_threads_init_platform (void)
363 {
364 #if !defined(__native_client__)
365         /*
366         FIXME we should use all macros from mini to make this more portable
367         FIXME it would be very sweet if sgen could end up using this too.
368         */
369         if (mono_thread_info_new_interrupt_enabled ())
370                 mono_posix_add_signal_handler (mono_thread_get_abort_signal (), suspend_signal_handler);
371 #endif
372 }
373
374 /*nothing to be done here since suspend always abort syscalls due using signals*/
375 void
376 mono_threads_core_interrupt (MonoThreadInfo *info)
377 {
378 }
379
380 int
381 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
382 {
383 #if defined (PLATFORM_ANDROID)
384         int result, old_errno = errno;
385         result = tkill (info->native_handle, signum);
386         if (result < 0) {
387                 result = errno;
388                 errno = old_errno;
389         }
390         return result;
391 #elif defined(__native_client__)
392         /* Workaround pthread_kill abort() in NaCl glibc. */
393         return 0;
394 #else
395         return pthread_kill (mono_thread_info_get_tid (info), signum);
396 #endif
397
398 }
399
400 void
401 mono_threads_core_abort_syscall (MonoThreadInfo *info)
402 {
403         /*
404         We signal a thread to break it from the urrent syscall.
405         This signal should not be interpreted as a suspend request.
406         */
407         info->syscall_break_signal = TRUE;
408         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
409 }
410
411 gboolean
412 mono_threads_core_needs_abort_syscall (void)
413 {
414         return TRUE;
415 }
416
417 gboolean
418 mono_threads_core_suspend (MonoThreadInfo *info)
419 {
420         /*FIXME, check return value*/
421         mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
422         while (MONO_SEM_WAIT (&info->begin_suspend_semaphore) != 0) {
423                 /* g_assert (errno == EINTR); */
424         }
425         return info->suspend_can_continue;
426 }
427
428 gboolean
429 mono_threads_core_resume (MonoThreadInfo *info)
430 {
431         MONO_SEM_POST (&info->resume_semaphore);
432         while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
433                 /* g_assert (errno == EINTR); */
434         }
435
436         return TRUE;
437 }
438
439 void
440 mono_threads_platform_register (MonoThreadInfo *info)
441 {
442         MONO_SEM_INIT (&info->begin_suspend_semaphore, 0);
443
444 #if defined (PLATFORM_ANDROID)
445         info->native_handle = (gpointer) gettid ();
446 #endif
447 }
448
449 void
450 mono_threads_platform_free (MonoThreadInfo *info)
451 {
452         MONO_SEM_DESTROY (&info->begin_suspend_semaphore);
453 }
454
455 MonoNativeThreadId
456 mono_native_thread_id_get (void)
457 {
458         return pthread_self ();
459 }
460
461 gboolean
462 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
463 {
464         return pthread_equal (id1, id2);
465 }
466
467 /*
468  * mono_native_thread_create:
469  *
470  *   Low level thread creation function without any GC wrappers.
471  */
472 gboolean
473 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
474 {
475         return pthread_create (tid, NULL, func, arg) == 0;
476 }
477
478 #endif /*!defined (__MACH__)*/
479
480 #endif