3

I'm using jvm->GetEnv(&envThread, JNI_VERSION_1_6) to get the "env" for multple threads in order to make multiple envThread->GetMethodID() calls. Both threads are properly attached to the JVM.

I call the function returned by "GetMethodID()" on the first thread with no problems, but when the second thread attemps the call I see this message:

art/runtime/check_jni.cc:65] JNI DETECTED ERROR IN APPLICATION: thread Thread[13,tid=8207,Native,Thread*=0xaed08400,peer=0x12dcd080,"Thread-10224"]
  using JNIEnv* from thread Thread[1,tid=8148,Runnable,Thread*=0xb4e07800,peer=0x87bc5ef0,"main"]

A/art(8148): art/runtime/check_jni.cc:65 in call to CallVoidMethodV
08-31 14:11:08.029: A/art(8148): art/runtime/check_jni.cc:65] "Thread-10224" prio=10 tid=13 Runnable
08-31 14:11:08.029: A/art(8148): art/runtime/check_jni.cc:65] group="main" sCount=0 dsCount=0 obj=0x12dcd080 self=0xaed08400
08-31 14:11:08.029: A/art(8148): art/runtime/check_jni.cc:65] sysTid=8207 nice=-11 cgrp=apps sched=0/0 handle=0xafb18b00
08-31 14:11:08.029: A/art(8148): art/runtime/check_jni.cc:65] state=R schedstat=( 17710887 6014947 64 ) utm=1 stm=0 core=3 HZ=100
08-31 14:11:08.029: A/art(8148): art/runtime/check_jni.cc:65]   | stack=0xaee04000-0xaee06000 stackSize=1012KB

Because my call to jvm->getEnv() returns JNI_OK for the second thread, it is already attached (as expected). But what isn't expected is that the JniENV returned is exactly the same as the one from the first thread. Thus causing the crash.

Has anyone seen this sort of thing before? I'm quite lost for what to do...

Thanks.

fadden
  • 51,356
  • 5
  • 116
  • 166
Bungles
  • 1,969
  • 2
  • 25
  • 54
  • May help: http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html as referenced by the Android ART docs: https://developer.android.com/guide/practices/verifying-apps-art.html – Morrison Chang Aug 31 '15 at 22:10
  • Thanks! Unfortunately checkjni provided no output whatsoever. Perhaps my device does't support it. Samsung Galaxy S5 with Lollipop 5.0.x. – Bungles Aug 31 '15 at 22:28

3 Answers3

5

I think you should be using AttachCurrentThread from the other thread.

Here's a similar question on SO:

void callback(int val) {
    JNIEnv * g_env;
    // double check it's all ok
    int getEnvStat = g_vm->GetEnv((void **)&g_env, JNI_VERSION_1_6);
    if (getEnvStat == JNI_EDETACHED) {
        std::cout << "GetEnv: not attached" << std::endl;
        if (g_vm->AttachCurrentThread((void **) &g_env, NULL) != 0) {
            std::cout << "Failed to attach" << std::endl;
        }
    } else if (getEnvStat == JNI_OK) {
        //
    } else if (getEnvStat == JNI_EVERSION) {
        std::cout << "GetEnv: version not supported" << std::endl;
    }

    g_env->CallVoidMethod(g_obj, g_mid, val);

    if (g_env->ExceptionCheck()) {
        g_env->ExceptionDescribe();
    }

    g_vm->DetachCurrentThread();
}
Community
  • 1
  • 1
Buddy
  • 10,874
  • 5
  • 41
  • 58
  • If the thread is not attached, `GetEnv()` is expected to return JNI_EDETACHED and set the JNIEnv* to NULL. According to the question it's returning JNI_OK with a non-NULL but non-unique JNIEnv*. Still, `AttachCurrentThread()` might be a useful workaround if `GetEnv()` is broken. – fadden Sep 01 '15 at 04:27
  • 1
    The above function will always detach the current thread, regardless of whether the thread was attached by the function or if it already was attached when entering the function. That may not be desirable. – Michael Sep 01 '15 at 05:32
  • This is basically the same as my code. The "getEnvStat" is JNI_OK because the thread was previously attached during startup. The crash occurs when I "CallVoidMedthod()" with the stack trace indicating that "g_env" is somehow invalid. I can't tell if "g_vm->GetEnv()" gave me the wrong "g_env" value or if the problem is that the OTHER thread was the first one to use "CallVoidMethod()" on said function. Is it true that once a thread calls into a Java method, no other thread may also call that Java method? – Bungles Sep 01 '15 at 19:02
  • The failure message is saying that "Thread-10224" is using a JNIEnv created for the main thread. JNIEnv is thread-local, and you should get a different value back for each thread. Naming something `g_env` (global env) is a red flag. Threads do not call "dibs" on methods; this has nothing to do with CallVoidMethod. – fadden Sep 01 '15 at 19:57
0

It is possible that JNI code in your project is not written for multitasking and saves the Java stuff in static variables. I have seen such code. As I understand, in one project the guys had some legacy C/C++ code that did not pass along all these env parameters, but at some point they needed to call Java. In another project, as I understand, the reason for using global variables was the management's decision to begin sales when the engineers showed them a prototype. If you see variable names like g_env, it's a hint that the variables once were global.

On the other hand, I have not seen your code, probably this note will be more relevant to later bypassers than to you.

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
0

This was a case of a bug in our code base, it just wasn't very obvious.

Bungles
  • 1,969
  • 2
  • 25
  • 54
  • 5
    Care to elaborate? Just saying that you had a bug in your code isn't going to be very useful for people who stumble upon this question in the future when they do a search for a similar error message. – Michael Sep 03 '15 at 08:18