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)
63 gpointer regs [ARCH_NUM_REGS];
67 g_assert (info->doing_handshake);
69 info->stopped_domain = mono_domain_get ();
70 info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
71 stop_count = sgen_global_stop_count;
72 /* duplicate signal */
73 if (0 && info->stop_count == stop_count)
76 sgen_fill_thread_info_for_suspend (info);
78 stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
79 /* If stack_start is not within the limits, then don't set it
80 in info and we will be restarted. */
81 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
82 info->stack_start = stack_start;
86 mono_sigctx_to_monoctx (context, &monoctx);
87 info->monoctx = &monoctx;
93 ARCH_COPY_SIGCTX_REGS (regs, context);
94 info->stopped_regs = regs;
96 info->stopped_regs = NULL;
100 g_assert (!info->stack_start);
104 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
105 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context);
107 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
110 Block the restart signal.
111 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
112 which might miss the signal and get stuck.
114 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
116 /* notify the waiting thread */
117 MONO_SEM_POST (suspend_ack_semaphore_ptr);
118 info->stop_count = stop_count;
120 /* wait until we receive the restart signal */
123 sigsuspend (&suspend_signal_mask);
124 } while (info->signal != restart_signal_num && info->doing_handshake);
126 /* Unblock the restart signal. */
127 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
129 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
130 /* notify the waiting thread */
131 MONO_SEM_POST (suspend_ack_semaphore_ptr);
134 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
136 suspend_handler (int sig, siginfo_t *siginfo, void *context)
138 SgenThreadInfo *info;
139 int old_errno = errno;
141 info = mono_thread_info_current ();
144 suspend_thread (info, context);
146 /* This can happen while a thread is dying */
147 //g_print ("no thread info in suspend\n");
154 restart_handler (int sig)
156 SgenThreadInfo *info;
157 int old_errno = errno;
159 info = mono_thread_info_current ();
161 If the thread info is null is means we're currently in the process of cleaning up,
162 the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
164 This means this thread has been suspended, TLS is dead, so the only option we have is to
165 rely on pthread_self () and seatch over the thread list.
168 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
171 * If a thread is dying there might be no thread info. In
172 * that case we rely on info->doing_handshake.
175 info->signal = restart_signal_num;
176 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
182 sgen_resume_thread (SgenThreadInfo *info)
184 return mono_threads_pthread_kill (info, restart_signal_num) == 0;
188 sgen_suspend_thread (SgenThreadInfo *info)
190 return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
194 sgen_wait_for_suspend_ack (int count)
198 for (i = 0; i < count; ++i) {
199 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
200 if (errno != EINTR) {
201 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
208 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
210 if (!p->doing_handshake)
213 suspend_thread (p, NULL);
218 sgen_thread_handshake (BOOL suspend)
221 SgenThreadInfo *info;
222 int signum = suspend ? suspend_signal_num : restart_signal_num;
224 MonoNativeThreadId me = mono_native_thread_id_get ();
227 FOREACH_THREAD_SAFE (info) {
228 if (info->joined_stw == suspend)
230 info->joined_stw = suspend;
231 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
234 if (info->gc_disabled)
236 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
239 g_assert (!info->doing_handshake);
240 info->doing_handshake = TRUE;
242 g_assert (info->doing_handshake);
243 info->doing_handshake = FALSE;
245 result = pthread_kill (mono_thread_info_get_tid (info), signum);
251 } END_FOREACH_THREAD_SAFE
253 sgen_wait_for_suspend_ack (count);
261 struct sigaction sinfo;
263 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
264 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
266 sigfillset (&sinfo.sa_mask);
267 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
268 sinfo.sa_sigaction = suspend_handler;
269 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
270 g_error ("failed sigaction");
273 sinfo.sa_handler = restart_handler;
274 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
275 g_error ("failed sigaction");
278 sigfillset (&suspend_signal_mask);
279 sigdelset (&suspend_signal_mask, restart_signal_num);
281 sigemptyset (&suspend_ack_signal_mask);
282 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
287 mono_gc_get_suspend_signal (void)
289 return suspend_signal_num;