66746ad3dfa2896823229ab1e97335b19eb32351
[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 void
137 mono_threads_platform_init (void)
138 {
139 }
140
141 gboolean
142 mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
143 {
144         return FALSE;
145 }
146
147 gboolean
148 mono_threads_platform_yield (void)
149 {
150         return sched_yield () == 0;
151 }
152
153 void
154 mono_threads_platform_exit (gsize exit_code)
155 {
156         pthread_exit ((gpointer) exit_code);
157 }
158
159 int
160 mono_threads_get_max_stack_size (void)
161 {
162         struct rlimit lim;
163
164         /* If getrlimit fails, we don't enforce any limits. */
165         if (getrlimit (RLIMIT_STACK, &lim))
166                 return INT_MAX;
167         /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
168         if (lim.rlim_max > (rlim_t)INT_MAX)
169                 return INT_MAX;
170         return (int)lim.rlim_max;
171 }
172
173 int
174 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
175 {
176         THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
177 #ifdef USE_TKILL_ON_ANDROID
178         int result, old_errno = errno;
179         result = tkill (info->native_handle, signum);
180         if (result < 0) {
181                 result = errno;
182                 errno = old_errno;
183         }
184         return result;
185 #elif defined(__native_client__)
186         /* Workaround pthread_kill abort() in NaCl glibc. */
187         return 0;
188 #elif !defined(HAVE_PTHREAD_KILL)
189         g_error ("pthread_kill() is not supported by this platform");
190 #else
191         return pthread_kill (mono_thread_info_get_tid (info), signum);
192 #endif
193 }
194
195 MonoNativeThreadId
196 mono_native_thread_id_get (void)
197 {
198         return pthread_self ();
199 }
200
201 gboolean
202 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
203 {
204         return pthread_equal (id1, id2);
205 }
206
207 /*
208  * mono_native_thread_create:
209  *
210  *   Low level thread creation function without any GC wrappers.
211  */
212 gboolean
213 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
214 {
215         return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
216 }
217
218 void
219 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
220 {
221 #ifdef __MACH__
222         /*
223          * We can't set the thread name for other threads, but we can at least make
224          * it work for threads that try to change their own name.
225          */
226         if (tid != mono_native_thread_id_get ())
227                 return;
228
229         if (!name) {
230                 pthread_setname_np ("");
231         } else {
232                 char n [63];
233
234                 strncpy (n, name, 63);
235                 n [62] = '\0';
236                 pthread_setname_np (n);
237         }
238 #elif defined (__NetBSD__)
239         if (!name) {
240                 pthread_setname_np (tid, "%s", (void*)"");
241         } else {
242                 char n [PTHREAD_MAX_NAMELEN_NP];
243
244                 strncpy (n, name, PTHREAD_MAX_NAMELEN_NP);
245                 n [PTHREAD_MAX_NAMELEN_NP - 1] = '\0';
246                 pthread_setname_np (tid, "%s", (void*)n);
247         }
248 #elif defined (HAVE_PTHREAD_SETNAME_NP)
249         if (!name) {
250                 pthread_setname_np (tid, "");
251         } else {
252                 char n [16];
253
254                 strncpy (n, name, 16);
255                 n [15] = '\0';
256                 pthread_setname_np (tid, n);
257         }
258 #endif
259 }
260
261 gboolean
262 mono_native_thread_join (MonoNativeThreadId tid)
263 {
264         void *res;
265
266         return !pthread_join (tid, &res);
267 }
268
269 #endif /* defined(_POSIX_VERSION) || defined(__native_client__) */
270
271 #if defined(USE_POSIX_BACKEND)
272
273 gboolean
274 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
275 {
276         int sig = interrupt_kernel ? mono_threads_suspend_get_abort_signal () :  mono_threads_suspend_get_suspend_signal ();
277
278         if (!mono_threads_pthread_kill (info, sig)) {
279                 mono_threads_add_to_pending_operation_set (info);
280                 return TRUE;
281         }
282         return FALSE;
283 }
284
285 gboolean
286 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
287 {
288         return info->suspend_can_continue;
289 }
290
291 /*
292 This begins async resume. This function must do the following:
293
294 - Install an async target if one was requested.
295 - Notify the target to resume.
296 */
297 gboolean
298 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
299 {
300         int sig = mono_threads_suspend_get_restart_signal ();
301
302         if (!mono_threads_pthread_kill (info, sig)) {
303                 mono_threads_add_to_pending_operation_set (info);
304                 return TRUE;
305         }
306         return FALSE;
307 }
308
309 void
310 mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
311 {
312         /* We signal a thread to break it from the current syscall.
313          * This signal should not be interpreted as a suspend request. */
314         info->syscall_break_signal = TRUE;
315         if (mono_threads_pthread_kill (info, mono_threads_suspend_get_abort_signal ()) == 0) {
316                 mono_threads_add_to_pending_operation_set (info);
317         }
318 }
319
320 gboolean
321 mono_threads_suspend_needs_abort_syscall (void)
322 {
323         return TRUE;
324 }
325
326 void
327 mono_threads_suspend_register (MonoThreadInfo *info)
328 {
329 #if defined (PLATFORM_ANDROID)
330         info->native_handle = gettid ();
331 #endif
332 }
333
334 void
335 mono_threads_suspend_free (MonoThreadInfo *info)
336 {
337 }
338
339 void
340 mono_threads_suspend_init (void)
341 {
342 }
343
344 #endif /* defined(USE_POSIX_BACKEND) */