Java ShutdownHook Unable to Join Main Thread when Run From JNI

Jeff G :

I have some Java code to create a shutdown hook in order to exit cleanly when the client presses ctrl+C:

private static void shutdownHandler(Thread mainThread) {
    try {
        mainThread.join(30000);
    } catch (InterruptedException e) {
    }
}

public static void main(String[] args) {
    final Thread mainThread = Thread.currentThread();
    Thread shutdownThread = new Thread(() -> shutdownHandler(mainThread));
    Runtime.getRuntime().addShutdownHook(shutdownThread);
}

When I run this from the command line, it works as expected (the main thread exits and returns almost immediately to the command prompt). However, if I write a JNI wrapper that calls this using the following C++ code instead:

JavaVMInitArgs vm_args;
// Populate vm_args

JavaVM *jvm;
JNIEnv *env;
JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&env), &vm_args);

jclass mainClass = env->FindClass("path/to/my/class");
jmethod mainMethod = env->GetStaticMethodID(mainClass, "main", "([L" STRING_CLASS ";)V");

jclass stringClass = env->FindClass(STRING_CLASS);
jobjectArray mainArgs = env->NewObjectArray(0, stringClass, NULL);

env->CallStaticVoidMethod(mainClass, mainMethod, mainArgs);
jvm->DestroyJavaVM();

Then the shutdownHandler method hangs until the 30 second timeout elapses, then returns control to the C++ code and eventually exits. Does anyone know of a way to allow the shutdownHandler method to join the main thread when started from a JNI call?

Holger :

In your first example, the main thread exits, then the JVM detects that there are no non-Daemon threads left and will initiate the JVM shutdown. At this point, there is no problem joining the main thread, as it has ended even before the shutdown.

In your second variant, the main thread, i.e. the thread which executed the main method via env -> CallStaticVoidMethod(…), is busy executing jvm -> DestroyJavaVM(). Since that function waits for the completion of the shutdown handlers and your shutdown handler waits for the completion of this thread, you have a deadlock.

You can get a similar behavior with pure Java code too. When you place System.exit(0); at the end of the main method, letting the main thread initiate the shutdown and wait for its completion, you get a similar deadlock.

Generally, you should not perform join operation in shutdown handlers. These handlers are supposed to clean up and return as quickly as possible.

Or, as the documentation puts it:

Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down. Attempts to use other thread-based services such as the AWT event-dispatch thread, for example, may lead to deadlocks.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=75564&siteId=1