4

In my Java console application I catch a Ctrl+C key press and add a thread performing graceful shutdown using Runtime.getRuntime().addShutdownHook()

In this thread I process all oustanding work in some internal queues and exit my application gracefully which is normally what I want.

However, sometimes the application can have internal queues that take a while to proces (several minutes), if so I would like an option to press Ctrl+C a second time to forcibly exit the application.

I have seen other applications working this way but I cannot find myself how to catch the second Ctrl+C in Java?

Nils
  • 189
  • 1
  • 14

2 Answers2

2

Warning, this is not what you should do. The usual reasons for avoiding the sun package apply. Think thrice before actually using this in real code. At minimum the access to the signal handling should be done with reflection and fall back to registering the usual shutdown hook when it fails. I left that out for brevity.

So, all hope abandon, ye who enter here:

import java.util.concurrent.atomic.AtomicBoolean;
import sun.misc.Signal;
import sun.misc.SignalHandler;

public class Sigint {
    private static void prepareShutdownHandling() {
        // This would be the normal shutdown hook
        final Thread shutdown = new Thread() {
            @Override
            public void run() {
                // Simulate slow shutdown
                for (int i = 0; i < 10; i++) {
                    System.out.println("Normal shutdown running");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Normal shutdown finished");
                System.exit(0);
            }
        };

        Signal.handle(new Signal("INT"), new SignalHandler() {
            private AtomicBoolean inShutdown = new AtomicBoolean();
            public void handle(Signal sig) {
                if (inShutdown.compareAndSet(false, true)) {
                    // Normal shutdown
                    shutdown.start();
                } else {
                    System.err.println("Emergency shutdown");
                    System.exit(1);
                }
            }
        });
    }

    public static void main(String args[]) {
        prepareShutdownHandling();
        while (true) {
            System.out.println("Main program running");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
cryptearth
  • 108
  • 8
kiheru
  • 6,588
  • 25
  • 31
  • Thanks for an elegant solution! So essentially what it boils down to is that it is impossible to do the signal handling required in a portable / cross-platform way using Java? (without using sun.* libraries) Or perhaps really in any language if signal handling varies much between platforms? – Nils Jul 26 '13 at 11:20
  • @Nils I'd hardly call it elegant :-D. AFAIK there's no portable way, and the *supported* non-portable way (signal chaining) runs the handlers after the usual signal handling so I presume it would not work (I haven't tried). But maybe someone comes up with a more reliable hack. – kiheru Jul 26 '13 at 11:32
  • Looks like the way to do it without sun.* is: the default SIGINT handler can be suppressed with -Xrs parameter. Then the signal handling can be done in native code. Not great for portability that solution either - neither the command line switch nor the native code. – kiheru Jul 26 '13 at 20:59
0

Use Signal.handle on SIGINT and in the handler, restore the original default SIG_DFL handler. So if it is hit a second time, it will terminate the program using the SIG_DFL handler (supplied by the OS).

private void registerSigIntHandler() {
    Signal.handle(new Signal("INT"), new SignalHandler() {
        public void handle(Signal sig) {
            log.info("SIGINT received");
            Signal.handle(new Signal("INT"), SignalHandler.SIG_DFL);
            shutdown();
            SignalHandler.SIG_DFL.handle(new Signal("INT"));
        }
    });
}

This also calls the SIG_DFL handler to terminate the process once your shutdown method completes.

https://stackoverflow.com/a/71028985/854342

Curtis Yallop
  • 6,696
  • 3
  • 46
  • 36