This article will consider various implementations of a shutdown hook: simply a piece of code we wish to execute at the very end of the application.

The usual scenario of using shutdown hooks is when we want to clean up resources (closing network ports, log files, files in general, streams, …). Normally, we do such as soon as possible but it is immensely useful to have this kind of tool when the user exits the application abruptly (or not in an expected manner). Even if there is no need for a cleanup, it is still sometimes useful to log the fact that the application stopped (and when).

JVM

First of all, let’s consider the Java/JVM approach to shutdown hook:

public class Main {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Doing something really important here...");     
        }));
    }

}

According to the Java Core API documentation:

If the VM crashes due to an error in native code then no guarantee can be made about whether or not the hooks will be run.

In the current implementation, the Java runtime uses an IdentityHashMap<Thread, Thread> to store all the threads which will then be executed by the static method ApplicationShutdownHooks.runHooks(). I highly suggest to inspect the source code of the java.lang.ApplicationShutdownHooks class for more detailed info. The implementation isn’t platform dependent (the dependent part is the thread API).

Bash

function cleanup() {
    RM=$1
    printf "====TRAP====\nDoing some cleanup...\n"
    # ...
    printf "OK.\n"
    exit 0
}

printf "The script runs here...\n"

trap cleanup EXIT

printf "...and also here.\n"

In the shell environment, traps are a very vague equivalent of shutdown hooks. The following excerpts are taken from the man page of trap:

trap [-lp] [[arg] sigspec ...]

The command arg is to be read and executed when the shell receives signal(s) sigspec.
If a sigspec is EXIT (0) the command arg is executed on exit from the shell.

From the man page, we can also learn that the trap command is much more powerful than being a simple shutdown hook executor since each sigspec carries a different behaviour (for example, the trap command can be used for printing out debug messages).

Python

import atexit

def first():
    print("Executing the first function.")

def second(_):
    print("Executing the second function.")

def third(a, b, c, d):
    print("Executing the third function.")
    print(len(a + b + c + d))

atexit.register(first)
atexit.register(second, "some argument which will be ignored")
atexit.register(third, "there", "are", "four", "args")

In Python, the atexit module is used for registering actions which are to be executed before the very termination of the script. The documentation also makes it clear that actions submitted are saved on a LIFO-like structure. The argumentation is that “lower level modules will normally be imported before higher level modules and thus must be cleaned up later”.