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