3 * Low-level threading, posix version
6 * Rodrigo Kumpera (kumpera@gmail.com)
13 /* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
14 #if defined (__MACH__)
15 #define _DARWIN_C_SOURCE 1
18 #if defined (HOST_FUCHSIA)
19 #include <zircon/syscalls.h>
22 #if defined (__HAIKU__)
23 #include <os/kernel/OS.h>
26 #include <mono/utils/mono-threads.h>
27 #include <mono/utils/mono-threads-coop.h>
28 #include <mono/utils/mono-coop-semaphore.h>
29 #include <mono/metadata/gc-internals.h>
30 #include <mono/utils/mono-threads-debug.h>
31 #include <mono/utils/mono-errno.h>
35 #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
36 #define USE_TKILL_ON_ANDROID 1
39 #ifdef USE_TKILL_ON_ANDROID
40 extern int tkill (pid_t tid
, int signal
);
43 #if defined(_POSIX_VERSION) && !defined (HOST_WASM)
48 #include <limits.h> /* for PAGESIZE */
53 #ifdef HAVE_SYS_RESOURCE_H
54 #include <sys/resource.h>
57 static pthread_mutex_t memory_barrier_process_wide_mutex
= PTHREAD_MUTEX_INITIALIZER
;
58 static void *memory_barrier_process_wide_helper_page
;
61 mono_thread_platform_create_thread (MonoThreadStart thread_fn
, gpointer thread_data
, gsize
* const stack_size
, MonoNativeThreadId
*tid
)
68 res
= pthread_attr_init (&attr
);
70 g_error ("%s: pthread_attr_init failed, error: \"%s\" (%d)", __func__
, g_strerror (res
), res
);
73 set_stack_size
= *stack_size
;
77 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
78 if (set_stack_size
== 0) {
79 #if HAVE_VALGRIND_MEMCHECK_H
80 if (RUNNING_ON_VALGRIND
)
81 set_stack_size
= 1 << 20;
83 set_stack_size
= (SIZEOF_VOID_P
/ 4) * 1024 * 1024;
85 set_stack_size
= (SIZEOF_VOID_P
/ 4) * 1024 * 1024;
89 #ifdef PTHREAD_STACK_MIN
90 if (set_stack_size
< PTHREAD_STACK_MIN
)
91 set_stack_size
= PTHREAD_STACK_MIN
;
94 res
= pthread_attr_setstacksize (&attr
, set_stack_size
);
96 g_error ("%s: pthread_attr_setstacksize failed, error: \"%s\" (%d)", __func__
, g_strerror (res
), res
);
97 #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
99 /* Actually start the thread */
100 res
= mono_gc_pthread_create (&thread
, &attr
, (gpointer (*)(gpointer
)) thread_fn
, thread_data
);
102 res
= pthread_attr_destroy (&attr
);
104 g_error ("%s: pthread_attr_destroy failed, error: \"%s\" (%d)", __func__
, g_strerror (res
), res
);
113 res
= pthread_attr_getstacksize (&attr
, stack_size
);
115 g_error ("%s: pthread_attr_getstacksize failed, error: \"%s\" (%d)", __func__
, g_strerror (res
), res
);
118 res
= pthread_attr_destroy (&attr
);
120 g_error ("%s: pthread_attr_destroy failed, error: \"%s\" (%d)", __func__
, g_strerror (res
), res
);
126 mono_threads_platform_init (void)
131 mono_threads_platform_in_critical_region (THREAD_INFO_TYPE
*info
)
137 mono_threads_platform_yield (void)
139 return sched_yield () == 0;
143 mono_threads_platform_exit (gsize exit_code
)
145 pthread_exit ((gpointer
) exit_code
);
150 mono_thread_info_get_system_max_stack_size (void)
152 /* For now, we do not enforce any limits */
158 mono_thread_info_get_system_max_stack_size (void)
162 /* If getrlimit fails, we don't enforce any limits. */
163 if (getrlimit (RLIMIT_STACK
, &lim
))
165 /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
166 if (lim
.rlim_max
> (rlim_t
)INT_MAX
)
168 return (int)lim
.rlim_max
;
173 mono_threads_pthread_kill (MonoThreadInfo
*info
, int signum
)
175 THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum
, info
, mono_thread_info_get_tid (info
));
179 #ifdef USE_TKILL_ON_ANDROID
180 int old_errno
= errno
;
182 result
= tkill (info
->native_handle
, signum
);
186 mono_set_errno (old_errno
);
188 #elif defined (HAVE_PTHREAD_KILL)
189 result
= pthread_kill (mono_thread_info_get_tid (info
), signum
);
192 g_error ("pthread_kill () is not supported by this platform");
196 * ESRCH just means the thread is gone; this is usually not fatal.
198 * ENOTSUP can occur if we try to send signals (e.g. for sampling) to Grand
199 * Central Dispatch threads on Apple platforms. This is kinda bad, but
200 * since there's really nothing we can do about it, we just ignore it and
203 * All other error codes are ill-documented and usually stem from various
204 * OS-specific idiosyncracies. We want to know about these, so fail loudly.
205 * One example is EAGAIN on Linux, which indicates a signal queue overflow.
209 #if defined (__MACH__) && defined (ENOTSUP)
213 g_error ("%s: pthread_kill failed with error %d - potential kernel OOM or signal queue overflow", __func__
, result
);
219 mono_native_thread_id_get (void)
221 return pthread_self ();
225 mono_native_thread_id_equals (MonoNativeThreadId id1
, MonoNativeThreadId id2
)
227 return pthread_equal (id1
, id2
);
231 * mono_native_thread_create:
233 * Low level thread creation function without any GC wrappers.
236 mono_native_thread_create (MonoNativeThreadId
*tid
, gpointer func
, gpointer arg
)
238 return pthread_create (tid
, NULL
, (void *(*)(void *)) func
, arg
) == 0;
242 mono_native_thread_get_name (MonoNativeThreadId tid
, char *name_out
, size_t max_len
)
244 #ifdef HAVE_PTHREAD_GETNAME_NP
245 int error
= pthread_getname_np(tid
, name_out
, max_len
);
248 return strlen(name_out
);
255 mono_native_thread_set_name (MonoNativeThreadId tid
, const char *name
)
259 * We can't set the thread name for other threads, but we can at least make
260 * it work for threads that try to change their own name.
262 if (tid
!= mono_native_thread_id_get ())
266 pthread_setname_np ("");
270 strncpy (n
, name
, sizeof (n
) - 1);
271 n
[sizeof (n
) - 1] = '\0';
272 pthread_setname_np (n
);
274 #elif defined (__HAIKU__)
276 haiku_tid
= get_pthread_thread_id (tid
);
278 rename_thread (haiku_tid
, "");
280 rename_thread (haiku_tid
, name
);
282 #elif defined (__NetBSD__)
284 pthread_setname_np (tid
, "%s", (void*)"");
286 char n
[PTHREAD_MAX_NAMELEN_NP
];
288 strncpy (n
, name
, sizeof (n
) - 1);
289 n
[sizeof (n
) - 1] = '\0';
290 pthread_setname_np (tid
, "%s", (void*)n
);
292 #elif defined (HAVE_PTHREAD_SETNAME_NP)
294 pthread_setname_np (tid
, "");
298 strncpy (n
, name
, sizeof (n
) - 1);
299 n
[sizeof (n
) - 1] = '\0';
300 pthread_setname_np (tid
, n
);
306 mono_native_thread_join (MonoNativeThreadId tid
)
310 return !pthread_join (tid
, &res
);
314 mono_memory_barrier_process_wide (void)
318 status
= pthread_mutex_lock (&memory_barrier_process_wide_mutex
);
319 g_assert (status
== 0);
321 if (memory_barrier_process_wide_helper_page
== NULL
) {
322 status
= posix_memalign(&memory_barrier_process_wide_helper_page
, PAGESIZE
, PAGESIZE
);
323 g_assert (status
== 0);
326 // Changing a helper memory page protection from read / write to no access
327 // causes the OS to issue IPI to flush TLBs on all processors. This also
328 // results in flushing the processor buffers.
329 status
= mprotect (memory_barrier_process_wide_helper_page
, PAGESIZE
, PROT_READ
| PROT_WRITE
);
330 g_assert (status
== 0);
332 // Ensure that the page is dirty before we change the protection so that
333 // we prevent the OS from skipping the global TLB flush.
334 __sync_add_and_fetch ((size_t*)memory_barrier_process_wide_helper_page
, 1);
336 status
= mprotect (memory_barrier_process_wide_helper_page
, PAGESIZE
, PROT_NONE
);
337 g_assert (status
== 0);
339 status
= pthread_mutex_unlock (&memory_barrier_process_wide_mutex
);
340 g_assert (status
== 0);
343 #endif /* defined(_POSIX_VERSION) */
345 #if defined(USE_POSIX_BACKEND)
348 mono_threads_suspend_begin_async_suspend (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
350 int sig
= interrupt_kernel
? mono_threads_suspend_get_abort_signal () : mono_threads_suspend_get_suspend_signal ();
352 if (!mono_threads_pthread_kill (info
, sig
)) {
353 mono_threads_add_to_pending_operation_set (info
);
356 if (!mono_threads_transition_abort_async_suspend (info
)) {
357 /* We raced with self suspend and lost so suspend can continue. */
358 g_assert (mono_threads_is_hybrid_suspension_enabled ());
359 info
->suspend_can_continue
= TRUE
;
360 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (info
));
367 mono_threads_suspend_check_suspend_result (MonoThreadInfo
*info
)
369 return info
->suspend_can_continue
;
373 This begins async resume. This function must do the following:
375 - Install an async target if one was requested.
376 - Notify the target to resume.
379 mono_threads_suspend_begin_async_resume (MonoThreadInfo
*info
)
381 int sig
= mono_threads_suspend_get_restart_signal ();
383 if (!mono_threads_pthread_kill (info
, sig
)) {
384 mono_threads_add_to_pending_operation_set (info
);
391 mono_threads_suspend_abort_syscall (MonoThreadInfo
*info
)
393 /* We signal a thread to break it from the current syscall.
394 * This signal should not be interpreted as a suspend request. */
395 info
->syscall_break_signal
= TRUE
;
396 if (mono_threads_pthread_kill (info
, mono_threads_suspend_get_abort_signal ()) == 0) {
397 mono_threads_add_to_pending_operation_set (info
);
402 mono_threads_suspend_register (MonoThreadInfo
*info
)
404 #if defined (HOST_ANDROID)
405 info
->native_handle
= gettid ();
410 mono_threads_suspend_free (MonoThreadInfo
*info
)
415 mono_threads_suspend_init (void)
419 #endif /* defined(USE_POSIX_BACKEND) */