d8982e946aafaeb4d0048be10650abcfe36cd32b
[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 typedef struct {
44         pthread_t id;
45         GPtrArray *owned_mutexes;
46         gint32 priority;
47 } MonoW32HandleThread;
48
49 static gpointer
50 thread_handle_create (void)
51 {
52         MonoW32HandleThread thread_data;
53         gpointer thread_handle;
54
55         thread_data.id = pthread_self ();
56         thread_data.owned_mutexes = g_ptr_array_new ();
57         thread_data.priority = MONO_THREAD_PRIORITY_NORMAL;
58
59         thread_handle = mono_w32handle_new (MONO_W32HANDLE_THREAD, (gpointer) &thread_data);
60         if (thread_handle == INVALID_HANDLE_VALUE)
61                 return NULL;
62
63         /* We need to keep the handle alive, as long as the corresponding managed
64          * thread object is alive. The handle is going to be unref when calling
65          * the finalizer on the MonoThreadInternal object */
66         mono_w32handle_ref (thread_handle);
67
68         return thread_handle;
69 }
70
71 static int
72 win32_priority_to_posix_priority (MonoThreadPriority priority, int policy)
73 {
74         g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
75         g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
76
77 /* Necessary to get valid priority range */
78 #ifdef _POSIX_PRIORITY_SCHEDULING
79         int max, min;
80
81         min = sched_get_priority_min (policy);
82         max = sched_get_priority_max (policy);
83
84         /* Partition priority range linearly (cross-multiply) */
85         if (max > 0 && min >= 0 && max > min)
86                 return (int)((double) priority * (max - min) / (MONO_THREAD_PRIORITY_HIGHEST - MONO_THREAD_PRIORITY_LOWEST));
87 #endif
88
89         switch (policy) {
90         case SCHED_FIFO:
91         case SCHED_RR:
92                 return 50;
93 #ifdef SCHED_BATCH
94         case SCHED_BATCH:
95 #endif
96         case SCHED_OTHER:
97                 return 0;
98         default:
99                 return -1;
100         }
101 }
102
103 void
104 mono_threads_platform_register (MonoThreadInfo *info)
105 {
106         g_assert (!info->handle);
107         info->handle = thread_handle_create ();
108 }
109
110 typedef struct {
111         void *(*start_routine)(void*);
112         void *arg;
113         int flags;
114         gint32 priority;
115         MonoCoopSem registered;
116         HANDLE handle;
117 } StartInfo;
118
119 static void*
120 inner_start_thread (void *arg)
121 {
122         StartInfo *start_info = (StartInfo *) arg;
123         void *t_arg = start_info->arg;
124         int res;
125         void *(*start_func)(void*) = start_info->start_routine;
126         guint32 flags = start_info->flags;
127         void *result;
128         MonoThreadInfo *info;
129
130         info = mono_thread_info_attach (&result);
131         info->runtime_thread = TRUE;
132
133         start_info->handle = info->handle;
134
135         mono_threads_platform_set_priority (info, start_info->priority);
136
137         if (flags & CREATE_SUSPENDED) {
138                 info->create_suspended = TRUE;
139                 mono_coop_sem_init (&info->create_suspended_sem, 0);
140         }
141
142         /* start_info is not valid after this */
143         mono_coop_sem_post (&(start_info->registered));
144         start_info = NULL;
145
146         if (flags & CREATE_SUSPENDED) {
147                 res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
148                 g_assert (res != -1);
149
150                 mono_coop_sem_destroy (&info->create_suspended_sem);
151         }
152
153         /* Run the actual main function of the thread */
154         result = start_func (t_arg);
155
156         mono_threads_platform_exit (GPOINTER_TO_UINT (result));
157         g_assert_not_reached ();
158 }
159
160 HANDLE
161 mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
162 {
163         pthread_attr_t attr;
164         int res;
165         pthread_t thread;
166         StartInfo start_info;
167         guint32 stack_size;
168         int policy;
169         struct sched_param sp;
170
171         res = pthread_attr_init (&attr);
172         g_assert (!res);
173
174         if (tp->stack_size == 0) {
175 #if HAVE_VALGRIND_MEMCHECK_H
176                 if (RUNNING_ON_VALGRIND)
177                         stack_size = 1 << 20;
178                 else
179                         stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
180 #else
181                 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
182 #endif
183         } else
184                 stack_size = tp->stack_size;
185
186 #ifdef PTHREAD_STACK_MIN
187         if (stack_size < PTHREAD_STACK_MIN)
188                 stack_size = PTHREAD_STACK_MIN;
189 #endif
190
191 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
192         res = pthread_attr_setstacksize (&attr, stack_size);
193         g_assert (!res);
194 #endif
195
196         /*
197          * For policies that respect priorities set the prirority for the new thread
198          */ 
199         pthread_getschedparam(pthread_self(), &policy, &sp);
200         if ((policy == SCHED_FIFO) || (policy == SCHED_RR)) {
201                 sp.sched_priority = win32_priority_to_posix_priority (tp->priority, policy);
202                 res = pthread_attr_setschedparam (&attr, &sp);
203         }
204
205         memset (&start_info, 0, sizeof (StartInfo));
206         start_info.start_routine = (void *(*)(void *)) start_routine;
207         start_info.arg = arg;
208         start_info.flags = tp->creation_flags;
209         start_info.priority = tp->priority;
210         mono_coop_sem_init (&(start_info.registered), 0);
211
212         /* Actually start the thread */
213         res = mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
214         if (res) {
215                 mono_coop_sem_destroy (&(start_info.registered));
216                 return NULL;
217         }
218
219         /* Wait until the thread register itself in various places */
220         res = mono_coop_sem_wait (&start_info.registered, MONO_SEM_FLAGS_NONE);
221         g_assert (res != -1);
222
223         mono_coop_sem_destroy (&(start_info.registered));
224
225         if (out_tid)
226                 *out_tid = thread;
227
228         return start_info.handle;
229 }
230
231 /*
232  * mono_threads_platform_resume_created:
233  *
234  *   Resume a newly created thread created using CREATE_SUSPENDED.
235  */
236 void
237 mono_threads_platform_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
238 {
239         mono_coop_sem_post (&info->create_suspended_sem);
240 }
241
242 gboolean
243 mono_threads_platform_yield (void)
244 {
245         return sched_yield () == 0;
246 }
247
248 void
249 mono_threads_platform_exit (int exit_code)
250 {
251 #if defined(__native_client__)
252         nacl_shutdown_gc_thread();
253 #endif
254
255         mono_thread_info_detach ();
256
257         pthread_exit (NULL);
258 }
259
260 void
261 mono_threads_platform_unregister (MonoThreadInfo *info)
262 {
263         mono_threads_platform_set_exited (info);
264 }
265
266 HANDLE
267 mono_threads_platform_open_handle (void)
268 {
269         MonoThreadInfo *info;
270
271         info = mono_thread_info_current ();
272         g_assert (info);
273         g_assert (info->handle);
274
275         mono_w32handle_ref (info->handle);
276
277         return info->handle;
278 }
279
280 int
281 mono_threads_get_max_stack_size (void)
282 {
283         struct rlimit lim;
284
285         /* If getrlimit fails, we don't enforce any limits. */
286         if (getrlimit (RLIMIT_STACK, &lim))
287                 return INT_MAX;
288         /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
289         if (lim.rlim_max > (rlim_t)INT_MAX)
290                 return INT_MAX;
291         return (int)lim.rlim_max;
292 }
293
294 HANDLE
295 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
296 {
297         mono_w32handle_ref (handle);
298
299         return handle;
300 }
301
302 int
303 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
304 {
305         THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
306 #ifdef USE_TKILL_ON_ANDROID
307         int result, old_errno = errno;
308         result = tkill (info->native_handle, signum);
309         if (result < 0) {
310                 result = errno;
311                 errno = old_errno;
312         }
313         return result;
314 #elif defined(__native_client__)
315         /* Workaround pthread_kill abort() in NaCl glibc. */
316         return 0;
317 #elif !defined(HAVE_PTHREAD_KILL)
318         g_error ("pthread_kill() is not supported by this platform");
319 #else
320         return pthread_kill (mono_thread_info_get_tid (info), signum);
321 #endif
322 }
323
324 MonoNativeThreadId
325 mono_native_thread_id_get (void)
326 {
327         return pthread_self ();
328 }
329
330 gboolean
331 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
332 {
333         return pthread_equal (id1, id2);
334 }
335
336 /*
337  * mono_native_thread_create:
338  *
339  *   Low level thread creation function without any GC wrappers.
340  */
341 gboolean
342 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
343 {
344         return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
345 }
346
347 void
348 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
349 {
350 #ifdef __MACH__
351         /*
352          * We can't set the thread name for other threads, but we can at least make
353          * it work for threads that try to change their own name.
354          */
355         if (tid != mono_native_thread_id_get ())
356                 return;
357
358         if (!name) {
359                 pthread_setname_np ("");
360         } else {
361                 char n [63];
362
363                 strncpy (n, name, 63);
364                 n [62] = '\0';
365                 pthread_setname_np (n);
366         }
367 #elif defined (__NetBSD__)
368         if (!name) {
369                 pthread_setname_np (tid, "%s", (void*)"");
370         } else {
371                 char n [PTHREAD_MAX_NAMELEN_NP];
372
373                 strncpy (n, name, PTHREAD_MAX_NAMELEN_NP);
374                 n [PTHREAD_MAX_NAMELEN_NP - 1] = '\0';
375                 pthread_setname_np (tid, "%s", (void*)n);
376         }
377 #elif defined (HAVE_PTHREAD_SETNAME_NP)
378         if (!name) {
379                 pthread_setname_np (tid, "");
380         } else {
381                 char n [16];
382
383                 strncpy (n, name, 16);
384                 n [15] = '\0';
385                 pthread_setname_np (tid, n);
386         }
387 #endif
388 }
389
390 void
391 mono_threads_platform_set_exited (MonoThreadInfo *info)
392 {
393         MonoW32HandleThread *thread_data;
394         gpointer mutex_handle;
395         int i, thr_ret;
396         pid_t pid;
397         pthread_t tid;
398
399         g_assert (info->handle);
400
401         if (mono_w32handle_issignalled (info->handle) || mono_w32handle_get_type (info->handle) == MONO_W32HANDLE_UNUSED) {
402                 /* We must have already deliberately finished
403                  * with this thread, so don't do any more now */
404                 return;
405         }
406
407         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
408                 g_error ("unknown thread handle %p", info->handle);
409
410         pid = wapi_getpid ();
411         tid = pthread_self ();
412
413         for (i = 0; i < thread_data->owned_mutexes->len; i++) {
414                 mutex_handle = g_ptr_array_index (thread_data->owned_mutexes, i);
415                 wapi_mutex_abandon (mutex_handle, pid, tid);
416                 mono_thread_info_disown_mutex (info, mutex_handle);
417         }
418
419         g_ptr_array_free (thread_data->owned_mutexes, TRUE);
420
421         thr_ret = mono_w32handle_lock_handle (info->handle);
422         g_assert (thr_ret == 0);
423
424         mono_w32handle_set_signal_state (info->handle, TRUE, TRUE);
425
426         thr_ret = mono_w32handle_unlock_handle (info->handle);
427         g_assert (thr_ret == 0);
428
429         /* The thread is no longer active, so unref it */
430         mono_w32handle_unref (info->handle);
431
432         info->handle = NULL;
433 }
434
435 void
436 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
437 {
438         MonoW32HandleThread *thread_data;
439         int i;
440
441         g_assert (info->handle);
442
443         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
444                 g_error ("unknown thread handle %p", info->handle);
445
446         g_string_append_printf (text, "thread handle %p state : ", info->handle);
447
448         mono_thread_info_describe_interrupt_token (info, text);
449
450         g_string_append_printf (text, ", owns (");
451         for (i = 0; i < thread_data->owned_mutexes->len; i++)
452                 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread_data->owned_mutexes, i));
453         g_string_append_printf (text, ")");
454 }
455
456 void
457 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
458 {
459         MonoW32HandleThread *thread_data;
460
461         g_assert (info->handle);
462
463         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
464                 g_error ("unknown thread handle %p", info->handle);
465
466         mono_w32handle_ref (mutex_handle);
467
468         g_ptr_array_add (thread_data->owned_mutexes, mutex_handle);
469 }
470
471 void
472 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
473 {
474         MonoW32HandleThread *thread_data;
475
476         g_assert (info->handle);
477
478         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
479                 g_error ("unknown thread handle %p", info->handle);
480
481         mono_w32handle_unref (mutex_handle);
482
483         g_ptr_array_remove (thread_data->owned_mutexes, mutex_handle);
484 }
485
486 MonoThreadPriority
487 mono_threads_platform_get_priority (MonoThreadInfo *info)
488 {
489         MonoW32HandleThread *thread_data;
490
491         g_assert (info->handle);
492
493         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer *)&thread_data))
494                 return MONO_THREAD_PRIORITY_NORMAL;
495
496         return thread_data->priority;
497 }
498
499 gboolean
500 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
501 {
502         MonoW32HandleThread *thread_data;
503         int policy, posix_priority;
504         struct sched_param param;
505
506         g_assert (info->handle);
507
508         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
509                 return FALSE;
510
511         switch (pthread_getschedparam (thread_data->id, &policy, &param)) {
512         case 0:
513                 break;
514         case ESRCH:
515                 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_data->id);
516                 return FALSE;
517         default:
518                 return FALSE;
519         }
520
521         posix_priority =  win32_priority_to_posix_priority (priority, policy);
522         if (posix_priority < 0)
523                 return FALSE;
524
525         param.sched_priority = posix_priority;
526         switch (pthread_setschedparam (thread_data->id, policy, &param)) {
527         case 0:
528                 break;
529         case ESRCH:
530                 g_warning ("%s: pthread_setschedprio: error looking up thread id %x", __func__, (gsize)thread_data->id);
531                 return FALSE;
532         case ENOTSUP:
533                 g_warning ("%s: priority %d not supported", __func__, priority);
534                 return FALSE;
535         case EPERM:
536                 g_warning ("%s: permission denied", __func__);
537                 return FALSE;
538         default:
539                 return FALSE;
540         }
541
542         thread_data->priority = priority;
543         return TRUE;
544
545 }
546
547 static void thread_details (gpointer data)
548 {
549         MonoW32HandleThread *thread = (MonoW32HandleThread*) data;
550         g_print ("id: %p, owned_mutexes: %d, priority: %d",
551                 thread->id, thread->owned_mutexes->len, thread->priority);
552 }
553
554 static const gchar* thread_typename (void)
555 {
556         return "Thread";
557 }
558
559 static gsize thread_typesize (void)
560 {
561         return sizeof (MonoW32HandleThread);
562 }
563
564 static MonoW32HandleOps thread_ops = {
565         NULL,                           /* close */
566         NULL,                           /* signal */
567         NULL,                           /* own */
568         NULL,                           /* is_owned */
569         NULL,                           /* special_wait */
570         NULL,                           /* prewait */
571         thread_details,         /* details */
572         thread_typename,        /* typename */
573         thread_typesize,        /* typesize */
574 };
575
576 void
577 mono_threads_platform_init (void)
578 {
579         mono_w32handle_register_ops (MONO_W32HANDLE_THREAD, &thread_ops);
580
581         mono_w32handle_register_capabilities (MONO_W32HANDLE_THREAD, MONO_W32HANDLE_CAP_WAIT);
582 }
583
584 #endif /* defined(_POSIX_VERSION) || defined(__native_client__) */
585
586 #if defined(USE_POSIX_BACKEND)
587
588 gboolean
589 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
590 {
591         int sig = interrupt_kernel ? mono_threads_posix_get_abort_signal () :  mono_threads_posix_get_suspend_signal ();
592
593         if (!mono_threads_pthread_kill (info, sig)) {
594                 mono_threads_add_to_pending_operation_set (info);
595                 return TRUE;
596         }
597         return FALSE;
598 }
599
600 gboolean
601 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
602 {
603         return info->suspend_can_continue;
604 }
605
606 /*
607 This begins async resume. This function must do the following:
608
609 - Install an async target if one was requested.
610 - Notify the target to resume.
611 */
612 gboolean
613 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
614 {
615         mono_threads_add_to_pending_operation_set (info);
616         return mono_threads_pthread_kill (info, mono_threads_posix_get_restart_signal ()) == 0;
617 }
618
619 void
620 mono_threads_suspend_register (MonoThreadInfo *info)
621 {
622 #if defined (PLATFORM_ANDROID)
623         info->native_handle = gettid ();
624 #endif
625 }
626
627 void
628 mono_threads_suspend_free (MonoThreadInfo *info)
629 {
630 }
631
632 void
633 mono_threads_suspend_init (void)
634 {
635         mono_threads_posix_init_signals (MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART);
636 }
637
638 #endif /* defined(USE_POSIX_BACKEND) */