2 * sgen-os-posix.c: Posix support.
5 * Paolo Molaro (lupus@ximian.com)
6 * Mark Probst (mprobst@novell.com)
7 * Geoff Norton (gnorton@novell.com)
9 * Copyright 2010 Novell, Inc (http://www.novell.com)
10 * Copyright (C) 2012 Xamarin Inc
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License 2.0 as published by the Free Software Foundation;
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License 2.0 along with this library; if not, write to the Free
23 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
33 #include "metadata/sgen-gc.h"
34 #include "metadata/gc-internal.h"
35 #include "metadata/sgen-archdep.h"
36 #include "metadata/object-internals.h"
38 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
39 const static int suspend_signal_num = SIGXFSZ;
41 const static int suspend_signal_num = SIGPWR;
43 const static int restart_signal_num = SIGXCPU;
45 static MonoSemType suspend_ack_semaphore;
46 static MonoSemType *suspend_ack_semaphore_ptr;
48 static sigset_t suspend_signal_mask;
49 static sigset_t suspend_ack_signal_mask;
52 suspend_thread (SgenThreadInfo *info, void *context)
56 gpointer regs [ARCH_NUM_REGS];
60 g_assert (info->doing_handshake);
62 info->stopped_domain = mono_domain_get ();
63 info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
64 stop_count = sgen_global_stop_count;
65 /* duplicate signal */
66 if (0 && info->stop_count == stop_count)
69 stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
70 /* If stack_start is not within the limits, then don't set it
71 in info and we will be restarted. */
72 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
73 info->stack_start = stack_start;
77 mono_sigctx_to_monoctx (context, &info->ctx);
79 memset (&info->ctx, 0, sizeof (MonoContext));
83 ARCH_COPY_SIGCTX_REGS (regs, context);
84 memcpy (&info->regs, regs, sizeof (info->regs));
86 memset (&info->regs, 0, sizeof (info->regs));
90 g_assert (!info->stack_start);
94 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
95 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context, NULL);
97 SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer)mono_native_thread_id_get ());
100 Block the restart signal.
101 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
102 which might miss the signal and get stuck.
104 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
106 /* notify the waiting thread */
107 MONO_SEM_POST (suspend_ack_semaphore_ptr);
108 info->stop_count = stop_count;
110 /* wait until we receive the restart signal */
113 sigsuspend (&suspend_signal_mask);
114 } while (info->signal != restart_signal_num && info->doing_handshake);
116 /* Unblock the restart signal. */
117 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
119 SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ());
120 /* notify the waiting thread */
121 MONO_SEM_POST (suspend_ack_semaphore_ptr);
124 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
126 suspend_handler (int sig, siginfo_t *siginfo, void *context)
128 SgenThreadInfo *info;
129 int old_errno = errno;
131 info = mono_thread_info_current ();
134 suspend_thread (info, context);
136 /* This can happen while a thread is dying */
137 //g_print ("no thread info in suspend\n");
144 restart_handler (int sig)
146 SgenThreadInfo *info;
147 int old_errno = errno;
149 info = mono_thread_info_current ();
151 If the thread info is null is means we're currently in the process of cleaning up,
152 the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
154 This means this thread has been suspended, TLS is dead, so the only option we have is to
155 rely on pthread_self () and seatch over the thread list.
158 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
161 * If a thread is dying there might be no thread info. In
162 * that case we rely on info->doing_handshake.
165 info->signal = restart_signal_num;
166 SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
172 sgen_resume_thread (SgenThreadInfo *info)
174 return mono_threads_pthread_kill (info, restart_signal_num) == 0;
178 sgen_suspend_thread (SgenThreadInfo *info)
180 return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
184 sgen_wait_for_suspend_ack (int count)
188 for (i = 0; i < count; ++i) {
189 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
190 if (errno != EINTR) {
191 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
198 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
200 if (!p->doing_handshake)
203 suspend_thread (p, NULL);
208 sgen_thread_handshake (BOOL suspend)
211 SgenThreadInfo *info;
212 int signum = suspend ? suspend_signal_num : restart_signal_num;
214 MonoNativeThreadId me = mono_native_thread_id_get ();
217 FOREACH_THREAD_SAFE (info) {
218 if (info->joined_stw == suspend)
220 info->joined_stw = suspend;
221 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
224 if (info->gc_disabled)
226 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
229 g_assert (!info->doing_handshake);
230 info->doing_handshake = TRUE;
232 g_assert (info->doing_handshake);
233 info->doing_handshake = FALSE;
235 result = pthread_kill (mono_thread_info_get_tid (info), signum);
241 } END_FOREACH_THREAD_SAFE
243 sgen_wait_for_suspend_ack (count);
251 struct sigaction sinfo;
253 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
254 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
256 sigfillset (&sinfo.sa_mask);
257 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
258 sinfo.sa_sigaction = suspend_handler;
259 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
260 g_error ("failed sigaction");
263 sinfo.sa_handler = restart_handler;
264 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
265 g_error ("failed sigaction");
268 sigfillset (&suspend_signal_mask);
269 sigdelset (&suspend_signal_mask, restart_signal_num);
271 sigemptyset (&suspend_ack_signal_mask);
272 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
277 mono_gc_get_suspend_signal (void)
279 return suspend_signal_num;