Merge pull request #3199 from lambdageek/dev/proxy-setter
[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
22 #include <errno.h>
23
24 #if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
25 #define USE_TKILL_ON_ANDROID 1
26 #endif
27
28 #ifdef USE_TKILL_ON_ANDROID
29 extern int tkill (pid_t tid, int signal);
30 #endif
31
32 #if defined(_POSIX_VERSION) || defined(__native_client__)
33
34 #include <sys/resource.h>
35
36 #if defined(__native_client__)
37 void nacl_shutdown_gc_thread(void);
38 #endif
39
40 typedef struct {
41         void *(*start_routine)(void*);
42         void *arg;
43         int flags;
44         gint32 priority;
45         MonoCoopSem registered;
46         HANDLE handle;
47 } StartInfo;
48
49 static void*
50 inner_start_thread (void *arg)
51 {
52         StartInfo *start_info = (StartInfo *) arg;
53         void *t_arg = start_info->arg;
54         int res;
55         void *(*start_func)(void*) = start_info->start_routine;
56         guint32 flags = start_info->flags;
57         void *result;
58         HANDLE handle;
59         MonoThreadInfo *info;
60
61         /* Register the thread with the io-layer */
62         handle = wapi_create_thread_handle ();
63         if (!handle) {
64                 mono_coop_sem_post (&(start_info->registered));
65                 return NULL;
66         }
67         start_info->handle = handle;
68
69         info = mono_thread_info_attach (&result);
70
71         info->runtime_thread = TRUE;
72         info->handle = handle;
73
74         wapi_init_thread_info_priority(handle, start_info->priority);
75
76         if (flags & CREATE_SUSPENDED) {
77                 info->create_suspended = TRUE;
78                 mono_coop_sem_init (&info->create_suspended_sem, 0);
79         }
80
81         /* start_info is not valid after this */
82         mono_coop_sem_post (&(start_info->registered));
83         start_info = NULL;
84
85         if (flags & CREATE_SUSPENDED) {
86                 res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
87                 g_assert (res != -1);
88
89                 mono_coop_sem_destroy (&info->create_suspended_sem);
90         }
91
92         /* Run the actual main function of the thread */
93         result = start_func (t_arg);
94
95         mono_threads_core_exit (GPOINTER_TO_UINT (result));
96         g_assert_not_reached ();
97 }
98
99 HANDLE
100 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
101 {
102         pthread_attr_t attr;
103         int res;
104         pthread_t thread;
105         StartInfo start_info;
106         guint32 stack_size;
107         int policy;
108         struct sched_param sp;
109
110         res = pthread_attr_init (&attr);
111         g_assert (!res);
112
113         if (tp->stack_size == 0) {
114 #if HAVE_VALGRIND_MEMCHECK_H
115                 if (RUNNING_ON_VALGRIND)
116                         stack_size = 1 << 20;
117                 else
118                         stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
119 #else
120                 stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
121 #endif
122         } else
123                 stack_size = tp->stack_size;
124
125 #ifdef PTHREAD_STACK_MIN
126         if (stack_size < PTHREAD_STACK_MIN)
127                 stack_size = PTHREAD_STACK_MIN;
128 #endif
129
130 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
131         res = pthread_attr_setstacksize (&attr, stack_size);
132         g_assert (!res);
133 #endif
134
135         /*
136          * For policies that respect priorities set the prirority for the new thread
137          */ 
138         pthread_getschedparam(pthread_self(), &policy, &sp);
139         if ((policy == SCHED_FIFO) || (policy == SCHED_RR)) {
140                 sp.sched_priority = wapi_thread_priority_to_posix_priority (tp->priority, policy);
141                 res = pthread_attr_setschedparam (&attr, &sp);
142         }
143
144         memset (&start_info, 0, sizeof (StartInfo));
145         start_info.start_routine = (void *(*)(void *)) start_routine;
146         start_info.arg = arg;
147         start_info.flags = tp->creation_flags;
148         start_info.priority = tp->priority;
149         mono_coop_sem_init (&(start_info.registered), 0);
150
151         /* Actually start the thread */
152         res = mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
153         if (res) {
154                 mono_coop_sem_destroy (&(start_info.registered));
155                 return NULL;
156         }
157
158         /* Wait until the thread register itself in various places */
159         res = mono_coop_sem_wait (&start_info.registered, MONO_SEM_FLAGS_NONE);
160         g_assert (res != -1);
161
162         mono_coop_sem_destroy (&(start_info.registered));
163
164         if (out_tid)
165                 *out_tid = thread;
166
167         return start_info.handle;
168 }
169
170 /*
171  * mono_threads_core_resume_created:
172  *
173  *   Resume a newly created thread created using CREATE_SUSPENDED.
174  */
175 void
176 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
177 {
178         mono_coop_sem_post (&info->create_suspended_sem);
179 }
180
181 gboolean
182 mono_threads_core_yield (void)
183 {
184         return sched_yield () == 0;
185 }
186
187 void
188 mono_threads_core_exit (int exit_code)
189 {
190         MonoThreadInfo *current = mono_thread_info_current ();
191
192 #if defined(__native_client__)
193         nacl_shutdown_gc_thread();
194 #endif
195
196         wapi_thread_handle_set_exited (current->handle, exit_code);
197
198         mono_thread_info_detach ();
199
200         pthread_exit (NULL);
201 }
202
203 void
204 mono_threads_core_unregister (MonoThreadInfo *info)
205 {
206         if (info->handle) {
207                 wapi_thread_handle_set_exited (info->handle, 0);
208                 info->handle = NULL;
209         }
210 }
211
212 HANDLE
213 mono_threads_core_open_handle (void)
214 {
215         MonoThreadInfo *info;
216
217         info = mono_thread_info_current ();
218         g_assert (info);
219
220         if (!info->handle)
221                 info->handle = wapi_create_thread_handle ();
222         else
223                 wapi_ref_thread_handle (info->handle);
224         return info->handle;
225 }
226
227 int
228 mono_threads_get_max_stack_size (void)
229 {
230         struct rlimit lim;
231
232         /* If getrlimit fails, we don't enforce any limits. */
233         if (getrlimit (RLIMIT_STACK, &lim))
234                 return INT_MAX;
235         /* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
236         if (lim.rlim_max > (rlim_t)INT_MAX)
237                 return INT_MAX;
238         return (int)lim.rlim_max;
239 }
240
241 HANDLE
242 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
243 {
244         wapi_ref_thread_handle (handle);
245
246         return handle;
247 }
248
249 int
250 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
251 {
252         THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
253 #ifdef USE_TKILL_ON_ANDROID
254         int result, old_errno = errno;
255         result = tkill (info->native_handle, signum);
256         if (result < 0) {
257                 result = errno;
258                 errno = old_errno;
259         }
260         return result;
261 #elif defined(__native_client__)
262         /* Workaround pthread_kill abort() in NaCl glibc. */
263         return 0;
264 #elif !defined(HAVE_PTHREAD_KILL)
265         g_error ("pthread_kill() is not supported by this platform");
266 #else
267         return pthread_kill (mono_thread_info_get_tid (info), signum);
268 #endif
269 }
270
271 MonoNativeThreadId
272 mono_native_thread_id_get (void)
273 {
274         return pthread_self ();
275 }
276
277 gboolean
278 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
279 {
280         return pthread_equal (id1, id2);
281 }
282
283 /*
284  * mono_native_thread_create:
285  *
286  *   Low level thread creation function without any GC wrappers.
287  */
288 gboolean
289 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
290 {
291         return pthread_create (tid, NULL, (void *(*)(void *)) func, arg) == 0;
292 }
293
294 void
295 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
296 {
297 #ifdef __MACH__
298         /*
299          * We can't set the thread name for other threads, but we can at least make
300          * it work for threads that try to change their own name.
301          */
302         if (tid != mono_native_thread_id_get ())
303                 return;
304
305         if (!name) {
306                 pthread_setname_np ("");
307         } else {
308                 char n [63];
309
310                 strncpy (n, name, 63);
311                 n [62] = '\0';
312                 pthread_setname_np (n);
313         }
314 #elif defined (__NetBSD__)
315         if (!name) {
316                 pthread_setname_np (tid, "%s", (void*)"");
317         } else {
318                 char n [PTHREAD_MAX_NAMELEN_NP];
319
320                 strncpy (n, name, PTHREAD_MAX_NAMELEN_NP);
321                 n [PTHREAD_MAX_NAMELEN_NP - 1] = '\0';
322                 pthread_setname_np (tid, "%s", (void*)n);
323         }
324 #elif defined (HAVE_PTHREAD_SETNAME_NP)
325         if (!name) {
326                 pthread_setname_np (tid, "");
327         } else {
328                 char n [16];
329
330                 strncpy (n, name, 16);
331                 n [15] = '\0';
332                 pthread_setname_np (tid, n);
333         }
334 #endif
335 }
336
337 #endif /* defined(_POSIX_VERSION) || defined(__native_client__) */
338
339 #if defined(USE_POSIX_BACKEND)
340
341 gboolean
342 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
343 {
344         int sig = interrupt_kernel ? mono_threads_posix_get_abort_signal () :  mono_threads_posix_get_suspend_signal ();
345
346         if (!mono_threads_pthread_kill (info, sig)) {
347                 mono_threads_add_to_pending_operation_set (info);
348                 return TRUE;
349         }
350         return FALSE;
351 }
352
353 gboolean
354 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
355 {
356         return info->suspend_can_continue;
357 }
358
359 /*
360 This begins async resume. This function must do the following:
361
362 - Install an async target if one was requested.
363 - Notify the target to resume.
364 */
365 gboolean
366 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
367 {
368         mono_threads_add_to_pending_operation_set (info);
369         return mono_threads_pthread_kill (info, mono_threads_posix_get_restart_signal ()) == 0;
370 }
371
372 void
373 mono_threads_platform_register (MonoThreadInfo *info)
374 {
375 #if defined (PLATFORM_ANDROID)
376         info->native_handle = gettid ();
377 #endif
378 }
379
380 void
381 mono_threads_platform_free (MonoThreadInfo *info)
382 {
383 }
384
385 void
386 mono_threads_init_platform (void)
387 {
388         mono_threads_posix_init_signals (MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART);
389 }
390
391 #endif /* defined(USE_POSIX_BACKEND) */