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