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 = mono_sgen_global_stop_count;
72 /* duplicate signal */
73 if (0 && info->stop_count == stop_count)
76 mono_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 mono_sgen_resume_thread (SgenThreadInfo *info)
184 return mono_threads_pthread_kill (info, restart_signal_num) == 0;
188 mono_sgen_suspend_thread (SgenThreadInfo *info)
190 return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
194 mono_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 ("sem_wait ()");
208 mono_sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
210 if (!p->doing_handshake)
213 suspend_thread (p, NULL);
218 mono_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 (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 mono_sgen_wait_for_suspend_ack (count);
256 mono_sgen_os_init (void)
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;