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>
26 #if defined(PLATFORM_ANDROID)
27 extern int tkill (pid_t tid, int signal);
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);
35 #if defined(_POSIX_VERSION) || defined(__native_client__)
38 #if defined(__native_client__)
39 void nacl_shutdown_gc_thread(void);
43 void *(*start_routine)(void*);
46 MonoSemType registered;
51 inner_start_thread (void *arg)
53 StartInfo *start_info = arg;
54 void *t_arg = start_info->arg;
56 void *(*start_func)(void*) = start_info->start_routine;
57 guint32 flags = start_info->flags;
62 /* Register the thread with the io-layer */
63 handle = wapi_create_thread_handle ();
65 res = MONO_SEM_POST (&(start_info->registered));
69 start_info->handle = handle;
71 info = mono_thread_info_attach (&result);
72 info->runtime_thread = TRUE;
73 info->handle = handle;
75 if (flags & CREATE_SUSPENDED) {
76 info->create_suspended = TRUE;
77 MONO_SEM_INIT (&info->create_suspended_sem, 0);
80 /* start_info is not valid after this */
81 res = MONO_SEM_POST (&(start_info->registered));
85 if (flags & CREATE_SUSPENDED) {
86 while (MONO_SEM_WAIT (&info->create_suspended_sem) != 0 &&
88 MONO_SEM_DESTROY (&info->create_suspended_sem);
91 /* Run the actual main function of the thread */
92 result = start_func (t_arg);
95 mono_thread_info_detach ();
98 #if defined(__native_client__)
99 nacl_shutdown_gc_thread();
102 wapi_thread_handle_set_exited (handle, GPOINTER_TO_UINT (result));
103 /* This is needed by mono_threads_core_unregister () which is called later */
106 g_assert (mono_threads_get_callbacks ()->thread_exit);
107 mono_threads_get_callbacks ()->thread_exit (NULL);
108 g_assert_not_reached ();
113 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
118 StartInfo start_info;
120 res = pthread_attr_init (&attr);
123 if (stack_size == 0) {
124 #if HAVE_VALGRIND_MEMCHECK_H
125 if (RUNNING_ON_VALGRIND)
126 stack_size = 1 << 20;
128 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
130 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
134 #ifdef PTHREAD_STACK_MIN
135 if (stack_size < PTHREAD_STACK_MIN)
136 stack_size = PTHREAD_STACK_MIN;
139 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
140 res = pthread_attr_setstacksize (&attr, stack_size);
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);
150 /* Actually start the thread */
151 res = mono_threads_get_callbacks ()->mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
154 g_assert_not_reached ();
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"); */
161 MONO_SEM_DESTROY (&(start_info.registered));
166 return start_info.handle;
170 * mono_threads_core_resume_created:
172 * Resume a newly created thread created using CREATE_SUSPENDED.
175 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
177 MONO_SEM_POST (&info->create_suspended_sem);
181 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
183 #if defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
185 *staddr = (guint8*)pthread_get_stackaddr_np (pthread_self());
186 *stsize = pthread_get_stacksize_np (pthread_self());
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
195 if (*stsize == 512 * 1024)
196 *stsize = 2048 * mono_pagesize ();
199 /* staddr points to the start of the stack, not the end */
202 /* When running under emacs, sometimes staddr is not aligned to a page size */
203 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize() - 1));
206 #elif (defined(HAVE_PTHREAD_GETATTR_NP) || defined(HAVE_PTHREAD_ATTR_GET_NP)) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
210 guint8 *current = (guint8*)&attr;
213 *stsize = (size_t)-1;
215 pthread_attr_init (&attr);
217 #if defined(HAVE_PTHREAD_GETATTR_NP)
219 pthread_getattr_np (pthread_self(), &attr);
221 #elif defined(HAVE_PTHREAD_ATTR_GET_NP)
223 pthread_attr_get_np (pthread_self(), &attr);
226 #error Cannot determine which API is needed to retrieve pthread attributes.
229 pthread_attr_getstack (&attr, (void**)staddr, stsize);
230 pthread_attr_destroy (&attr);
233 g_assert ((current > *staddr) && (current < *staddr + *stsize));
235 /* When running under emacs, sometimes staddr is not aligned to a page size */
236 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
239 #elif defined(__OpenBSD__)
241 /* TODO : Determine if this code is actually still needed. It may already be covered by the case above. */
244 guint8 *current = (guint8*)&attr;
247 *stsize = (size_t)-1;
249 pthread_attr_init (&attr);
254 rslt = pthread_stackseg_np(pthread_self(), &ss);
255 g_assert (rslt == 0);
257 *staddr = (guint8*)((size_t)ss.ss_sp - ss.ss_size);
258 *stsize = ss.ss_size;
260 pthread_attr_destroy (&attr);
263 g_assert ((current > *staddr) && (current < *staddr + *stsize));
265 /* When running under emacs, sometimes staddr is not aligned to a page size */
266 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
269 #elif defined(sun) || defined(__native_client__)
270 /* Solaris/Illumos, NaCl */
272 pthread_attr_init (&attr);
273 pthread_attr_getstacksize (&attr, &stsize);
274 pthread_attr_destroy (&attr);
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()'."
289 mono_threads_core_yield (void)
291 return sched_yield () == 0;
295 mono_threads_core_exit (int exit_code)
297 MonoThreadInfo *current = mono_thread_info_current ();
299 #if defined(__native_client__)
300 nacl_shutdown_gc_thread();
303 wapi_thread_handle_set_exited (current->handle, exit_code);
305 g_assert (mono_threads_get_callbacks ()->thread_exit);
306 mono_threads_get_callbacks ()->thread_exit (NULL);
310 mono_threads_core_unregister (MonoThreadInfo *info)
313 wapi_thread_handle_set_exited (info->handle, 0);
319 mono_threads_core_open_handle (void)
321 MonoThreadInfo *info;
323 info = mono_thread_info_current ();
327 info->handle = wapi_create_thread_handle ();
329 wapi_ref_thread_handle (info->handle);
333 #if !defined (__MACH__)
335 #if !defined(__native_client__)
337 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
339 MonoThreadInfo *current = mono_thread_info_current ();
342 if (current->syscall_break_signal) {
343 current->syscall_break_signal = FALSE;
347 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->suspend_state, context);
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;
352 MONO_SEM_POST (¤t->begin_suspend_semaphore);
354 /* This thread is doomed, all we can do is give up and let the suspender recover. */
358 while (MONO_SEM_WAIT (¤t->resume_semaphore) != 0) {
359 /*if (EINTR != errno) ABORT("sem_wait failed"); */
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);
369 g_error ("The new interruption machinery requires a working mono-context");
373 MONO_SEM_POST (¤t->finish_resume_semaphore);
378 mono_posix_add_signal_handler (int signo, gpointer handler)
380 #if !defined(__native_client__)
381 /*FIXME, move the code from mini to utils and do the right thing!*/
383 struct sigaction previous_sa;
386 sa.sa_sigaction = handler;
387 sigemptyset (&sa.sa_mask);
388 sa.sa_flags = SA_SIGINFO;
389 ret = sigaction (signo, &sa, &previous_sa);
391 g_assert (ret != -1);
396 mono_threads_init_platform (void)
398 #if !defined(__native_client__)
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.
403 if (mono_thread_info_new_interrupt_enabled ())
404 mono_posix_add_signal_handler (mono_thread_get_abort_signal (), suspend_signal_handler);
408 /*nothing to be done here since suspend always abort syscalls due using signals*/
410 mono_threads_core_interrupt (MonoThreadInfo *info)
415 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
417 #if defined (PLATFORM_ANDROID)
418 int result, old_errno = errno;
419 result = tkill (info->native_handle, signum);
425 #elif defined(__native_client__)
426 /* Workaround pthread_kill abort() in NaCl glibc. */
429 return pthread_kill (mono_thread_info_get_tid (info), signum);
435 mono_threads_core_abort_syscall (MonoThreadInfo *info)
438 We signal a thread to break it from the urrent syscall.
439 This signal should not be interpreted as a suspend request.
441 info->syscall_break_signal = TRUE;
442 mono_threads_pthread_kill (info, mono_thread_get_abort_signal ());
446 mono_threads_core_needs_abort_syscall (void)
452 mono_threads_core_suspend (MonoThreadInfo *info)
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); */
459 return info->suspend_can_continue;
463 mono_threads_core_resume (MonoThreadInfo *info)
465 MONO_SEM_POST (&info->resume_semaphore);
466 while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
467 /* g_assert (errno == EINTR); */
474 mono_threads_platform_register (MonoThreadInfo *info)
476 MONO_SEM_INIT (&info->begin_suspend_semaphore, 0);
478 #if defined (PLATFORM_ANDROID)
479 info->native_handle = (gpointer) gettid ();
484 mono_threads_platform_free (MonoThreadInfo *info)
486 MONO_SEM_DESTROY (&info->begin_suspend_semaphore);
490 mono_native_thread_id_get (void)
492 return pthread_self ();
496 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
498 return pthread_equal (id1, id2);
502 * mono_native_thread_create:
504 * Low level thread creation function without any GC wrappers.
507 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
509 return pthread_create (tid, NULL, func, arg) == 0;
512 #endif /*!defined (__MACH__)*/