In my Java application I want to capture SIGINTs, do some pre-processing, and then let the default behavior (process termination) run. I would think I could do something like this:
Signal.handle(new Signal("INT"), new SignalHandler() { @Override public void handle(Signal signal) { // preprocessing // ... // now do default behavior SignalHandler.SIG_DFL.handle(signal); } });
However when I send at SIGINT
to this application, I get a SEGV
:
# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x0000000000000000, pid=10261, tid=21507 # # JRE version: Java(TM) SE Runtime Environment (8.0_51-b16) (build 1.8.0_51-b16) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.51-b03 mixed mode bsd-amd64 compressed oops) # Problematic frame: # C 0x0000000000000000 # # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again # # An error report file with more information is saved as: # /private/tmp/hs_err_pid10261.log # # If you would like to submit a bug report, please visit: # http://bugreport.java.com/bugreport/crash.jsp # The crash happened outside the Java Virtual Machine in native code. # See problematic frame for where to report the bug. # Abort trap: 6
It seems SignalHandler.SIG_DFL
is not meant to be called directly (even from other signal handling code). So how can I manually trigger it?
Alternatively, how can I manually replicate the behavior of SIG_DFL
? It appears to be equivalent to:
System.exit(signal.getNumber() + 128)
but I don’t see any documentation to that effect.
Another way to phrase my question:
In practice* is there a difference between these two code blocks?
A)
Signal.handle(new Signal("INT"), SignalHandler.SIG_DFL);
B)
Signal.handle(new Signal("INT"), new SignalHandler() { @Override public void handle(Signal signal) { System.exit(signal.getNumber() + 128) }});
*I know undocumented behavior could change at any time, but it’s unlikely that the JVM’s exit behavior will change mid-version. An answer that simply details what happens now is acceptable, in practice.
Advertisement
Answer
Credit for originally noticing this goes to RealSkeptic, but I wanted to expand on it in an answer.
The default behavior for SIGINT
, SIGTERM
, and SIGHUP
is not, in fact, SignalHandler.SIG_DFL
. Instead, the java.lang.Terminator
class registers a SignalHandler
that simply calls Shutdown.exit()
:
SignalHandler sh = new SignalHandler() { public void handle(Signal sig) { Shutdown.exit(sig.getNumber() + 0200); } };
You can capture this SignalHandler
by calling Signal.handle()
(since it returns the old handler), or you can simply define your own handler that calls System.exit()
which will do the same thing.
Note that Terminator
‘s call to Shutdown.exit()
is not exactly the same as System.exit()
. The former is package-private, meaning you can’t call it directly. If a security manager prevents you from calling System.exit()
, you’ll have to capture the original handler and reuse it.
Warning: this is undocumented behavior. It’s unlikely but entirely possible that future releases of Java could change this behavior.