2 * mono-threads-posix.c: Low-level threading, posix version
5 * Rodrigo Kumpera (kumpera@gmail.com)
12 /* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
13 #if defined (__MACH__)
14 #define _DARWIN_C_SOURCE 1
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>
27 #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
28 #define USE_TKILL_ON_ANDROID 1
31 #ifdef USE_TKILL_ON_ANDROID
32 extern int tkill (pid_t tid
, int signal
);
35 #if defined(_POSIX_VERSION) || defined(__native_client__)
36 #include <sys/resource.h>
39 #if defined(__native_client__)
40 void nacl_shutdown_gc_thread(void);
44 void *(*start_routine
)(void*);
47 MonoSemType registered
;
52 inner_start_thread (void *arg
)
54 StartInfo
*start_info
= (StartInfo
*) arg
;
55 void *t_arg
= start_info
->arg
;
57 void *(*start_func
)(void*) = start_info
->start_routine
;
58 guint32 flags
= start_info
->flags
;
63 /* Register the thread with the io-layer */
64 handle
= wapi_create_thread_handle ();
66 res
= MONO_SEM_POST (&(start_info
->registered
));
70 start_info
->handle
= handle
;
72 info
= mono_thread_info_attach (&result
);
75 info
->runtime_thread
= TRUE
;
76 info
->handle
= handle
;
78 if (flags
& CREATE_SUSPENDED
) {
79 info
->create_suspended
= TRUE
;
80 MONO_SEM_INIT (&info
->create_suspended_sem
, 0);
83 /* start_info is not valid after this */
84 res
= MONO_SEM_POST (&(start_info
->registered
));
88 if (flags
& CREATE_SUSPENDED
) {
89 while (MONO_SEM_WAIT (&info
->create_suspended_sem
) != 0 &&
91 MONO_SEM_DESTROY (&info
->create_suspended_sem
);
95 /* Run the actual main function of the thread */
96 result
= start_func (t_arg
);
99 mono_thread_info_detach ();
102 #if defined(__native_client__)
103 nacl_shutdown_gc_thread();
106 wapi_thread_handle_set_exited (handle
, GPOINTER_TO_UINT (result
));
107 /* This is needed by mono_threads_core_unregister () which is called later */
110 g_assert (mono_threads_get_callbacks ()->thread_exit
);
111 mono_threads_get_callbacks ()->thread_exit (NULL
);
112 g_assert_not_reached ();
117 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine
, gpointer arg
, guint32 stack_size
, guint32 creation_flags
, MonoNativeThreadId
*out_tid
)
122 StartInfo start_info
;
124 res
= pthread_attr_init (&attr
);
127 if (stack_size
== 0) {
128 #if HAVE_VALGRIND_MEMCHECK_H
129 if (RUNNING_ON_VALGRIND
)
130 stack_size
= 1 << 20;
132 stack_size
= (SIZEOF_VOID_P
/ 4) * 1024 * 1024;
134 stack_size
= (SIZEOF_VOID_P
/ 4) * 1024 * 1024;
138 #ifdef PTHREAD_STACK_MIN
139 if (stack_size
< PTHREAD_STACK_MIN
)
140 stack_size
= PTHREAD_STACK_MIN
;
143 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
144 res
= pthread_attr_setstacksize (&attr
, stack_size
);
148 memset (&start_info
, 0, sizeof (StartInfo
));
149 start_info
.start_routine
= (void *(*)(void *)) start_routine
;
150 start_info
.arg
= arg
;
151 start_info
.flags
= creation_flags
;
152 MONO_SEM_INIT (&(start_info
.registered
), 0);
154 /* Actually start the thread */
155 res
= mono_threads_get_callbacks ()->mono_gc_pthread_create (&thread
, &attr
, inner_start_thread
, &start_info
);
157 MONO_SEM_DESTROY (&(start_info
.registered
));
161 /* Wait until the thread register itself in various places */
162 while (MONO_SEM_WAIT (&(start_info
.registered
)) != 0) {
163 /*if (EINTR != errno) ABORT("sem_wait failed"); */
165 MONO_SEM_DESTROY (&(start_info
.registered
));
170 return start_info
.handle
;
174 * mono_threads_core_resume_created:
176 * Resume a newly created thread created using CREATE_SUSPENDED.
179 mono_threads_core_resume_created (MonoThreadInfo
*info
, MonoNativeThreadId tid
)
181 MONO_SEM_POST (&info
->create_suspended_sem
);
185 mono_threads_core_yield (void)
187 return sched_yield () == 0;
191 mono_threads_core_exit (int exit_code
)
193 MonoThreadInfo
*current
= mono_thread_info_current ();
195 #if defined(__native_client__)
196 nacl_shutdown_gc_thread();
199 wapi_thread_handle_set_exited (current
->handle
, exit_code
);
201 g_assert (mono_threads_get_callbacks ()->thread_exit
);
202 mono_threads_get_callbacks ()->thread_exit (NULL
);
206 mono_threads_core_unregister (MonoThreadInfo
*info
)
209 wapi_thread_handle_set_exited (info
->handle
, 0);
215 mono_threads_core_open_handle (void)
217 MonoThreadInfo
*info
;
219 info
= mono_thread_info_current ();
223 info
->handle
= wapi_create_thread_handle ();
225 wapi_ref_thread_handle (info
->handle
);
230 mono_threads_get_max_stack_size (void)
234 /* If getrlimit fails, we don't enforce any limits. */
235 if (getrlimit (RLIMIT_STACK
, &lim
))
237 /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
238 if (lim
.rlim_max
> (rlim_t
)INT_MAX
)
240 return (int)lim
.rlim_max
;
244 mono_threads_core_open_thread_handle (HANDLE handle
, MonoNativeThreadId tid
)
246 wapi_ref_thread_handle (handle
);
252 mono_threads_core_prepare_interrupt (HANDLE thread_handle
)
254 return wapi_prepare_interrupt_thread (thread_handle
);
258 mono_threads_core_finish_interrupt (gpointer wait_handle
)
260 wapi_finish_interrupt_thread (wait_handle
);
264 mono_threads_core_self_interrupt (void)
266 wapi_self_interrupt ();
270 mono_threads_core_clear_interruption (void)
272 wapi_clear_interruption ();
276 mono_threads_pthread_kill (MonoThreadInfo
*info
, int signum
)
278 THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum
, info
, mono_thread_info_get_tid (info
));
279 #ifdef USE_TKILL_ON_ANDROID
280 int result
, old_errno
= errno
;
281 result
= tkill (info
->native_handle
, signum
);
287 #elif defined(__native_client__)
288 /* Workaround pthread_kill abort() in NaCl glibc. */
291 return pthread_kill (mono_thread_info_get_tid (info
), signum
);
296 mono_native_thread_id_get (void)
298 return pthread_self ();
302 mono_native_thread_id_equals (MonoNativeThreadId id1
, MonoNativeThreadId id2
)
304 return pthread_equal (id1
, id2
);
308 * mono_native_thread_create:
310 * Low level thread creation function without any GC wrappers.
313 mono_native_thread_create (MonoNativeThreadId
*tid
, gpointer func
, gpointer arg
)
315 return pthread_create (tid
, NULL
, (void *(*)(void *)) func
, arg
) == 0;
319 mono_threads_core_set_name (MonoNativeThreadId tid
, const char *name
)
321 #if defined (HAVE_PTHREAD_SETNAME_NP) && !defined (__MACH__)
323 pthread_setname_np (tid
, "");
327 strncpy (n
, name
, 16);
329 pthread_setname_np (tid
, n
);
335 #if defined (USE_POSIX_BACKEND) && !defined (USE_COOP_GC)
337 static int suspend_signal_num
;
338 static int restart_signal_num
;
339 static int abort_signal_num
;
340 static sigset_t suspend_signal_mask
;
341 static sigset_t suspend_ack_signal_mask
;
344 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
345 #define DEFAULT_SUSPEND_SIGNAL SIGXFSZ
347 #define DEFAULT_SUSPEND_SIGNAL SIGPWR
349 #define DEFAULT_RESTART_SIGNAL SIGXCPU
352 mono_thread_search_alt_signal (int min_signal
)
354 #if !defined (SIGRTMIN)
355 g_error ("signal search only works with RTMIN");
358 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
359 for (i
= MAX (min_signal
, SIGRTMIN
) + 1; i
< SIGRTMAX
; ++i
) {
360 struct sigaction sinfo
;
361 sigaction (i
, NULL
, &sinfo
);
362 if (sinfo
.sa_handler
== SIG_DFL
&& (void*)sinfo
.sa_sigaction
== (void*)SIG_DFL
) {
366 g_error ("Could not find an available signal");
371 mono_thread_get_alt_suspend_signal (void)
373 #if defined(PLATFORM_ANDROID)
375 #elif !defined (SIGRTMIN)
382 static int suspend_signum
= -1;
383 if (suspend_signum
== -1)
384 suspend_signum
= mono_thread_search_alt_signal (-1);
385 return suspend_signum
;
386 #endif /* SIGRTMIN */
390 mono_thread_get_alt_resume_signal (void)
392 #if defined(PLATFORM_ANDROID)
394 #elif !defined (SIGRTMIN)
401 static int resume_signum
= -1;
402 if (resume_signum
== -1)
403 resume_signum
= mono_thread_search_alt_signal (mono_thread_get_alt_suspend_signal () + 1);
404 return resume_signum
;
405 #endif /* SIGRTMIN */
410 mono_threads_get_abort_signal (void)
412 #if defined(PLATFORM_ANDROID)
414 #elif !defined (SIGRTMIN)
419 #endif /* SIGRTMIN */
421 static int abort_signum
= -1;
422 if (abort_signum
== -1)
423 abort_signum
= mono_thread_search_alt_signal (mono_thread_get_alt_resume_signal () + 1);
425 #endif /* SIGRTMIN */
429 #if !defined(__native_client__)
431 restart_signal_handler (int _dummy
, siginfo_t
*_info
, void *context
)
433 MonoThreadInfo
*info
;
434 int old_errno
= errno
;
436 info
= mono_thread_info_current ();
437 info
->signal
= restart_signal_num
;
442 suspend_signal_handler (int _dummy
, siginfo_t
*info
, void *context
)
444 int old_errno
= errno
;
445 int hp_save_index
= mono_hazard_pointer_save_for_signal_handler ();
448 MonoThreadInfo
*current
= mono_thread_info_current ();
451 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current
, (void*)current
->native_handle
);
452 if (current
->syscall_break_signal
) {
453 current
->syscall_break_signal
= FALSE
;
454 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current
);
458 /* Have we raced with self suspend? */
459 if (!mono_threads_transition_finish_async_suspend (current
)) {
460 current
->suspend_can_continue
= TRUE
;
461 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current
);
465 ret
= mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
], context
);
467 /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
468 current
->suspend_can_continue
= ret
;
472 Block the restart signal.
473 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
474 which might miss the signal and get stuck.
476 pthread_sigmask (SIG_BLOCK
, &suspend_ack_signal_mask
, NULL
);
478 /* We're done suspending */
479 mono_threads_notify_initiator_of_suspend (current
);
481 /* This thread is doomed, all we can do is give up and let the suspender recover. */
483 THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current
);
484 mono_threads_transition_async_suspend_compensation (current
);
485 /* Unblock the restart signal. */
486 pthread_sigmask (SIG_UNBLOCK
, &suspend_ack_signal_mask
, NULL
);
493 sigsuspend (&suspend_signal_mask
);
494 } while (current
->signal
!= restart_signal_num
);
496 /* Unblock the restart signal. */
497 pthread_sigmask (SIG_UNBLOCK
, &suspend_ack_signal_mask
, NULL
);
499 if (current
->async_target
) {
500 #if MONO_ARCH_HAS_MONO_CONTEXT
501 MonoContext tmp
= current
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
].ctx
;
502 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp
, current
->async_target
, current
->user_data
);
503 current
->async_target
= current
->user_data
= NULL
;
504 mono_monoctx_to_sigctx (&tmp
, context
);
506 g_error ("The new interruption machinery requires a working mono-context");
510 /* We're done resuming */
511 mono_threads_notify_initiator_of_resume (current
);
514 mono_hazard_pointer_restore_for_signal_handler (hp_save_index
);
519 abort_signal_handler (int _dummy
, siginfo_t
*info
, void *context
)
521 suspend_signal_handler (_dummy
, info
, context
);
527 mono_posix_add_signal_handler (int signo
, gpointer handler
, int flags
)
529 #if !defined(__native_client__)
530 /*FIXME, move the code from mini to utils and do the right thing!*/
532 struct sigaction previous_sa
;
535 sa
.sa_sigaction
= handler
;
536 sigfillset (&sa
.sa_mask
);
538 sa
.sa_flags
= SA_SIGINFO
| flags
;
539 ret
= sigaction (signo
, &sa
, &previous_sa
);
541 g_assert (ret
!= -1);
546 mono_threads_init_platform (void)
550 abort_signal_num
= mono_threads_get_abort_signal ();
551 if (mono_thread_info_unified_management_enabled ()) {
552 suspend_signal_num
= DEFAULT_SUSPEND_SIGNAL
;
553 restart_signal_num
= DEFAULT_RESTART_SIGNAL
;
555 suspend_signal_num
= mono_thread_get_alt_suspend_signal ();
556 restart_signal_num
= mono_thread_get_alt_resume_signal ();
559 sigfillset (&suspend_signal_mask
);
560 sigdelset (&suspend_signal_mask
, restart_signal_num
);
562 sigemptyset (&suspend_ack_signal_mask
);
563 sigaddset (&suspend_ack_signal_mask
, restart_signal_num
);
565 mono_posix_add_signal_handler (suspend_signal_num
, suspend_signal_handler
, SA_RESTART
);
566 mono_posix_add_signal_handler (restart_signal_num
, restart_signal_handler
, SA_RESTART
);
567 mono_posix_add_signal_handler (abort_signal_num
, abort_signal_handler
, 0);
569 /* ensure all the new signals are unblocked */
570 sigemptyset (&signal_set
);
571 sigaddset (&signal_set
, suspend_signal_num
);
572 sigaddset (&signal_set
, restart_signal_num
);
573 sigaddset (&signal_set
, abort_signal_num
);
574 sigprocmask (SIG_UNBLOCK
, &signal_set
, NULL
);
578 mono_threads_core_abort_syscall (MonoThreadInfo
*info
)
581 We signal a thread to break it from the urrent syscall.
582 This signal should not be interpreted as a suspend request.
584 info
->syscall_break_signal
= TRUE
;
585 mono_threads_pthread_kill (info
, abort_signal_num
);
589 mono_threads_core_needs_abort_syscall (void)
595 mono_threads_core_begin_async_suspend (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
597 int sig
= interrupt_kernel
? abort_signal_num
: suspend_signal_num
;
599 if (!mono_threads_pthread_kill (info
, sig
)) {
600 mono_threads_add_to_pending_operation_set (info
);
607 mono_threads_core_check_suspend_result (MonoThreadInfo
*info
)
609 return info
->suspend_can_continue
;
613 This begins async resume. This function must do the following:
615 - Install an async target if one was requested.
616 - Notify the target to resume.
619 mono_threads_core_begin_async_resume (MonoThreadInfo
*info
)
621 mono_threads_add_to_pending_operation_set (info
);
622 return mono_threads_pthread_kill (info
, restart_signal_num
) == 0;
626 mono_threads_platform_register (MonoThreadInfo
*info
)
628 #if defined (PLATFORM_ANDROID)
629 info
->native_handle
= gettid ();
634 mono_threads_platform_free (MonoThreadInfo
*info
)
639 mono_threads_core_begin_global_suspend (void)
644 mono_threads_core_end_global_suspend (void)
648 #endif /*defined (USE_POSIX_BACKEND)*/