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 sgen_fill_thread_info_for_suspend (info);
71 stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
72 /* If stack_start is not within the limits, then don't set it
73 in info and we will be restarted. */
74 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
75 info->stack_start = stack_start;
79 mono_sigctx_to_monoctx (context, &info->ctx);
81 memset (&info->ctx, 0, sizeof (MonoContext));
85 ARCH_COPY_SIGCTX_REGS (regs, context);
86 memcpy (&info->regs, regs, sizeof (info->regs));
88 memset (&info->regs, 0, sizeof (info->regs));
92 g_assert (!info->stack_start);
96 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
97 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context, NULL);
99 SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer)mono_native_thread_id_get ());
102 Block the restart signal.
103 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
104 which might miss the signal and get stuck.
106 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
108 /* notify the waiting thread */
109 MONO_SEM_POST (suspend_ack_semaphore_ptr);
110 info->stop_count = stop_count;
112 /* wait until we receive the restart signal */
115 sigsuspend (&suspend_signal_mask);
116 } while (info->signal != restart_signal_num && info->doing_handshake);
118 /* Unblock the restart signal. */
119 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
121 SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ());
122 /* notify the waiting thread */
123 MONO_SEM_POST (suspend_ack_semaphore_ptr);
126 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
128 suspend_handler (int sig, siginfo_t *siginfo, void *context)
130 SgenThreadInfo *info;
131 int old_errno = errno;
133 info = mono_thread_info_current ();
136 suspend_thread (info, context);
138 /* This can happen while a thread is dying */
139 //g_print ("no thread info in suspend\n");
146 restart_handler (int sig)
148 SgenThreadInfo *info;
149 int old_errno = errno;
151 info = mono_thread_info_current ();
153 If the thread info is null is means we're currently in the process of cleaning up,
154 the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
156 This means this thread has been suspended, TLS is dead, so the only option we have is to
157 rely on pthread_self () and seatch over the thread list.
160 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
163 * If a thread is dying there might be no thread info. In
164 * that case we rely on info->doing_handshake.
167 info->signal = restart_signal_num;
168 SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
174 sgen_resume_thread (SgenThreadInfo *info)
176 return mono_threads_pthread_kill (info, restart_signal_num) == 0;
180 sgen_suspend_thread (SgenThreadInfo *info)
182 return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
186 sgen_wait_for_suspend_ack (int count)
190 for (i = 0; i < count; ++i) {
191 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
192 if (errno != EINTR) {
193 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
200 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
202 if (!p->doing_handshake)
205 suspend_thread (p, NULL);
210 sgen_thread_handshake (BOOL suspend)
213 SgenThreadInfo *info;
214 int signum = suspend ? suspend_signal_num : restart_signal_num;
216 MonoNativeThreadId me = mono_native_thread_id_get ();
219 FOREACH_THREAD_SAFE (info) {
220 if (info->joined_stw == suspend)
222 info->joined_stw = suspend;
223 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
226 if (info->gc_disabled)
228 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
231 g_assert (!info->doing_handshake);
232 info->doing_handshake = TRUE;
234 g_assert (info->doing_handshake);
235 info->doing_handshake = FALSE;
237 result = pthread_kill (mono_thread_info_get_tid (info), signum);
243 } END_FOREACH_THREAD_SAFE
245 sgen_wait_for_suspend_ack (count);
253 struct sigaction sinfo;
255 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
256 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
258 sigfillset (&sinfo.sa_mask);
259 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
260 sinfo.sa_sigaction = suspend_handler;
261 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
262 g_error ("failed sigaction");
265 sinfo.sa_handler = restart_handler;
266 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
267 g_error ("failed sigaction");
270 sigfillset (&suspend_signal_mask);
271 sigdelset (&suspend_signal_mask, restart_signal_num);
273 sigemptyset (&suspend_ack_signal_mask);
274 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
279 mono_gc_get_suspend_signal (void)
281 return suspend_signal_num;