2 * mono-threads-posix.c: Low-level threading, posix version
5 * Rodrigo Kumpera (kumpera@gmail.com)
12 #if defined(__OpenBSD__) || defined(__FreeBSD__)
14 #include <pthread_np.h>
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)
28 extern int tkill (pid_t tid, int signal);
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);
36 #if defined(_POSIX_VERSION) || defined(__native_client__)
37 #include <sys/resource.h>
40 #if defined(__native_client__)
41 void nacl_shutdown_gc_thread(void);
45 void *(*start_routine)(void*);
48 MonoSemType registered;
53 inner_start_thread (void *arg)
55 StartInfo *start_info = arg;
56 void *t_arg = start_info->arg;
58 void *(*start_func)(void*) = start_info->start_routine;
59 guint32 flags = start_info->flags;
64 /* Register the thread with the io-layer */
65 handle = wapi_create_thread_handle ();
67 res = MONO_SEM_POST (&(start_info->registered));
71 start_info->handle = handle;
73 info = mono_thread_info_attach (&result);
74 info->runtime_thread = TRUE;
75 info->handle = handle;
77 if (flags & CREATE_SUSPENDED) {
78 info->create_suspended = TRUE;
79 MONO_SEM_INIT (&info->create_suspended_sem, 0);
82 /* start_info is not valid after this */
83 res = MONO_SEM_POST (&(start_info->registered));
87 if (flags & CREATE_SUSPENDED) {
88 while (MONO_SEM_WAIT (&info->create_suspended_sem) != 0 &&
90 MONO_SEM_DESTROY (&info->create_suspended_sem);
93 /* Run the actual main function of the thread */
94 result = start_func (t_arg);
97 mono_thread_info_detach ();
100 #if defined(__native_client__)
101 nacl_shutdown_gc_thread();
104 wapi_thread_handle_set_exited (handle, GPOINTER_TO_UINT (result));
105 /* This is needed by mono_threads_core_unregister () which is called later */
108 g_assert (mono_threads_get_callbacks ()->thread_exit);
109 mono_threads_get_callbacks ()->thread_exit (NULL);
110 g_assert_not_reached ();
115 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
120 StartInfo start_info;
122 res = pthread_attr_init (&attr);
125 if (stack_size == 0) {
126 #if HAVE_VALGRIND_MEMCHECK_H
127 if (RUNNING_ON_VALGRIND)
128 stack_size = 1 << 20;
130 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
132 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
136 #ifdef PTHREAD_STACK_MIN
137 if (stack_size < PTHREAD_STACK_MIN)
138 stack_size = PTHREAD_STACK_MIN;
141 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
142 res = pthread_attr_setstacksize (&attr, stack_size);
146 memset (&start_info, 0, sizeof (StartInfo));
147 start_info.start_routine = (gpointer)start_routine;
148 start_info.arg = arg;
149 start_info.flags = creation_flags;
150 MONO_SEM_INIT (&(start_info.registered), 0);
152 /* Actually start the thread */
153 res = mono_threads_get_callbacks ()->mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
155 MONO_SEM_DESTROY (&(start_info.registered));
159 /* Wait until the thread register itself in various places */
160 while (MONO_SEM_WAIT (&(start_info.registered)) != 0) {
161 /*if (EINTR != errno) ABORT("sem_wait failed"); */
163 MONO_SEM_DESTROY (&(start_info.registered));
168 return start_info.handle;
172 * mono_threads_core_resume_created:
174 * Resume a newly created thread created using CREATE_SUSPENDED.
177 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
179 MONO_SEM_POST (&info->create_suspended_sem);
183 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
185 #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
187 *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self());
188 *stsize = pthread_get_stacksize_np (pthread_self());
193 * Mavericks reports stack sizes as 512kb:
194 * http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.devel/11590
195 * https://bugs.openjdk.java.net/browse/JDK-8020753
197 if (*stsize == 512 * 1024)
198 *stsize = 2048 * mono_pagesize ();
201 /* staddr points to the start of the stack, not the end */
204 /* When running under emacs, sometimes staddr is not aligned to a page size */
205 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize() - 1));
208 #elif (defined(HAVE_PTHREAD_GETATTR_NP) || defined(HAVE_PTHREAD_ATTR_GET_NP)) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
212 guint8 *current = (guint8*)&attr;
215 *stsize = (size_t)-1;
217 pthread_attr_init (&attr);
219 #if defined(HAVE_PTHREAD_GETATTR_NP)
221 pthread_getattr_np (pthread_self(), &attr);
223 #elif defined(HAVE_PTHREAD_ATTR_GET_NP)
225 pthread_attr_get_np (pthread_self(), &attr);
228 #error Cannot determine which API is needed to retrieve pthread attributes.
231 pthread_attr_getstack (&attr, (void**)staddr, stsize);
232 pthread_attr_destroy (&attr);
235 g_assert ((current > *staddr) && (current < *staddr + *stsize));
237 /* When running under emacs, sometimes staddr is not aligned to a page size */
238 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
241 #elif defined(__OpenBSD__)
243 /* TODO : Determine if this code is actually still needed. It may already be covered by the case above. */
246 guint8 *current = (guint8*)&attr;
249 *stsize = (size_t)-1;
251 pthread_attr_init (&attr);
256 rslt = pthread_stackseg_np(pthread_self(), &ss);
257 g_assert (rslt == 0);
259 *staddr = (guint8*)((size_t)ss.ss_sp - ss.ss_size);
260 *stsize = ss.ss_size;
262 pthread_attr_destroy (&attr);
265 g_assert ((current > *staddr) && (current < *staddr + *stsize));
267 /* When running under emacs, sometimes staddr is not aligned to a page size */
268 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
271 #elif defined(sun) || defined(__native_client__)
272 /* Solaris/Illumos, NaCl */
274 pthread_attr_init (&attr);
275 pthread_attr_getstacksize (&attr, &stsize);
276 pthread_attr_destroy (&attr);
281 /* FIXME: It'd be better to use the 'error' preprocessor macro here so we know
282 at compile-time if the target platform isn't supported. */
283 #warning "Unable to determine how to retrieve a thread's stack-bounds for this platform in 'mono_thread_get_stack_bounds()'."
291 mono_threads_core_yield (void)
293 return sched_yield () == 0;
297 mono_threads_core_exit (int exit_code)
299 MonoThreadInfo *current = mono_thread_info_current ();
301 #if defined(__native_client__)
302 nacl_shutdown_gc_thread();
305 wapi_thread_handle_set_exited (current->handle, exit_code);
307 g_assert (mono_threads_get_callbacks ()->thread_exit);
308 mono_threads_get_callbacks ()->thread_exit (NULL);
312 mono_threads_core_unregister (MonoThreadInfo *info)
315 wapi_thread_handle_set_exited (info->handle, 0);
321 mono_threads_core_open_handle (void)
323 MonoThreadInfo *info;
325 info = mono_thread_info_current ();
329 info->handle = wapi_create_thread_handle ();
331 wapi_ref_thread_handle (info->handle);
336 mono_threads_get_max_stack_size (void)
340 /* If getrlimit fails, we don't enforce any limits. */
341 if (getrlimit (RLIMIT_STACK, &lim))
343 /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
344 if (lim.rlim_max > (rlim_t)INT_MAX)
346 return (int)lim.rlim_max;
350 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
352 wapi_ref_thread_handle (handle);
357 #if !defined (__MACH__)
359 #if !defined(__native_client__)
361 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
363 MonoThreadInfo *current = mono_thread_info_current ();
366 if (current->syscall_break_signal) {
367 current->syscall_break_signal = FALSE;
371 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->suspend_state, context);
373 /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
374 current->suspend_can_continue = ret;
376 MONO_SEM_POST (¤t->begin_suspend_semaphore);
378 /* This thread is doomed, all we can do is give up and let the suspender recover. */
382 while (MONO_SEM_WAIT (¤t->resume_semaphore) != 0) {
383 /*if (EINTR != errno) ABORT("sem_wait failed"); */
386 if (current->async_target) {
387 #if MONO_ARCH_HAS_MONO_CONTEXT
388 MonoContext tmp = current->suspend_state.ctx;
389 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
390 current->async_target = current->user_data = NULL;
391 mono_monoctx_to_sigctx (&tmp, context);
393 g_error ("The new interruption machinery requires a working mono-context");
397 MONO_SEM_POST (¤t->finish_resume_semaphore);
402 mono_posix_add_signal_handler (int signo, gpointer handler)
404 #if !defined(__native_client__)
405 /*FIXME, move the code from mini to utils and do the right thing!*/
407 struct sigaction previous_sa;
410 sa.sa_sigaction = handler;
411 sigemptyset (&sa.sa_mask);
412 sa.sa_flags = SA_SIGINFO;
413 ret = sigaction (signo, &sa, &previous_sa);
415 g_assert (ret != -1);
420 mono_threads_init_platform (void)
422 #if !defined(__native_client__)
424 FIXME we should use all macros from mini to make this more portable
425 FIXME it would be very sweet if sgen could end up using this too.
427 if (mono_thread_info_new_interrupt_enabled ())
428 mono_posix_add_signal_handler (mono_thread_get_abort_signal (), suspend_signal_handler);
432 /*nothing to be done here since suspend always abort syscalls due using signals*/
434 mono_threads_core_interrupt (MonoThreadInfo *info)
439 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
441 #if defined (PLATFORM_ANDROID)
442 int result, old_errno = errno;
443 result = tkill (info->native_handle, signum);
449 #elif defined(__native_client__)
450 /* Workaround pthread_kill abort() in NaCl glibc. */
453 return pthread_kill (mono_thread_info_get_tid (info), signum);
459 mono_threads_core_abort_syscall (MonoThreadInfo *info)
462 We signal a thread to break it from the urrent syscall.
463 This signal should not be interpreted as a suspend request.
465 info->syscall_break_signal = TRUE;
466 mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
470 mono_threads_core_needs_abort_syscall (void)
476 mono_threads_core_suspend (MonoThreadInfo *info)
478 /*FIXME, check return value*/
479 mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
480 while (MONO_SEM_WAIT (&info->begin_suspend_semaphore) != 0) {
481 /* g_assert (errno == EINTR); */
483 return info->suspend_can_continue;
487 mono_threads_core_resume (MonoThreadInfo *info)
489 MONO_SEM_POST (&info->resume_semaphore);
490 while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
491 /* g_assert (errno == EINTR); */
498 mono_threads_platform_register (MonoThreadInfo *info)
500 MONO_SEM_INIT (&info->begin_suspend_semaphore, 0);
502 #if defined (PLATFORM_ANDROID)
503 info->native_handle = (gpointer) gettid ();
508 mono_threads_platform_free (MonoThreadInfo *info)
510 MONO_SEM_DESTROY (&info->begin_suspend_semaphore);
514 mono_native_thread_id_get (void)
516 return pthread_self ();
520 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
522 return pthread_equal (id1, id2);
526 * mono_native_thread_create:
528 * Low level thread creation function without any GC wrappers.
531 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
533 return pthread_create (tid, NULL, func, arg) == 0;
537 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
539 #ifdef HAVE_PTHREAD_SETNAME_NP
541 pthread_setname_np (tid, "");
545 strncpy (n, name, 16);
547 pthread_setname_np (tid, n);
552 #endif /*!defined (__MACH__)*/