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 = 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 info->joined_stw = suspend;
229 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
232 if (info->gc_disabled)
234 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
237 g_assert (!info->doing_handshake);
238 info->doing_handshake = TRUE;
240 g_assert (info->doing_handshake);
241 info->doing_handshake = FALSE;
243 result = pthread_kill (mono_thread_info_get_tid (info), signum);
249 } END_FOREACH_THREAD_SAFE
251 sgen_wait_for_suspend_ack (count);
259 struct sigaction sinfo;
261 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
262 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
264 sigfillset (&sinfo.sa_mask);
265 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
266 sinfo.sa_sigaction = suspend_handler;
267 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
268 g_error ("failed sigaction");
271 sinfo.sa_handler = restart_handler;
272 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
273 g_error ("failed sigaction");
276 sigfillset (&suspend_signal_mask);
277 sigdelset (&suspend_signal_mask, restart_signal_num);
279 sigemptyset (&suspend_ack_signal_mask);
280 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
285 mono_gc_get_suspend_signal (void)
287 return suspend_signal_num;