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