Merge pull request #3406 from alexanderkyte/mobile_static_default
[mono.git] / mono / utils / mono-threads-posix.c
1 /*
2  * mono-threads-posix.c: Low-level threading, posix version
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2011 Novell, Inc
8  */
9
10 #include <config.h>
11
12 /* For pthread_main_np, pthread_get_stackaddr_np and pthread_get_stacksize_np */
13 #if defined (__MACH__)
14 #define _DARWIN_C_SOURCE 1
15 #endif
16
17 #include <mono/utils/mono-threads.h>
18 #include <mono/utils/mono-coop-semaphore.h>
19 #include <mono/metadata/gc-internals.h>
20 #include <mono/utils/mono-threads-debug.h>
21
22 #include <errno.h>
23
24 #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
25 #define USE_TKILL_ON_ANDROID 1
26 #endif
27
28 #ifdef USE_TKILL_ON_ANDROID
29 extern int tkill (pid_t tid, int signal);
30 #endif
31
32 #if defined(_POSIX_VERSION) || defined(__native_client__)
33
34 #include <pthread.h>
35
36 #include <sys/resource.h>
37
38 int
39 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid)
40 {
41         pthread_attr_t attr;
42         pthread_t thread;
43         int policy;
44         struct sched_param param;
45         gint res;
46         gsize set_stack_size;
47         size_t min_size;
48
49         res = pthread_attr_init (&attr);
50         g_assert (!res);
51
52         if (stack_size)
53                 set_stack_size = *stack_size;
54         else
55                 set_stack_size = 0;
56
57 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
58         if (set_stack_size == 0) {
59 #if HAVE_VALGRIND_MEMCHECK_H
60                 if (RUNNING_ON_VALGRIND)
61                         set_stack_size = 1 << 20;
62                 else
63                         set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
64 #else
65                 set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
66 #endif
67         }
68
69 #ifdef PTHREAD_STACK_MIN
70         if (set_stack_size < PTHREAD_STACK_MIN)
71                 set_stack_size = PTHREAD_STACK_MIN;
72 #endif
73
74         res = pthread_attr_setstacksize (&attr, set_stack_size);
75         g_assert (!res);
76 #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
77
78         memset (&param, 0, sizeof (param));
79
80         res = pthread_attr_getschedpolicy (&attr, &policy);
81         if (res != 0)
82                 g_error ("%s: pthread_attr_getschedpolicy failed, error: \"%s\" (%d)", g_strerror (res), res);
83
84 #ifdef _POSIX_PRIORITY_SCHEDULING
85         int max, min;
86
87         /* Necessary to get valid priority range */
88
89         min = sched_get_priority_min (policy);
90         max = sched_get_priority_max (policy);
91
92         if (max > 0 && min >= 0 && max > min)
93                 param.sched_priority = (max - min) / 2 + min;
94         else
95 #endif
96         {
97                 switch (policy) {
98                 case SCHED_FIFO:
99                 case SCHED_RR:
100                         param.sched_priority = 50;
101                         break;
102 #ifdef SCHED_BATCH
103                 case SCHED_BATCH:
104 #endif
105                 case SCHED_OTHER:
106                         param.sched_priority = 0;
107                         break;
108                 default:
109                         g_error ("%s: unknown policy %d", __func__, policy);
110                 }
111         }
112
113         res = pthread_attr_setschedparam (&attr, &param);
114         if (res != 0)
115                 g_error ("%s: pthread_attr_setschedparam failed, error: \"%s\" (%d)", g_strerror (res), res);
116
117         if (stack_size) {
118                 res = pthread_attr_getstacksize (&attr, &min_size);
119                 if (res != 0)
120                         g_error ("%s: pthread_attr_getstacksize failed, error: \"%s\" (%d)", g_strerror (res), res);
121                 else
122                         *stack_size = min_size;
123         }
124
125         /* Actually start the thread */
126         res = mono_gc_pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data);
127         if (res)
128                 return -1;
129
130         if (out_tid)
131                 *out_tid = thread;
132
133         return 0;
134 }
135
136 gboolean
137 mono_threads_platform_yield (void)
138 {
139         return sched_yield () == 0;
140 }
141
142 void
143 mono_threads_platform_exit (gsize exit_code)
144 {
145         pthread_exit ((gpointer) exit_code);
146 }
147
148 int
149 mono_threads_get_max_stack_size (void)
150 {
151         struct rlimit lim;
152
153         /* If getrlimit fails, we don't enforce any limits. */
154         if (getrlimit (RLIMIT_STACK, &lim))
155                 return INT_MAX;
156         /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
157         if (lim.rlim_max > (rlim_t)INT_MAX)
158                 return INT_MAX;
159         return (int)lim.rlim_max;
160 }
161
162 int
163 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
164 {
165         THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
166 #ifdef USE_TKILL_ON_ANDROID
167         int result, old_errno = errno;
168         result = tkill (info->native_handle, signum);
169         if (result < 0) {
170                 result = errno;
171                 errno = old_errno;
172         }
173         return result;
174 #elif defined(__native_client__)
175         /* Workaround pthread_kill abort() in NaCl glibc. */
176         return 0;
177 #elif !defined(HAVE_PTHREAD_KILL)
178         g_error ("pthread_kill() is not supported by this platform");
179 #else
180         return pthread_kill (mono_thread_info_get_tid (info), signum);
181 #endif
182 }
183
184 MonoNativeThreadId
185 mono_native_thread_id_get (void)
186 {
187         return pthread_self ();
188 }
189
190 gboolean
191 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
192 {
193         return pthread_equal (id1, id2);
194 }
195
196 /*
197  * mono_native_thread_create:
198  *
199  *   Low level thread creation function without any GC wrappers.
200  */
201 gboolean
202 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
203 {
204         return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
205 }
206
207 void
208 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
209 {
210 #ifdef __MACH__
211         /*
212          * We can't set the thread name for other threads, but we can at least make
213          * it work for threads that try to change their own name.
214          */
215         if (tid != mono_native_thread_id_get ())
216                 return;
217
218         if (!name) {
219                 pthread_setname_np ("");
220         } else {
221                 char n [63];
222
223                 strncpy (n, name, 63);
224                 n [62] = '\0';
225                 pthread_setname_np (n);
226         }
227 #elif defined (__NetBSD__)
228         if (!name) {
229                 pthread_setname_np (tid, "%s", (void*)"");
230         } else {
231                 char n [PTHREAD_MAX_NAMELEN_NP];
232
233                 strncpy (n, name, PTHREAD_MAX_NAMELEN_NP);
234                 n [PTHREAD_MAX_NAMELEN_NP - 1] = '\0';
235                 pthread_setname_np (tid, "%s", (void*)n);
236         }
237 #elif defined (HAVE_PTHREAD_SETNAME_NP)
238         if (!name) {
239                 pthread_setname_np (tid, "");
240         } else {
241                 char n [16];
242
243                 strncpy (n, name, 16);
244                 n [15] = '\0';
245                 pthread_setname_np (tid, n);
246         }
247 #endif
248 }
249
250 gboolean
251 mono_native_thread_join (MonoNativeThreadId tid)
252 {
253         void *res;
254
255         return !pthread_join (tid, &res);
256 }
257
258 #endif /* defined(_POSIX_VERSION) || defined(__native_client__) */
259
260 #if defined(USE_POSIX_BACKEND)
261
262 gboolean
263 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
264 {
265         int sig = interrupt_kernel ? mono_threads_suspend_get_abort_signal () :  mono_threads_suspend_get_suspend_signal ();
266
267         if (!mono_threads_pthread_kill (info, sig)) {
268                 mono_threads_add_to_pending_operation_set (info);
269                 return TRUE;
270         }
271         return FALSE;
272 }
273
274 gboolean
275 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
276 {
277         return info->suspend_can_continue;
278 }
279
280 /*
281 This begins async resume. This function must do the following:
282
283 - Install an async target if one was requested.
284 - Notify the target to resume.
285 */
286 gboolean
287 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
288 {
289         int sig = mono_threads_suspend_get_restart_signal ();
290
291         if (!mono_threads_pthread_kill (info, sig)) {
292                 mono_threads_add_to_pending_operation_set (info);
293                 return TRUE;
294         }
295         return FALSE;
296 }
297
298 void
299 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
300 {
301         /* We signal a thread to break it from the current syscall.
302          * This signal should not be interpreted as a suspend request. */
303         info->syscall_break_signal = TRUE;
304         if (mono_threads_pthread_kill (info, mono_threads_suspend_get_abort_signal ()) == 0) {
305                 mono_threads_add_to_pending_operation_set (info);
306         }
307 }
308
309 gboolean
310 mono_threads_suspend_needs_abort_syscall (void)
311 {
312         return TRUE;
313 }
314
315 void
316 mono_threads_suspend_register (MonoThreadInfo *info)
317 {
318 #if defined (PLATFORM_ANDROID)
319         info->native_handle = gettid ();
320 #endif
321 }
322
323 void
324 mono_threads_suspend_free (MonoThreadInfo *info)
325 {
326 }
327
328 void
329 mono_threads_suspend_init (void)
330 {
331 }
332
333 #endif /* defined(USE_POSIX_BACKEND) */