Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android JNI - Call AttachCurrentThread without DetachCurrentThread

I have been reading about JNI stuff and can't seem to figure out what happens if a thread starts -> calls AttachCurrentThread() -> make some JNI calls -> thread exit.

Ideally, we should call DetachCurrentThread() before thread exits, however, what are the implications if the app doesn't do that? Would it cause memory leak or any other problem?

like image 502
pree Avatar asked Oct 23 '14 17:10

pree


People also ask

Can I call AttachCurrentThread () on an already-attached thread?

Calling AttachCurrentThread () on an already-attached thread is a no-op. Android does not suspend threads executing native code. If garbage collection is in progress, or the debugger has issued a suspend request, Android will pause the thread the next time it makes a JNI call.

How do I detach a thread that is attached through JNI?

Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward, in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be called before the thread exits, and call DetachCurrentThread from there.

Why can't I call JNI from a thread?

Until a thread is attached, it has no JNIEnv, and cannot make JNI calls. It's usually best to use Thread.start () to create any thread that needs to call in to Java code. Doing so will ensure that you have sufficient stack space, that you're in the correct ThreadGroup, and that you're using the same ClassLoader as your Java code.

Why does Android pause a thread when JNI calls?

If garbage collection is in progress, or the debugger has issued a suspend request, Android will pause the thread the next time it makes a JNI call. Threads attached through JNI must call DetachCurrentThread () before they exit .


1 Answers

Not calling DetachCurrentThread() will definitely cause a memory leak; other consequences are JVM-specific, and probably irrelevant for Android apps, where the JVM shuts down when the process exits. There are quite a few C++ wrappers that help to manage thread Attach/Detach, see for example: http://w01fe.com/blog/2009/05/c-callbacks-into-java-via-jni-made-easyier

Update: 1000 thanks to fadden for the eye-opening link; on Dalvik, a thread that exits without calling DetachCurrentThread(), brings the whole VM and the process crashing down.

Here is the logcat from the official emulator, my code based on the HelloJni sample from NDK:

10-26 04:16:25.853: D/dalvikvm(1554): Trying to load lib /data/app-lib/com.example.hellojni-2/libhello-jni.so 0xb3d264f0
10-26 04:16:25.893: D/dalvikvm(1554): Added shared lib /data/app-lib/com.example.hellojni-2/libhello-jni.so 0xb3d264f0
10-26 04:16:25.893: D/dalvikvm(1554): No JNI_OnLoad found in /data/app-lib/com.example.hellojni-2/libhello-jni.so 0xb3d264f0, skipping init
10-26 04:16:26.463: D/gralloc_goldfish(1554): Emulator without GPU emulation detected.
10-26 04:16:31.033: D/threadFunction(1554): Attaching
10-26 04:16:31.173: D/threadFunction(1554): Not Detaching
10-26 04:16:31.183: D/dalvikvm(1554): threadid=11: thread exiting, not yet detached (count=0)
10-26 04:16:31.193: D/dalvikvm(1554): threadid=11: thread exiting, not yet detached (count=1)
10-26 04:16:31.193: E/dalvikvm(1554): threadid=11: native thread exited without detaching
10-26 04:16:31.193: E/dalvikvm(1554): VM aborting
10-26 04:16:31.213: A/libc(1554): Fatal signal 6 (SIGABRT) at 0x00000612 (code=-6), thread 1567 (xample.hellojni)

Here is the relevant function added to hello-jni.c:

static JavaVM* jvm = 0;
static jobject activity = 0; // GlobalRef

void* threadFunction(void* irrelevant)
{
    JNIEnv* env;
    usleep(5000000);

    __android_log_print(ANDROID_LOG_DEBUG, "threadFunction", "Attaching");

    (*jvm)->AttachCurrentThread(jvm, &env, NULL);

    jclass clazz = (*env)->GetObjectClass(env, activity);
    jmethodID methodID = (*env)->GetMethodID(env, clazz, "finish", "()V" );
    (*env)->CallVoidMethod(env, activity, methodID);

    __android_log_print(ANDROID_LOG_DEBUG, "threadFunction", "Not Detaching");
//    (*jvm)->DetachCurrentThread(jvm);
}

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    (*env)->GetJavaVM(env, &jvm);
    activity = (*env)->NewGlobalRef(env, thiz);

    pthread_t hThread;
    pthread_create(&hThread, NULL, &threadFunction, NULL);
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

A nice implementation of this strategy can be found in WebRTC git repo.

like image 61
Alex Cohn Avatar answered Sep 20 '22 12:09

Alex Cohn