2 * mono-threads-posix.c: Low-level threading, posix version
5 * Rodrigo Kumpera (kumpera@gmail.com)
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/mono-mmap.h>
17 #include <mono/metadata/threads-types.h>
22 #if defined(PLATFORM_ANDROID)
23 extern int tkill (pid_t tid, int signal);
26 #if defined(_POSIX_VERSION) || defined(__native_client__)
27 #include <sys/resource.h>
30 #if defined(__native_client__)
31 void nacl_shutdown_gc_thread(void);
35 void *(*start_routine)(void*);
38 MonoSemType registered;
42 #ifdef PLATFORM_ANDROID
43 static int no_interrupt_signo;
47 inner_start_thread (void *arg)
49 StartInfo *start_info = arg;
50 void *t_arg = start_info->arg;
52 void *(*start_func)(void*) = start_info->start_routine;
53 guint32 flags = start_info->flags;
58 /* Register the thread with the io-layer */
59 handle = wapi_create_thread_handle ();
61 res = MONO_SEM_POST (&(start_info->registered));
65 start_info->handle = handle;
67 info = mono_thread_info_attach (&result);
68 info->runtime_thread = TRUE;
69 info->handle = handle;
71 if (flags & CREATE_SUSPENDED) {
72 info->create_suspended = TRUE;
73 MONO_SEM_INIT (&info->create_suspended_sem, 0);
76 /* start_info is not valid after this */
77 res = MONO_SEM_POST (&(start_info->registered));
81 if (flags & CREATE_SUSPENDED) {
82 while (MONO_SEM_WAIT (&info->create_suspended_sem) != 0 &&
84 MONO_SEM_DESTROY (&info->create_suspended_sem);
87 /* Run the actual main function of the thread */
88 result = start_func (t_arg);
91 mono_thread_info_detach ();
94 #if defined(__native_client__)
95 nacl_shutdown_gc_thread();
98 wapi_thread_handle_set_exited (handle, GPOINTER_TO_UINT (result));
99 /* This is needed by mono_threads_core_unregister () which is called later */
102 g_assert (mono_threads_get_callbacks ()->thread_exit);
103 mono_threads_get_callbacks ()->thread_exit (NULL);
104 g_assert_not_reached ();
109 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
114 StartInfo start_info;
116 res = pthread_attr_init (&attr);
119 if (stack_size == 0) {
120 #if HAVE_VALGRIND_MEMCHECK_H
121 if (RUNNING_ON_VALGRIND)
122 stack_size = 1 << 20;
124 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
126 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
130 #ifdef PTHREAD_STACK_MIN
131 if (stack_size < PTHREAD_STACK_MIN)
132 stack_size = PTHREAD_STACK_MIN;
135 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
136 res = pthread_attr_setstacksize (&attr, stack_size);
140 memset (&start_info, 0, sizeof (StartInfo));
141 start_info.start_routine = (gpointer)start_routine;
142 start_info.arg = arg;
143 start_info.flags = creation_flags;
144 MONO_SEM_INIT (&(start_info.registered), 0);
146 /* Actually start the thread */
147 res = mono_threads_get_callbacks ()->mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
149 MONO_SEM_DESTROY (&(start_info.registered));
153 /* Wait until the thread register itself in various places */
154 while (MONO_SEM_WAIT (&(start_info.registered)) != 0) {
155 /*if (EINTR != errno) ABORT("sem_wait failed"); */
157 MONO_SEM_DESTROY (&(start_info.registered));
162 return start_info.handle;
166 * mono_threads_core_resume_created:
168 * Resume a newly created thread created using CREATE_SUSPENDED.
171 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
173 MONO_SEM_POST (&info->create_suspended_sem);
177 mono_threads_core_yield (void)
179 return sched_yield () == 0;
183 mono_threads_core_exit (int exit_code)
185 MonoThreadInfo *current = mono_thread_info_current ();
187 #if defined(__native_client__)
188 nacl_shutdown_gc_thread();
191 wapi_thread_handle_set_exited (current->handle, exit_code);
193 g_assert (mono_threads_get_callbacks ()->thread_exit);
194 mono_threads_get_callbacks ()->thread_exit (NULL);
198 mono_threads_core_unregister (MonoThreadInfo *info)
201 wapi_thread_handle_set_exited (info->handle, 0);
207 mono_threads_core_open_handle (void)
209 MonoThreadInfo *info;
211 info = mono_thread_info_current ();
215 info->handle = wapi_create_thread_handle ();
217 wapi_ref_thread_handle (info->handle);
222 mono_threads_get_max_stack_size (void)
226 /* If getrlimit fails, we don't enforce any limits. */
227 if (getrlimit (RLIMIT_STACK, &lim))
229 /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
230 if (lim.rlim_max > (rlim_t)INT_MAX)
232 return (int)lim.rlim_max;
236 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
238 wapi_ref_thread_handle (handle);
244 mono_threads_core_prepare_interrupt (HANDLE thread_handle)
246 return wapi_prepare_interrupt_thread (thread_handle);
250 mono_threads_core_finish_interrupt (gpointer wait_handle)
252 wapi_finish_interrupt_thread (wait_handle);
256 mono_threads_core_self_interrupt (void)
258 wapi_self_interrupt ();
262 mono_threads_core_clear_interruption (void)
264 wapi_clear_interruption ();
268 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
270 #if defined (PLATFORM_ANDROID)
271 int result, old_errno = errno;
272 result = tkill (info->native_handle, signum);
278 #elif defined(__native_client__)
279 /* Workaround pthread_kill abort() in NaCl glibc. */
282 return pthread_kill (mono_thread_info_get_tid (info), signum);
287 #if !defined (__MACH__)
289 #if !defined(__native_client__)
291 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
293 MonoThreadInfo *current = mono_thread_info_current ();
296 if (current->syscall_break_signal) {
297 current->syscall_break_signal = FALSE;
301 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->suspend_state, context);
303 /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
304 current->suspend_can_continue = ret;
306 MONO_SEM_POST (¤t->begin_suspend_semaphore);
308 /* This thread is doomed, all we can do is give up and let the suspender recover. */
312 while (MONO_SEM_WAIT (¤t->resume_semaphore) != 0) {
313 /*if (EINTR != errno) ABORT("sem_wait failed"); */
316 if (current->async_target) {
317 #if MONO_ARCH_HAS_MONO_CONTEXT
318 MonoContext tmp = current->suspend_state.ctx;
319 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
320 current->async_target = current->user_data = NULL;
321 mono_monoctx_to_sigctx (&tmp, context);
323 g_error ("The new interruption machinery requires a working mono-context");
327 MONO_SEM_POST (¤t->finish_resume_semaphore);
332 mono_posix_add_signal_handler (int signo, gpointer handler, int flags)
334 #if !defined(__native_client__)
335 /*FIXME, move the code from mini to utils and do the right thing!*/
337 struct sigaction previous_sa;
340 sa.sa_sigaction = handler;
341 sigemptyset (&sa.sa_mask);
342 sa.sa_flags = SA_SIGINFO | flags;
343 ret = sigaction (signo, &sa, &previous_sa);
345 g_assert (ret != -1);
350 mono_threads_init_platform (void)
352 #if !defined(__native_client__)
356 FIXME we should use all macros from mini to make this more portable
357 FIXME it would be very sweet if sgen could end up using this too.
359 if (!mono_thread_info_new_interrupt_enabled ())
361 abort_signo = mono_thread_get_abort_signal ();
362 mono_posix_add_signal_handler (abort_signo, suspend_signal_handler, 0);
364 #ifdef PLATFORM_ANDROID
366 * Lots of android native code can't handle the EINTR caused by
367 * the normal abort signal, so use a different signal for the
368 * no interruption case, which is used by sdb.
369 * FIXME: Use this on all platforms.
370 * SIGUSR1 is used by dalvik/art.
372 no_interrupt_signo = SIGUSR2;
373 g_assert (abort_signo != no_interrupt_signo);
374 mono_posix_add_signal_handler (no_interrupt_signo, suspend_signal_handler, SA_RESTART);
380 mono_threads_core_interrupt (MonoThreadInfo *info)
382 /* Handled in mono_threads_core_suspend () */
386 mono_threads_core_abort_syscall (MonoThreadInfo *info)
389 We signal a thread to break it from the urrent syscall.
390 This signal should not be interpreted as a suspend request.
392 info->syscall_break_signal = TRUE;
393 mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
397 mono_threads_core_needs_abort_syscall (void)
403 mono_threads_core_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
405 /*FIXME, check return value*/
406 #ifdef PLATFORM_ANDROID
407 if (!interrupt_kernel)
408 mono_threads_pthread_kill (info, no_interrupt_signo);
410 mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
412 mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
414 while (MONO_SEM_WAIT (&info->begin_suspend_semaphore) != 0) {
415 /* g_assert (errno == EINTR); */
417 return info->suspend_can_continue;
421 mono_threads_core_resume (MonoThreadInfo *info)
423 MONO_SEM_POST (&info->resume_semaphore);
424 while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
425 /* g_assert (errno == EINTR); */
432 mono_threads_platform_register (MonoThreadInfo *info)
434 MONO_SEM_INIT (&info->begin_suspend_semaphore, 0);
436 #if defined (PLATFORM_ANDROID)
437 info->native_handle = gettid ();
442 mono_threads_platform_free (MonoThreadInfo *info)
444 MONO_SEM_DESTROY (&info->begin_suspend_semaphore);
448 mono_native_thread_id_get (void)
450 return pthread_self ();
454 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
456 return pthread_equal (id1, id2);
460 * mono_native_thread_create:
462 * Low level thread creation function without any GC wrappers.
465 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
467 return pthread_create (tid, NULL, func, arg) == 0;
471 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
473 #ifdef HAVE_PTHREAD_SETNAME_NP
475 pthread_setname_np (tid, "");
479 strncpy (n, name, 16);
481 pthread_setname_np (tid, n);
486 #endif /*!defined (__MACH__)*/