merged volatile memory barriers
[cacao.git] / src / vm / signal.cpp
1 /* src/vm/signal.c - machine independent signal functions
2
3    Copyright (C) 1996-2005, 2006, 2007, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <assert.h>
29 #include <signal.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32
33 #include "arch.h"
34
35 #if defined(ENABLE_GC_BOEHM)
36 # include "mm/memory.hpp"
37 #endif
38
39 #include "threads/thread.hpp"
40 #include "threads/threadlist.hpp"
41
42 #include "vm/exceptions.hpp"
43 #include "vm/globals.hpp"
44 #include "vm/method.hpp"
45 #include "vm/options.h"
46 #include "vm/os.hpp"
47 #include "vm/signallocal.hpp"
48 #include "vm/vm.hpp"
49
50 #if defined(ENABLE_STATISTICS)
51 # include "vm/statistics.h"
52 #endif
53
54
55 /* function prototypes ********************************************************/
56
57 void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p);
58 void signal_handler_sigusr1(int sig, siginfo_t *siginfo, void *_p);
59
60
61 /* signal_init *****************************************************************
62
63    Initializes the signal subsystem and installs the signal handler.
64
65 *******************************************************************************/
66
67 bool signal_init(void)
68 {
69 #if !defined(__CYGWIN__)
70         sigset_t mask;
71
72         TRACESUBSYSTEMINITIALIZATION("signal_init");
73
74 #if defined(__LINUX__) && defined(ENABLE_THREADS)
75         /* XXX Remove for exact-GC. */
76         if (threads_pthreads_implementation_nptl) {
77 #endif
78
79         /* Block the following signals (SIGINT for <ctrl>-c, SIGQUIT for
80            <ctrl>-\).  We enable them later in signal_thread, but only for
81            this thread. */
82
83         if (sigemptyset(&mask) != 0)
84                 os::abort_errno("signal_init: sigemptyset failed");
85
86 #if !defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
87         /* Let OpenJDK handle SIGINT itself. */
88
89         if (sigaddset(&mask, SIGINT) != 0)
90                 os::abort_errno("signal_init: sigaddset failed");
91 #endif
92
93 #if !defined(__FREEBSD__)
94         if (sigaddset(&mask, SIGQUIT) != 0)
95                 os::abort_errno("signal_init: sigaddset failed");
96 #endif
97
98         if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
99                 os::abort_errno("signal_init: sigprocmask failed");
100
101 #if defined(__LINUX__) && defined(ENABLE_THREADS)
102         /* XXX Remove for exact-GC. */
103         }
104 #endif
105
106 #if defined(ENABLE_GC_BOEHM)
107         /* Allocate something so the garbage collector's signal handlers
108            are installed. */
109
110         (void) GCNEW(int);
111 #endif
112
113         /* Install signal handlers for signals we want to catch in all
114            threads. */
115
116 #if defined(ENABLE_JIT)
117 # if defined(ENABLE_INTRP)
118         if (!opt_intrp) {
119 # endif
120                 /* SIGSEGV handler */
121
122                 signal_register_signal(SIGSEGV, (functionptr) md_signal_handler_sigsegv,
123                                                            SA_NODEFER | SA_SIGINFO);
124
125 #  if defined(SIGBUS)
126                 signal_register_signal(SIGBUS, (functionptr) md_signal_handler_sigsegv,
127                                                            SA_NODEFER | SA_SIGINFO);
128 #  endif
129
130 #  if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
131                 /* SIGFPE handler */
132
133                 signal_register_signal(SIGFPE, (functionptr) md_signal_handler_sigfpe,
134                                                            SA_NODEFER | SA_SIGINFO);
135 #  endif
136
137 #  if defined(__ALPHA__) || defined(__ARM__) || defined(__I386__) || defined(__MIPS__) || defined(__POWERPC__) || defined(__POWERPC64__) || defined(__S390__) || defined(__X86_64__) || defined(__M68K__)
138                 /* XXX use better defines for that (in arch.h) */
139                 /* SIGILL handler */
140
141                 signal_register_signal(SIGILL, (functionptr) md_signal_handler_sigill,
142                                                            SA_NODEFER | SA_SIGINFO);
143 #  endif
144
145 #  if defined(__POWERPC__)
146                 /* XXX use better defines for that (in arch.h) */
147                 /* SIGTRAP handler */
148
149                 signal_register_signal(SIGTRAP, (functionptr) md_signal_handler_sigtrap,
150                                                            SA_NODEFER | SA_SIGINFO);
151 #  endif
152 # if defined(ENABLE_INTRP)
153         }
154 # endif
155
156 #if defined(__DARWIN__)
157         do {
158                 struct utsname name;
159                 kern_return_t kr;
160
161                 /* Check if we're on 10.4 (Tiger/8.x) or earlier */
162                 if (uname(&name) != 0) 
163                         break;
164
165                 /* Make sure the string is large enough */
166                 /* Check the major number (ascii comparison) */
167                 /* Verify that we're not looking at '10.' by checking for a trailing period. */
168                 if (name.release[0] == '\0' || name.release[0] > '8' || name.release[1] != '.')
169                         break;
170
171                 /* Reset CrashReporter's task signal handler */
172                 kr = task_set_exception_ports(mach_task_self(),
173                                                                           EXC_MASK_BAD_ACCESS
174 #  if defined(__I386__)
175                                                                           | EXC_MASK_BAD_INSTRUCTION
176 #endif
177                                                                           , MACH_PORT_NULL,
178                                                                           EXCEPTION_STATE_IDENTITY,
179                                                                           MACHINE_THREAD_STATE);
180
181                 assert(kr == KERN_SUCCESS);
182         } while (false);
183 #endif
184 #endif /* !defined(ENABLE_JIT) */
185
186 #if defined(ENABLE_THREADS)
187         /* SIGHUP handler for threads_thread_interrupt */
188
189         signal_register_signal(Signal_INTERRUPT_SYSTEM_CALL, (functionptr) signal_handler_sighup, 0);
190 #endif
191
192 #if defined(ENABLE_THREADS)
193         /* SIGUSR1 handler for thread suspension */
194
195         signal_register_signal(SIGUSR1, (functionptr) signal_handler_sigusr1,
196                                                    SA_SIGINFO);
197 #endif
198
199 #if defined(ENABLE_THREADS) && defined(ENABLE_PROFILING)
200         /* SIGUSR2 handler for profiling sampling */
201
202         signal_register_signal(SIGUSR2, (functionptr) md_signal_handler_sigusr2,
203                                                    SA_SIGINFO);
204 #endif
205
206 #endif /* !defined(__CYGWIN__) */
207
208         return true;
209 }
210
211
212 /* signal_register_signal ******************************************************
213
214    Register the specified handler with the specified signal.
215
216 *******************************************************************************/
217
218 void signal_register_signal(int signum, functionptr handler, int flags)
219 {
220         struct sigaction act;
221
222         void (*function)(int, siginfo_t *, void *);
223
224         function = (void (*)(int, siginfo_t *, void *)) handler;
225
226         if (sigemptyset(&act.sa_mask) != 0)
227                 os::abort_errno("signal_register_signal: sigemptyset failed");
228
229         act.sa_sigaction = function;
230         act.sa_flags     = flags;
231
232         if (sigaction(signum, &act, NULL) != 0)
233                 os::abort_errno("signal_register_signal: sigaction failed");
234 }
235
236
237 /* signal_thread ************************************************************
238
239    This thread sets the signal mask to catch the user input signals
240    (SIGINT, SIGQUIT).  We use such a thread, so we don't get the
241    signals on every single thread running.
242
243 *******************************************************************************/
244
245 static void signal_thread(void)
246 {
247         threadobject *t;
248         sigset_t      mask;
249         int           sig;
250         int result;
251
252         t = THREADOBJECT;
253
254         if (sigemptyset(&mask) != 0)
255                 os::abort_errno("signal_thread: sigemptyset failed");
256
257 #if !defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
258         /* Let OpenJDK handle SIGINT itself. */
259
260         if (sigaddset(&mask, SIGINT) != 0)
261                 os::abort_errno("signal_thread: sigaddset failed");
262 #endif
263
264 #if !defined(__FREEBSD__)
265         if (sigaddset(&mask, SIGQUIT) != 0)
266                 os::abort_errno("signal_thread: sigaddset failed");
267 #endif
268
269         for (;;) {
270                 /* just wait for a signal */
271
272 #if defined(ENABLE_THREADS)
273                 thread_set_state_waiting(t);
274 #endif
275
276                 // sigwait can return EINTR (unlike what the Linux man-page
277                 // says).
278                 do {
279                         result = sigwait(&mask, &sig);
280                 } while (result == EINTR);
281
282                 if (result != 0)
283                         os::abort_errnum(result, "signal_thread: sigwait failed");
284
285 #if defined(ENABLE_THREADS)
286                 thread_set_state_runnable(t);
287 #endif
288
289                 /* Handle the signal. */
290
291                 signal_thread_handler(sig);
292         }
293 }
294
295
296 /* signal_thread_handler *******************************************************
297
298    Handles the signals caught in the signal handler thread.  Also used
299    from sun.misc.Signal with OpenJDK.
300
301 *******************************************************************************/
302
303 void signal_thread_handler(int sig)
304 {
305         switch (sig) {
306         case SIGINT:
307                 /* exit the vm properly */
308
309                 vm_exit(1);
310                 break;
311
312         case SIGQUIT:
313                 /* print a thread dump */
314 #if defined(ENABLE_THREADS)
315                 ThreadList::dump_threads();
316 #endif
317
318 #if defined(ENABLE_STATISTICS)
319                 if (opt_stat)
320                         statistics_print_memory_usage();
321 #endif
322                 break;
323
324 #if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
325         default: {
326                 // For OpenJDK we dispatch all unknown signals to Java.
327                 methodinfo* m = class_resolvemethod(class_sun_misc_Signal, utf_dispatch, utf_int__void);
328                 (void) vm_call_method(m, NULL, sig);
329
330                 if (exceptions_get_exception()) {
331                         log_println("signal_thread_handler: Java signal handler throw an exception while dispatching signal %d:", sig);
332                         exceptions_print_stacktrace();
333                         vm_abort("signal_thread_handler: Aborting...");
334                 }
335                 break;
336         }
337 #else
338         default:
339                 vm_abort("signal_thread_handler: Unknown signal %d", sig);
340 #endif
341         }
342 }
343
344
345 /* signal_start_thread *********************************************************
346
347    Starts the signal handler thread.
348
349 *******************************************************************************/
350
351 bool signal_start_thread(void)
352 {
353 #if defined(ENABLE_THREADS)
354         utf *name;
355
356         name = utf_new_char("Signal Handler");
357
358         if (!threads_thread_start_internal(name, signal_thread))
359                 return false;
360
361         /* everything's ok */
362
363         return true;
364 #else
365 #warning FIX ME!
366 #endif
367 }
368
369
370 /* signal_handler_sighup *******************************************************
371
372    This handler is required by threads_thread_interrupt and does
373    nothing.
374
375 *******************************************************************************/
376
377 #if defined(ENABLE_THREADS)
378 void signal_handler_sighup(int sig, siginfo_t *siginfo, void *_p)
379 {
380         /* do nothing */
381 }
382 #endif
383
384
385 /* signal_handler_sigusr1 ******************************************************
386
387    Signal handler for suspending threads.
388
389 *******************************************************************************/
390
391 #if defined(ENABLE_THREADS)
392 void signal_handler_sigusr1(int sig, siginfo_t *siginfo, void *_p)
393 {
394         // Really suspend ourselves by acknowledging the suspension.
395         threads_suspend_ack();
396 }
397 #endif
398
399
400 /*
401  * These are local overrides for various environment variables in Emacs.
402  * Please do not remove this and leave it at the end of the file, where
403  * Emacs will automagically detect them.
404  * ---------------------------------------------------------------------
405  * Local variables:
406  * mode: c++
407  * indent-tabs-mode: t
408  * c-basic-offset: 4
409  * tab-width: 4
410  * End:
411  */