Search code examples
javac++cmultithreadingjava-native-interface

Multithreaded JNI-Calls


I've learned that I have to attach the c-threads to the jvm with jvm->AttachCurrentThread everytime I use the JNIEnv. This should be quite similar to a mutex, I lock it with jvm->AttachCurrentThread at the beginning of the method and unlock it with jvm->DetachCurrentThread() at the end. So, now I have a method where I use the JNIEnv moreoften. Do I have to call AttachCurrentThread everytime? Here you have a code sample:

  std::unique_ptr<IState> JNIGame::createEmptyState() {
    JNIEnv* env;
    jvm->AttachCurrentThread((void**)&env, NULL);
    if(!jGame_createEmptyState)
      jGame_createEmptyState =  env->GetMethodID(jGameC, "createEmptyState", "()Ljni/JNIGames$IJNIState;");

    JNIState *state = new JNIState();
    //needed?
    jvm->AttachCurrentThread((void**)&env, NULL);
    state->jStateO = env->CallObjectMethod(jGameO, jGame_createEmptyState);
    jvm->DetachCurrentThread();
    return std::unique_ptr<IState>(state);
}

As you see, I attach the thread two times, because without the second one, the code crashes. But if they act like mutex, only the first one would be needed. Could you help me, why I need them every time? Is it save like the code is now?


Solution

  • Not sure what you mean by "act like a mutex," but the documentation says, "Trying to attach a thread that is already attached is a no-op."

    Your example attaches twice, and then detaches once. The second attach has no effect.

    That sentence I quoted came from the "Invocation API" chapter of the JNI doc. "Invocation API" is what they call the collected methods that you need to call Java from C (as opposed to the ones that you use to write native methods that you can call from Java.)


    A couple of points worth mentioning:

    The thread that starts the JVM is implicitly attached. That thread becomes the JVM's "main" thread.

    The JVM will shut down (can't be restarted) whenever the last non-daemon thread exits. If a native thread detaches, that is counted as a thread exit.

    My application has a dedicated "main" thread that starts the JVM, notifies other threads that it is started, and then sleeps forever. The sleeping "main" thread is what keeps my JVM alive when none of my app's other native threads are using it.