Nils
Nils

Reputation: 189

Catch multiple Ctrl+C keypresses in Java to do graceful and forced shutdowns

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?

Upvotes: 4

Views: 2077

Answers (2)

Curtis Yallop
Curtis Yallop

Reputation: 7319

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

Upvotes: 0

kiheru
kiheru

Reputation: 6618

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();
            }
        }
    }
}

Upvotes: 2

Related Questions