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