Wrap always_inline and noinline attributes in compiler checks and use MSVC equivalent.
[mono.git] / mono / metadata / sgen-os-posix.c
1 /*
2  * sgen-os-posix.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *      Mark Probst (mprobst@novell.com)
7  *      Geoff Norton (gnorton@novell.com)
8  *
9  * Copyright 2010 Novell, Inc (http://www.novell.com)
10  *
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:
18  * 
19  * The above copyright notice and this permission notice shall be
20  * included in all copies or substantial portions of the Software.
21  * 
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.
29  */
30
31 #include "config.h"
32
33 #ifdef HAVE_SGEN_GC
34 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
35
36 #include <errno.h>
37 #include <glib.h>
38 #include "metadata/sgen-gc.h"
39 #include "metadata/gc-internal.h"
40 #include "metadata/sgen-archdep.h"
41 #include "metadata/object-internals.h"
42
43 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
44 const static int suspend_signal_num = SIGXFSZ;
45 #else
46 const static int suspend_signal_num = SIGPWR;
47 #endif
48 const static int restart_signal_num = SIGXCPU;
49
50 static MonoSemType suspend_ack_semaphore;
51 static MonoSemType *suspend_ack_semaphore_ptr;
52
53 static sigset_t suspend_signal_mask;
54 static sigset_t suspend_ack_signal_mask;
55
56 static void
57 suspend_thread (SgenThreadInfo *info, void *context)
58 {
59         int stop_count;
60 #ifdef USE_MONO_CTX
61         MonoContext monoctx;
62 #else
63         gpointer regs [ARCH_NUM_REGS];
64 #endif
65         gpointer stack_start;
66
67         g_assert (info->doing_handshake);
68
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)
74                 return;
75
76         sgen_fill_thread_info_for_suspend (info);
77
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;
83
84 #ifdef USE_MONO_CTX
85                 if (context) {
86                         mono_sigctx_to_monoctx (context, &monoctx);
87                         info->monoctx = &monoctx;
88                 } else {
89                         info->monoctx = NULL;
90                 }
91 #else
92                 if (context) {
93                         ARCH_COPY_SIGCTX_REGS (regs, context);
94                         info->stopped_regs = regs;
95                 } else {
96                         info->stopped_regs = NULL;
97                 }
98 #endif
99         } else {
100                 g_assert (!info->stack_start);
101         }
102
103         /* Notify the JIT */
104         if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
105                 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context);
106
107         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
108
109         /*
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.
113         */
114         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
115
116         /* notify the waiting thread */
117         MONO_SEM_POST (suspend_ack_semaphore_ptr);
118         info->stop_count = stop_count;
119
120         /* wait until we receive the restart signal */
121         do {
122                 info->signal = 0;
123                 sigsuspend (&suspend_signal_mask);
124         } while (info->signal != restart_signal_num && info->doing_handshake);
125
126         /* Unblock the restart signal. */
127         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
128
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);
132 }
133
134 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
135 static void
136 suspend_handler (int sig, siginfo_t *siginfo, void *context)
137 {
138         SgenThreadInfo *info;
139         int old_errno = errno;
140
141         info = mono_thread_info_current ();
142
143         if (info) {
144                 suspend_thread (info, context);
145         } else {
146                 /* This can happen while a thread is dying */
147                 //g_print ("no thread info in suspend\n");
148         }
149
150         errno = old_errno;
151 }
152
153 static void
154 restart_handler (int sig)
155 {
156         SgenThreadInfo *info;
157         int old_errno = errno;
158
159         info = mono_thread_info_current ();
160         /*
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.
163         
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.
166         */
167         if (!info)
168                 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
169
170         /*
171          * If a thread is dying there might be no thread info.  In
172          * that case we rely on info->doing_handshake.
173          */
174         if (info) {
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 ()));
177         }
178         errno = old_errno;
179 }
180
181 gboolean
182 sgen_resume_thread (SgenThreadInfo *info)
183 {
184         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
185 }
186
187 gboolean
188 sgen_suspend_thread (SgenThreadInfo *info)
189 {
190         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
191 }
192
193 void
194 sgen_wait_for_suspend_ack (int count)
195 {
196         int i, result;
197
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));
202                         }
203                 }
204         }
205 }
206
207 gboolean
208 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
209 {
210     if (!p->doing_handshake)
211             return FALSE;
212
213     suspend_thread (p, NULL);
214     return TRUE;
215 }
216
217 int
218 sgen_thread_handshake (BOOL suspend)
219 {
220         int count, result;
221         SgenThreadInfo *info;
222         int signum = suspend ? suspend_signal_num : restart_signal_num;
223
224         MonoNativeThreadId me = mono_native_thread_id_get ();
225
226         count = 0;
227         FOREACH_THREAD_SAFE (info) {
228                 if (info->joined_stw == suspend)
229                         continue;
230                 info->joined_stw = suspend;
231                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
232                         continue;
233                 }
234                 if (info->gc_disabled)
235                         continue;
236                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
237                         continue;*/
238                 if (suspend) {
239                         g_assert (!info->doing_handshake);
240                         info->doing_handshake = TRUE;
241                 } else {
242                         g_assert (info->doing_handshake);
243                         info->doing_handshake = FALSE;
244                 }
245                 result = pthread_kill (mono_thread_info_get_tid (info), signum);
246                 if (result == 0) {
247                         count++;
248                 } else {
249                         info->skip = 1;
250                 }
251         } END_FOREACH_THREAD_SAFE
252
253         sgen_wait_for_suspend_ack (count);
254
255         return count;
256 }
257
258 void
259 sgen_os_init (void)
260 {
261         struct sigaction sinfo;
262
263         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
264         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
265
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");
271         }
272
273         sinfo.sa_handler = restart_handler;
274         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
275                 g_error ("failed sigaction");
276         }
277
278         sigfillset (&suspend_signal_mask);
279         sigdelset (&suspend_signal_mask, restart_signal_num);
280
281         sigemptyset (&suspend_ack_signal_mask);
282         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
283         
284 }
285
286 int
287 mono_gc_get_suspend_signal (void)
288 {
289         return suspend_signal_num;
290 }
291 #endif
292 #endif