Changes GC.cs
[mono-project.git] / mono / utils / mono-threads-posix.c
blobd467657a6407831b76ee5b188410b584b927b156
1 /**
2 * \file
3 * Low-level threading, posix version
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * (C) 2011 Novell, Inc
9 */
11 #include <config.h>
13 /* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
14 #if defined (__MACH__)
15 #define _DARWIN_C_SOURCE 1
16 #endif
18 #if defined (HOST_FUCHSIA)
19 #include <zircon/syscalls.h>
20 #endif
22 #if defined (__HAIKU__)
23 #include <os/kernel/OS.h>
24 #endif
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>
33 #include <errno.h>
35 #if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
36 #define USE_TKILL_ON_ANDROID 1
37 #endif
39 #ifdef USE_TKILL_ON_ANDROID
40 extern int tkill (pid_t tid, int signal);
41 #endif
43 #if defined(_POSIX_VERSION) && !defined (HOST_WASM)
45 #include <pthread.h>
47 #include <sys/mman.h>
48 #include <limits.h> /* for PAGESIZE */
49 #ifndef PAGESIZE
50 #define PAGESIZE 4096
51 #endif
53 #ifdef HAVE_SYS_RESOURCE_H
54 #include <sys/resource.h>
55 #endif
57 static pthread_mutex_t memory_barrier_process_wide_mutex = PTHREAD_MUTEX_INITIALIZER;
58 static void *memory_barrier_process_wide_helper_page;
60 gboolean
61 mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
63 pthread_attr_t attr;
64 pthread_t thread;
65 gint res;
66 gsize set_stack_size;
68 res = pthread_attr_init (&attr);
69 if (res != 0)
70 g_error ("%s: pthread_attr_init failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
72 if (stack_size)
73 set_stack_size = *stack_size;
74 else
75 set_stack_size = 0;
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;
82 else
83 set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
84 #else
85 set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
86 #endif
89 #ifdef PTHREAD_STACK_MIN
90 if (set_stack_size < PTHREAD_STACK_MIN)
91 set_stack_size = PTHREAD_STACK_MIN;
92 #endif
94 res = pthread_attr_setstacksize (&attr, set_stack_size);
95 if (res != 0)
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);
101 if (res) {
102 res = pthread_attr_destroy (&attr);
103 if (res != 0)
104 g_error ("%s: pthread_attr_destroy failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
106 return FALSE;
109 if (tid)
110 *tid = thread;
112 if (stack_size) {
113 res = pthread_attr_getstacksize (&attr, stack_size);
114 if (res != 0)
115 g_error ("%s: pthread_attr_getstacksize failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
118 res = pthread_attr_destroy (&attr);
119 if (res != 0)
120 g_error ("%s: pthread_attr_destroy failed, error: \"%s\" (%d)", __func__, g_strerror (res), res);
122 return TRUE;
125 void
126 mono_threads_platform_init (void)
130 gboolean
131 mono_threads_platform_in_critical_region (THREAD_INFO_TYPE *info)
133 return FALSE;
136 gboolean
137 mono_threads_platform_yield (void)
139 return sched_yield () == 0;
142 void
143 mono_threads_platform_exit (gsize exit_code)
145 pthread_exit ((gpointer) exit_code);
148 #if HOST_FUCHSIA
150 mono_thread_info_get_system_max_stack_size (void)
152 /* For now, we do not enforce any limits */
153 return INT_MAX;
156 #else
158 mono_thread_info_get_system_max_stack_size (void)
160 struct rlimit lim;
162 /* If getrlimit fails, we don't enforce any limits. */
163 if (getrlimit (RLIMIT_STACK, &lim))
164 return INT_MAX;
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)
167 return INT_MAX;
168 return (int)lim.rlim_max;
170 #endif
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));
177 int result;
179 #ifdef USE_TKILL_ON_ANDROID
180 int old_errno = errno;
182 result = tkill (info->native_handle, signum);
184 if (result < 0) {
185 result = errno;
186 mono_set_errno (old_errno);
188 #elif defined (HAVE_PTHREAD_KILL)
189 result = pthread_kill (mono_thread_info_get_tid (info), signum);
190 #else
191 result = -1;
192 g_error ("pthread_kill () is not supported by this platform");
193 #endif
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
201 * move on.
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.
207 if (result &&
208 result != ESRCH
209 #if defined (__MACH__) && defined (ENOTSUP)
210 && result != ENOTSUP
211 #endif
213 g_error ("%s: pthread_kill failed with error %d - potential kernel OOM or signal queue overflow", __func__, result);
215 return result;
218 MonoNativeThreadId
219 mono_native_thread_id_get (void)
221 return pthread_self ();
224 gboolean
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.
235 gboolean
236 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
238 return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
241 size_t
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);
246 if (error != 0)
247 return 0;
248 return strlen(name_out);
249 #else
250 return 0;
251 #endif
254 void
255 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
257 #ifdef __MACH__
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 ())
263 return;
265 if (!name) {
266 pthread_setname_np ("");
267 } else {
268 char n [63];
270 strncpy (n, name, sizeof (n) - 1);
271 n [sizeof (n) - 1] = '\0';
272 pthread_setname_np (n);
274 #elif defined (__HAIKU__)
275 thread_id haiku_tid;
276 haiku_tid = get_pthread_thread_id (tid);
277 if (!name) {
278 rename_thread (haiku_tid, "");
279 } else {
280 rename_thread (haiku_tid, name);
282 #elif defined (__NetBSD__)
283 if (!name) {
284 pthread_setname_np (tid, "%s", (void*)"");
285 } else {
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)
293 if (!name) {
294 pthread_setname_np (tid, "");
295 } else {
296 char n [16];
298 strncpy (n, name, sizeof (n) - 1);
299 n [sizeof (n) - 1] = '\0';
300 pthread_setname_np (tid, n);
302 #endif
305 gboolean
306 mono_native_thread_join (MonoNativeThreadId tid)
308 void *res;
310 return !pthread_join (tid, &res);
313 void
314 mono_memory_barrier_process_wide (void)
316 int status;
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)
347 gboolean
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);
354 return TRUE;
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));
361 return TRUE;
363 return FALSE;
366 gboolean
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.
378 gboolean
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);
385 return TRUE;
387 return FALSE;
390 void
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);
401 void
402 mono_threads_suspend_register (MonoThreadInfo *info)
404 #if defined (HOST_ANDROID)
405 info->native_handle = gettid ();
406 #endif
409 void
410 mono_threads_suspend_free (MonoThreadInfo *info)
414 void
415 mono_threads_suspend_init (void)
419 #endif /* defined(USE_POSIX_BACKEND) */