[threads] Import mono_thread_info_set_priority (#3543)
[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-threads-posix-signals.h>
19 #include <mono/utils/mono-coop-semaphore.h>
20 #include <mono/metadata/gc-internals.h>
21 #include <mono/utils/w32handle.h>
22
23 #include <errno.h>
24
25 #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
26 #define USE_TKILL_ON_ANDROID 1
27 #endif
28
29 #ifdef USE_TKILL_ON_ANDROID
30 extern int tkill (pid_t tid, int signal);
31 #endif
32
33 #if defined(_POSIX_VERSION) || defined(__native_client__)
34
35 #include <pthread.h>
36
37 #include <sys/resource.h>
38
39 #if defined(__native_client__)
40 void nacl_shutdown_gc_thread(void);
41 #endif
42
43 void
44 mono_threads_platform_register (MonoThreadInfo *info)
45 {
46         gpointer thread_handle;
47
48         info->owned_mutexes = g_ptr_array_new ();
49
50         thread_handle = mono_w32handle_new (MONO_W32HANDLE_THREAD, NULL);
51         if (thread_handle == INVALID_HANDLE_VALUE)
52                 g_error ("%s: failed to create handle", __func__);
53
54         g_assert (!info->handle);
55         info->handle = thread_handle;
56 }
57
58 int
59 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid)
60 {
61         pthread_attr_t attr;
62         pthread_t thread;
63         int policy;
64         struct sched_param param;
65         gint res;
66
67         res = pthread_attr_init (&attr);
68         g_assert (!res);
69
70 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
71         if (stack_size == 0) {
72 #if HAVE_VALGRIND_MEMCHECK_H
73                 if (RUNNING_ON_VALGRIND)
74                         stack_size = 1 << 20;
75                 else
76                         stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
77 #else
78                 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
79 #endif
80         }
81
82 #ifdef PTHREAD_STACK_MIN
83         if (stack_size < PTHREAD_STACK_MIN)
84                 stack_size = PTHREAD_STACK_MIN;
85 #endif
86
87         res = pthread_attr_setstacksize (&attr, stack_size);
88         g_assert (!res);
89 #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
90
91         memset (&param, 0, sizeof (param));
92
93         res = pthread_attr_getschedpolicy (&attr, &policy);
94         if (res != 0)
95                 g_error ("%s: pthread_attr_getschedpolicy failed, error: \"%s\" (%d)", g_strerror (res), res);
96
97 #ifdef _POSIX_PRIORITY_SCHEDULING
98         int max, min;
99
100         /* Necessary to get valid priority range */
101
102         min = sched_get_priority_min (policy);
103         max = sched_get_priority_max (policy);
104
105         if (max > 0 && min >= 0 && max > min)
106                 param.sched_priority = (max - min) / 2 + min;
107         else
108 #endif
109         {
110                 switch (policy) {
111                 case SCHED_FIFO:
112                 case SCHED_RR:
113                         param.sched_priority = 50;
114                         break;
115 #ifdef SCHED_BATCH
116                 case SCHED_BATCH:
117 #endif
118                 case SCHED_OTHER:
119                         param.sched_priority = 0;
120                         break;
121                 default:
122                         g_error ("%s: unknown policy %d", __func__, policy);
123                 }
124         }
125
126         res = pthread_attr_setschedparam (&attr, &param);
127         if (res != 0)
128                 g_error ("%s: pthread_attr_setschedparam failed, error: \"%s\" (%d)", g_strerror (res), res);
129
130         /* Actually start the thread */
131         res = mono_gc_pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data);
132         if (res)
133                 return -1;
134
135         if (out_tid)
136                 *out_tid = thread;
137
138         return 0;
139 }
140
141 gboolean
142 mono_threads_platform_yield (void)
143 {
144         return sched_yield () == 0;
145 }
146
147 void
148 mono_threads_platform_exit (int exit_code)
149 {
150 #if defined(__native_client__)
151         nacl_shutdown_gc_thread();
152 #endif
153
154         mono_thread_info_detach ();
155
156         pthread_exit (NULL);
157 }
158
159 void
160 mono_threads_platform_unregister (MonoThreadInfo *info)
161 {
162         mono_threads_platform_set_exited (info);
163 }
164
165 int
166 mono_threads_get_max_stack_size (void)
167 {
168         struct rlimit lim;
169
170         /* If getrlimit fails, we don't enforce any limits. */
171         if (getrlimit (RLIMIT_STACK, &lim))
172                 return INT_MAX;
173         /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
174         if (lim.rlim_max > (rlim_t)INT_MAX)
175                 return INT_MAX;
176         return (int)lim.rlim_max;
177 }
178
179 gpointer
180 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
181 {
182         g_assert (info->handle);
183         mono_w32handle_ref (info->handle);
184         return info->handle;
185 }
186
187 HANDLE
188 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
189 {
190         mono_w32handle_ref (handle);
191
192         return handle;
193 }
194
195 void
196 mono_threads_platform_close_thread_handle (HANDLE handle)
197 {
198         mono_w32handle_unref (handle);
199 }
200
201 int
202 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
203 {
204         THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
205 #ifdef USE_TKILL_ON_ANDROID
206         int result, old_errno = errno;
207         result = tkill (info->native_handle, signum);
208         if (result < 0) {
209                 result = errno;
210                 errno = old_errno;
211         }
212         return result;
213 #elif defined(__native_client__)
214         /* Workaround pthread_kill abort() in NaCl glibc. */
215         return 0;
216 #elif !defined(HAVE_PTHREAD_KILL)
217         g_error ("pthread_kill() is not supported by this platform");
218 #else
219         return pthread_kill (mono_thread_info_get_tid (info), signum);
220 #endif
221 }
222
223 MonoNativeThreadId
224 mono_native_thread_id_get (void)
225 {
226         return pthread_self ();
227 }
228
229 gboolean
230 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
231 {
232         return pthread_equal (id1, id2);
233 }
234
235 /*
236  * mono_native_thread_create:
237  *
238  *   Low level thread creation function without any GC wrappers.
239  */
240 gboolean
241 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
242 {
243         return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
244 }
245
246 void
247 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
248 {
249 #ifdef __MACH__
250         /*
251          * We can't set the thread name for other threads, but we can at least make
252          * it work for threads that try to change their own name.
253          */
254         if (tid != mono_native_thread_id_get ())
255                 return;
256
257         if (!name) {
258                 pthread_setname_np ("");
259         } else {
260                 char n [63];
261
262                 strncpy (n, name, 63);
263                 n [62] = '\0';
264                 pthread_setname_np (n);
265         }
266 #elif defined (__NetBSD__)
267         if (!name) {
268                 pthread_setname_np (tid, "%s", (void*)"");
269         } else {
270                 char n [PTHREAD_MAX_NAMELEN_NP];
271
272                 strncpy (n, name, PTHREAD_MAX_NAMELEN_NP);
273                 n [PTHREAD_MAX_NAMELEN_NP - 1] = '\0';
274                 pthread_setname_np (tid, "%s", (void*)n);
275         }
276 #elif defined (HAVE_PTHREAD_SETNAME_NP)
277         if (!name) {
278                 pthread_setname_np (tid, "");
279         } else {
280                 char n [16];
281
282                 strncpy (n, name, 16);
283                 n [15] = '\0';
284                 pthread_setname_np (tid, n);
285         }
286 #endif
287 }
288
289 void
290 mono_threads_platform_set_exited (MonoThreadInfo *info)
291 {
292         gpointer mutex_handle;
293         int i, thr_ret;
294         pid_t pid;
295         pthread_t tid;
296
297         g_assert (info->handle);
298
299         if (mono_w32handle_issignalled (info->handle))
300                 g_error ("%s: handle %p thread %p has already exited, it's handle is signalled", __func__, info->handle, mono_thread_info_get_tid (info));
301         if (mono_w32handle_get_type (info->handle) == MONO_W32HANDLE_UNUSED)
302                 g_error ("%s: handle %p thread %p has already exited, it's handle type is 'unused'", __func__, info->handle, mono_thread_info_get_tid (info));
303
304         pid = wapi_getpid ();
305         tid = pthread_self ();
306
307         for (i = 0; i < info->owned_mutexes->len; i++) {
308                 mutex_handle = g_ptr_array_index (info->owned_mutexes, i);
309                 wapi_mutex_abandon (mutex_handle, pid, tid);
310                 mono_thread_info_disown_mutex (info, mutex_handle);
311         }
312
313         g_ptr_array_free (info->owned_mutexes, TRUE);
314
315         thr_ret = mono_w32handle_lock_handle (info->handle);
316         g_assert (thr_ret == 0);
317
318         mono_w32handle_set_signal_state (info->handle, TRUE, TRUE);
319
320         thr_ret = mono_w32handle_unlock_handle (info->handle);
321         g_assert (thr_ret == 0);
322
323         /* The thread is no longer active, so unref it */
324         mono_w32handle_unref (info->handle);
325
326         info->handle = NULL;
327 }
328
329 void
330 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
331 {
332         int i;
333
334         g_string_append_printf (text, "thread handle %p state : ", info->handle);
335
336         mono_thread_info_describe_interrupt_token (info, text);
337
338         g_string_append_printf (text, ", owns (");
339         for (i = 0; i < info->owned_mutexes->len; i++)
340                 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (info->owned_mutexes, i));
341         g_string_append_printf (text, ")");
342 }
343
344 void
345 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
346 {
347         mono_w32handle_ref (mutex_handle);
348
349         g_ptr_array_add (info->owned_mutexes, mutex_handle);
350 }
351
352 void
353 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
354 {
355         mono_w32handle_unref (mutex_handle);
356
357         g_ptr_array_remove (info->owned_mutexes, mutex_handle);
358 }
359
360 static const gchar* thread_typename (void)
361 {
362         return "Thread";
363 }
364
365 static gsize thread_typesize (void)
366 {
367         return 0;
368 }
369
370 static MonoW32HandleOps thread_ops = {
371         NULL,                           /* close */
372         NULL,                           /* signal */
373         NULL,                           /* own */
374         NULL,                           /* is_owned */
375         NULL,                           /* special_wait */
376         NULL,                           /* prewait */
377         NULL,                           /* details */
378         thread_typename,        /* typename */
379         thread_typesize,        /* typesize */
380 };
381
382 void
383 mono_threads_platform_init (void)
384 {
385         mono_w32handle_register_ops (MONO_W32HANDLE_THREAD, &thread_ops);
386
387         mono_w32handle_register_capabilities (MONO_W32HANDLE_THREAD, MONO_W32HANDLE_CAP_WAIT);
388 }
389
390 #endif /* defined(_POSIX_VERSION) || defined(__native_client__) */
391
392 #if defined(USE_POSIX_BACKEND)
393
394 gboolean
395 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
396 {
397         int sig = interrupt_kernel ? mono_threads_posix_get_abort_signal () :  mono_threads_posix_get_suspend_signal ();
398
399         if (!mono_threads_pthread_kill (info, sig)) {
400                 mono_threads_add_to_pending_operation_set (info);
401                 return TRUE;
402         }
403         return FALSE;
404 }
405
406 gboolean
407 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
408 {
409         return info->suspend_can_continue;
410 }
411
412 /*
413 This begins async resume. This function must do the following:
414
415 - Install an async target if one was requested.
416 - Notify the target to resume.
417 */
418 gboolean
419 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
420 {
421         mono_threads_add_to_pending_operation_set (info);
422         return mono_threads_pthread_kill (info, mono_threads_posix_get_restart_signal ()) == 0;
423 }
424
425 void
426 mono_threads_suspend_register (MonoThreadInfo *info)
427 {
428 #if defined (PLATFORM_ANDROID)
429         info->native_handle = gettid ();
430 #endif
431 }
432
433 void
434 mono_threads_suspend_free (MonoThreadInfo *info)
435 {
436 }
437
438 void
439 mono_threads_suspend_init (void)
440 {
441         mono_threads_posix_init_signals (MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART);
442 }
443
444 #endif /* defined(USE_POSIX_BACKEND) */