2 * sgen-os-posix.c: Simple generational GC.
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)
11 * Permission is hereby granted, free of charge, to any person obtaining
12 * a copy of this software and associated documentation files (the
13 * "Software"), to deal in the Software without restriction, including
14 * without limitation the rights to use, copy, modify, merge, publish,
15 * distribute, sublicense, and/or sell copies of the Software, and to
16 * permit persons to whom the Software is furnished to do so, subject to
17 * the following conditions:
19 * The above copyright notice and this permission notice shall be
20 * included in all copies or substantial portions of the Software.
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
38 #include "metadata/sgen-gc.h"
39 #include "metadata/gc-internal.h"
40 #include "metadata/sgen-archdep.h"
41 #include "metadata/object-internals.h"
43 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
44 const static int suspend_signal_num = SIGXFSZ;
46 const static int suspend_signal_num = SIGPWR;
48 const static int restart_signal_num = SIGXCPU;
50 static MonoSemType suspend_ack_semaphore;
51 static MonoSemType *suspend_ack_semaphore_ptr;
53 static sigset_t suspend_signal_mask;
54 static sigset_t suspend_ack_signal_mask;
57 suspend_thread (SgenThreadInfo *info, void *context)
61 gpointer regs [ARCH_NUM_REGS];
65 g_assert (info->doing_handshake);
67 info->stopped_domain = mono_domain_get ();
68 info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
69 stop_count = sgen_global_stop_count;
70 /* duplicate signal */
71 if (0 && info->stop_count == stop_count)
74 sgen_fill_thread_info_for_suspend (info);
76 stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
77 /* If stack_start is not within the limits, then don't set it
78 in info and we will be restarted. */
79 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
80 info->stack_start = stack_start;
84 mono_sigctx_to_monoctx (context, &info->ctx);
86 memset (&info->ctx, 0, sizeof (MonoContext));
90 ARCH_COPY_SIGCTX_REGS (regs, context);
91 memcpy (&info->regs, regs, sizeof (info->regs));
93 memset (&info->regs, 0, sizeof (info->regs));
97 g_assert (!info->stack_start);
101 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
102 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context, NULL);
104 SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer)mono_native_thread_id_get ());
107 Block the restart signal.
108 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
109 which might miss the signal and get stuck.
111 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
113 /* notify the waiting thread */
114 MONO_SEM_POST (suspend_ack_semaphore_ptr);
115 info->stop_count = stop_count;
117 /* wait until we receive the restart signal */
120 sigsuspend (&suspend_signal_mask);
121 } while (info->signal != restart_signal_num && info->doing_handshake);
123 /* Unblock the restart signal. */
124 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
126 SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ());
127 /* notify the waiting thread */
128 MONO_SEM_POST (suspend_ack_semaphore_ptr);
131 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
133 suspend_handler (int sig, siginfo_t *siginfo, void *context)
135 SgenThreadInfo *info;
136 int old_errno = errno;
138 info = mono_thread_info_current ();
141 suspend_thread (info, context);
143 /* This can happen while a thread is dying */
144 //g_print ("no thread info in suspend\n");
151 restart_handler (int sig)
153 SgenThreadInfo *info;
154 int old_errno = errno;
156 info = mono_thread_info_current ();
158 If the thread info is null is means we're currently in the process of cleaning up,
159 the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
161 This means this thread has been suspended, TLS is dead, so the only option we have is to
162 rely on pthread_self () and seatch over the thread list.
165 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
168 * If a thread is dying there might be no thread info. In
169 * that case we rely on info->doing_handshake.
172 info->signal = restart_signal_num;
173 SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
179 sgen_resume_thread (SgenThreadInfo *info)
181 return mono_threads_pthread_kill (info, restart_signal_num) == 0;
185 sgen_suspend_thread (SgenThreadInfo *info)
187 return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
191 sgen_wait_for_suspend_ack (int count)
195 for (i = 0; i < count; ++i) {
196 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
197 if (errno != EINTR) {
198 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
205 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
207 if (!p->doing_handshake)
210 suspend_thread (p, NULL);
215 sgen_thread_handshake (BOOL suspend)
218 SgenThreadInfo *info;
219 int signum = suspend ? suspend_signal_num : restart_signal_num;
221 MonoNativeThreadId me = mono_native_thread_id_get ();
224 FOREACH_THREAD_SAFE (info) {
225 if (info->joined_stw == suspend)
227 info->joined_stw = suspend;
228 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
231 if (info->gc_disabled)
233 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
236 g_assert (!info->doing_handshake);
237 info->doing_handshake = TRUE;
239 g_assert (info->doing_handshake);
240 info->doing_handshake = FALSE;
242 result = pthread_kill (mono_thread_info_get_tid (info), signum);
248 } END_FOREACH_THREAD_SAFE
250 sgen_wait_for_suspend_ack (count);
258 struct sigaction sinfo;
260 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
261 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
263 sigfillset (&sinfo.sa_mask);
264 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
265 sinfo.sa_sigaction = suspend_handler;
266 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
267 g_error ("failed sigaction");
270 sinfo.sa_handler = restart_handler;
271 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
272 g_error ("failed sigaction");
275 sigfillset (&suspend_signal_mask);
276 sigdelset (&suspend_signal_mask, restart_signal_num);
278 sigemptyset (&suspend_ack_signal_mask);
279 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
284 mono_gc_get_suspend_signal (void)
286 return suspend_signal_num;