Merge pull request #3447 from ludovic-henry/iolayer-extract-threading
[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 int
267 mono_threads_get_max_stack_size (void)
268 {
269         struct rlimit lim;
270
271         /* If getrlimit fails, we don't enforce any limits. */
272         if (getrlimit (RLIMIT_STACK, &lim))
273                 return INT_MAX;
274         /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
275         if (lim.rlim_max > (rlim_t)INT_MAX)
276                 return INT_MAX;
277         return (int)lim.rlim_max;
278 }
279
280 HANDLE
281 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
282 {
283         mono_w32handle_ref (handle);
284
285         return handle;
286 }
287
288 int
289 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
290 {
291         THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
292 #ifdef USE_TKILL_ON_ANDROID
293         int result, old_errno = errno;
294         result = tkill (info->native_handle, signum);
295         if (result < 0) {
296                 result = errno;
297                 errno = old_errno;
298         }
299         return result;
300 #elif defined(__native_client__)
301         /* Workaround pthread_kill abort() in NaCl glibc. */
302         return 0;
303 #elif !defined(HAVE_PTHREAD_KILL)
304         g_error ("pthread_kill() is not supported by this platform");
305 #else
306         return pthread_kill (mono_thread_info_get_tid (info), signum);
307 #endif
308 }
309
310 MonoNativeThreadId
311 mono_native_thread_id_get (void)
312 {
313         return pthread_self ();
314 }
315
316 gboolean
317 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
318 {
319         return pthread_equal (id1, id2);
320 }
321
322 /*
323  * mono_native_thread_create:
324  *
325  *   Low level thread creation function without any GC wrappers.
326  */
327 gboolean
328 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
329 {
330         return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
331 }
332
333 void
334 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
335 {
336 #ifdef __MACH__
337         /*
338          * We can't set the thread name for other threads, but we can at least make
339          * it work for threads that try to change their own name.
340          */
341         if (tid != mono_native_thread_id_get ())
342                 return;
343
344         if (!name) {
345                 pthread_setname_np ("");
346         } else {
347                 char n [63];
348
349                 strncpy (n, name, 63);
350                 n [62] = '\0';
351                 pthread_setname_np (n);
352         }
353 #elif defined (__NetBSD__)
354         if (!name) {
355                 pthread_setname_np (tid, "%s", (void*)"");
356         } else {
357                 char n [PTHREAD_MAX_NAMELEN_NP];
358
359                 strncpy (n, name, PTHREAD_MAX_NAMELEN_NP);
360                 n [PTHREAD_MAX_NAMELEN_NP - 1] = '\0';
361                 pthread_setname_np (tid, "%s", (void*)n);
362         }
363 #elif defined (HAVE_PTHREAD_SETNAME_NP)
364         if (!name) {
365                 pthread_setname_np (tid, "");
366         } else {
367                 char n [16];
368
369                 strncpy (n, name, 16);
370                 n [15] = '\0';
371                 pthread_setname_np (tid, n);
372         }
373 #endif
374 }
375
376 void
377 mono_threads_platform_set_exited (MonoThreadInfo *info)
378 {
379         MonoW32HandleThread *thread_data;
380         gpointer mutex_handle;
381         int i, thr_ret;
382         pid_t pid;
383         pthread_t tid;
384
385         g_assert (info->handle);
386
387         if (mono_w32handle_issignalled (info->handle) || mono_w32handle_get_type (info->handle) == MONO_W32HANDLE_UNUSED) {
388                 /* We must have already deliberately finished
389                  * with this thread, so don't do any more now */
390                 return;
391         }
392
393         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
394                 g_error ("unknown thread handle %p", info->handle);
395
396         pid = wapi_getpid ();
397         tid = pthread_self ();
398
399         for (i = 0; i < thread_data->owned_mutexes->len; i++) {
400                 mutex_handle = g_ptr_array_index (thread_data->owned_mutexes, i);
401                 wapi_mutex_abandon (mutex_handle, pid, tid);
402                 mono_thread_info_disown_mutex (info, mutex_handle);
403         }
404
405         g_ptr_array_free (thread_data->owned_mutexes, TRUE);
406
407         thr_ret = mono_w32handle_lock_handle (info->handle);
408         g_assert (thr_ret == 0);
409
410         mono_w32handle_set_signal_state (info->handle, TRUE, TRUE);
411
412         thr_ret = mono_w32handle_unlock_handle (info->handle);
413         g_assert (thr_ret == 0);
414
415         /* The thread is no longer active, so unref it */
416         mono_w32handle_unref (info->handle);
417
418         info->handle = NULL;
419 }
420
421 void
422 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
423 {
424         MonoW32HandleThread *thread_data;
425         int i;
426
427         g_assert (info->handle);
428
429         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
430                 g_error ("unknown thread handle %p", info->handle);
431
432         g_string_append_printf (text, "thread handle %p state : ", info->handle);
433
434         mono_thread_info_describe_interrupt_token (info, text);
435
436         g_string_append_printf (text, ", owns (");
437         for (i = 0; i < thread_data->owned_mutexes->len; i++)
438                 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread_data->owned_mutexes, i));
439         g_string_append_printf (text, ")");
440 }
441
442 void
443 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
444 {
445         MonoW32HandleThread *thread_data;
446
447         g_assert (info->handle);
448
449         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
450                 g_error ("unknown thread handle %p", info->handle);
451
452         mono_w32handle_ref (mutex_handle);
453
454         g_ptr_array_add (thread_data->owned_mutexes, mutex_handle);
455 }
456
457 void
458 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
459 {
460         MonoW32HandleThread *thread_data;
461
462         g_assert (info->handle);
463
464         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
465                 g_error ("unknown thread handle %p", info->handle);
466
467         mono_w32handle_unref (mutex_handle);
468
469         g_ptr_array_remove (thread_data->owned_mutexes, mutex_handle);
470 }
471
472 MonoThreadPriority
473 mono_threads_platform_get_priority (MonoThreadInfo *info)
474 {
475         MonoW32HandleThread *thread_data;
476
477         g_assert (info->handle);
478
479         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer *)&thread_data))
480                 return MONO_THREAD_PRIORITY_NORMAL;
481
482         return thread_data->priority;
483 }
484
485 gboolean
486 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
487 {
488         MonoW32HandleThread *thread_data;
489         int policy, posix_priority;
490         struct sched_param param;
491
492         g_assert (info->handle);
493
494         if (!mono_w32handle_lookup (info->handle, MONO_W32HANDLE_THREAD, (gpointer*) &thread_data))
495                 return FALSE;
496
497         switch (pthread_getschedparam (thread_data->id, &policy, &param)) {
498         case 0:
499                 break;
500         case ESRCH:
501                 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_data->id);
502                 return FALSE;
503         default:
504                 return FALSE;
505         }
506
507         posix_priority =  win32_priority_to_posix_priority (priority, policy);
508         if (posix_priority < 0)
509                 return FALSE;
510
511         param.sched_priority = posix_priority;
512         switch (pthread_setschedparam (thread_data->id, policy, &param)) {
513         case 0:
514                 break;
515         case ESRCH:
516                 g_warning ("%s: pthread_setschedprio: error looking up thread id %x", __func__, (gsize)thread_data->id);
517                 return FALSE;
518         case ENOTSUP:
519                 g_warning ("%s: priority %d not supported", __func__, priority);
520                 return FALSE;
521         case EPERM:
522                 g_warning ("%s: permission denied", __func__);
523                 return FALSE;
524         default:
525                 return FALSE;
526         }
527
528         thread_data->priority = priority;
529         return TRUE;
530
531 }
532
533 static void thread_details (gpointer data)
534 {
535         MonoW32HandleThread *thread = (MonoW32HandleThread*) data;
536         g_print ("id: %p, owned_mutexes: %d, priority: %d",
537                 thread->id, thread->owned_mutexes->len, thread->priority);
538 }
539
540 static const gchar* thread_typename (void)
541 {
542         return "Thread";
543 }
544
545 static gsize thread_typesize (void)
546 {
547         return sizeof (MonoW32HandleThread);
548 }
549
550 static MonoW32HandleOps thread_ops = {
551         NULL,                           /* close */
552         NULL,                           /* signal */
553         NULL,                           /* own */
554         NULL,                           /* is_owned */
555         NULL,                           /* special_wait */
556         NULL,                           /* prewait */
557         thread_details,         /* details */
558         thread_typename,        /* typename */
559         thread_typesize,        /* typesize */
560 };
561
562 void
563 mono_threads_platform_init (void)
564 {
565         mono_w32handle_register_ops (MONO_W32HANDLE_THREAD, &thread_ops);
566
567         mono_w32handle_register_capabilities (MONO_W32HANDLE_THREAD, MONO_W32HANDLE_CAP_WAIT);
568 }
569
570 #endif /* defined(_POSIX_VERSION) || defined(__native_client__) */
571
572 #if defined(USE_POSIX_BACKEND)
573
574 gboolean
575 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
576 {
577         int sig = interrupt_kernel ? mono_threads_posix_get_abort_signal () :  mono_threads_posix_get_suspend_signal ();
578
579         if (!mono_threads_pthread_kill (info, sig)) {
580                 mono_threads_add_to_pending_operation_set (info);
581                 return TRUE;
582         }
583         return FALSE;
584 }
585
586 gboolean
587 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
588 {
589         return info->suspend_can_continue;
590 }
591
592 /*
593 This begins async resume. This function must do the following:
594
595 - Install an async target if one was requested.
596 - Notify the target to resume.
597 */
598 gboolean
599 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
600 {
601         mono_threads_add_to_pending_operation_set (info);
602         return mono_threads_pthread_kill (info, mono_threads_posix_get_restart_signal ()) == 0;
603 }
604
605 void
606 mono_threads_suspend_register (MonoThreadInfo *info)
607 {
608 #if defined (PLATFORM_ANDROID)
609         info->native_handle = gettid ();
610 #endif
611 }
612
613 void
614 mono_threads_suspend_free (MonoThreadInfo *info)
615 {
616 }
617
618 void
619 mono_threads_suspend_init (void)
620 {
621         mono_threads_posix_init_signals (MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART);
622 }
623
624 #endif /* defined(USE_POSIX_BACKEND) */