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