diff --git a/src/classlib/gnuclasspath/classlib.h b/src/classlib/gnuclasspath/classlib.h index 1ecf9f9..e8ec4e2 100644 --- a/src/classlib/gnuclasspath/classlib.h +++ b/src/classlib/gnuclasspath/classlib.h @@ -51,6 +51,8 @@ extern Thread *classlibJThread2Thread(Object *jThread); extern void classlibThreadName2Buff(Object *jThread, char *buffer, int buff_len); +extern void classlibInitialiseSignalMask(); + #define classlibInitialiseSignals() \ /* NOTHING TO DO */ TRUE diff --git a/src/classlib/gnuclasspath/thread.c b/src/classlib/gnuclasspath/thread.c index 6136e6d..b6abc73 100644 --- a/src/classlib/gnuclasspath/thread.c +++ b/src/classlib/gnuclasspath/thread.c @@ -139,6 +139,20 @@ void classlibThreadName2Buff(Object *jThread, char *buffer, int buff_len) { String2Buff(name, buffer, buff_len); } +/* SIGQUIT and SIGINT are received synchronously via sigwait(), + so they must be blocked in all threads. SIGPIPE is blocked + so writes to broken pipes fail with EPIPE. Process-spawning + code must clear the inherited mask before exec. */ +void classlibInitialiseSignalMask() { + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGPIPE); + sigprocmask(SIG_BLOCK, &mask, NULL); +} + void classlibSignalThread(Thread *self) { sigset_t mask; int sig; diff --git a/src/classlib/openjdk/classlib.h b/src/classlib/openjdk/classlib.h index 015dfb2..43a2e57 100644 --- a/src/classlib/openjdk/classlib.h +++ b/src/classlib/openjdk/classlib.h @@ -44,6 +44,7 @@ extern void classlibSetThreadState(Thread *thread, int state); extern void classlibThreadName2Buff(Object *jThread, char *buffer, int buff_len); +extern void classlibInitialiseSignalMask(); extern int classlibInitialiseSignals(); extern void classlibSignalThread(Thread *self); diff --git a/src/classlib/openjdk/thread.c b/src/classlib/openjdk/thread.c index 71fc8aa..21ea6a4 100644 --- a/src/classlib/openjdk/thread.c +++ b/src/classlib/openjdk/thread.c @@ -205,15 +205,43 @@ void classlibSignalThread(Thread *self) { } } +/* Signals are received via asynchronous handlers. Make sure all + signals the VM uses are unblocked, so they can be delivered to + the registered handlers. SIGQUIT and SIGPIPE are handled by + JamVM itself; SIGHUP, SIGINT and SIGTERM can be handled by the + class library via sun.misc.Signal, and are the only signals it + can register (see JVM_FindSignal) */ +void classlibInitialiseSignalMask() { + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGPIPE); + pthread_sigmask(SIG_UNBLOCK, &mask, NULL); +} + +/* Writes to broken pipes must fail with EPIPE rather than kill + the VM. Unlike blocking or SIG_IGN, a no-op handler is reset + to default on exec, so it doesn't leak into spawned children. */ +static void pipeHandler(int sig) { +} + int classlibInitialiseSignals() { struct sigaction act; Class *signal_class; - act.sa_handler = signalHandler; + act.sa_handler = pipeHandler; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART; - sigaction(SIGQUIT, &act, NULL); + sigaction(SIGPIPE, &act, NULL); + /* + * Initialise everything used by signalHandler before installing + * it, as these signals may already be unblocked. + */ sem_init(&signal_sem, 0, 0); signal_class = findSystemClass(SYMBOL(sun_misc_Signal)); @@ -222,6 +250,17 @@ int classlibInitialiseSignals() { signal_dispatch_mb = findMethod(signal_class, SYMBOL(dispatch), SYMBOL(_I__V)); + if(signal_dispatch_mb == NULL) + return FALSE; - return signal_dispatch_mb != NULL; + act.sa_handler = signalHandler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + sigaction(SIGQUIT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + return TRUE; } diff --git a/src/thread.c b/src/thread.c index e4fb7d1..8ca1fbc 100644 --- a/src/thread.c +++ b/src/thread.c @@ -761,8 +761,6 @@ void createJavaThread(Object *jThread, long long stack_size) { enableSuspend(self); } -static void initialiseSignalMask(); - Thread *attachJNIThread(char *name, char is_daemon, Object *group) { Thread *thread = sysMalloc(sizeof(Thread)); void *stack_base = nativeStackBase(); @@ -775,7 +773,7 @@ Thread *attachJNIThread(char *name, char is_daemon, Object *group) { memset(thread, 0, sizeof(Thread)); /* Externally created threads will not inherit signal state */ - initialiseSignalMask(); + classlibInitialiseSignalMask(); /* Initialise the thread and add it to the VM thread list */ return attachThread(name, is_daemon, stack_base, thread, group); @@ -1134,24 +1132,9 @@ void printThreadsDump(Thread *self) { resumeAllThreads(self); } -static void initialiseSignalMask() { - sigset_t mask; - - sigemptyset(&mask); - sigaddset(&mask, SIGQUIT); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGPIPE); - sigprocmask(SIG_BLOCK, &mask, NULL); -} - static int initialiseSignals() { struct sigaction act; - /* Initialise signal mask. Signal masks are per-thread, - but as this is the main thread it will be inherited - by all threads created wtihin Java */ - initialiseSignalMask(); - /* Setup signal handler for thread suspension. Signal handlers are process-wide */ @@ -1161,7 +1144,16 @@ static int initialiseSignals() { sigaction(SIGUSR1, &act, NULL); /* Do classlib specific initialisation */ - return classlibInitialiseSignals(); + if(!classlibInitialiseSignals()) + return FALSE; + + /* Initialise signal mask, after the handlers have been + installed. Signal masks are per-thread, but as this is + the main thread it will be inherited by all threads + created wtihin Java */ + classlibInitialiseSignalMask(); + + return TRUE; } /* garbage collection support */