Multithreading with pthread and JNI calls crashs with "native thread exited without detaching"

Hi Everybody,

in my game I want to async load some images from web and save them to the local storage. For the Android part I use JNI calls to download and save the image to storage with native java code. It works perfect. But when I try to do this in a seperated pthread to prevent my game from freezing I get the error: “native thread exited without detaching” after “ThreadFunction(void *arg)” is finished.

here is my c++ code:

//c++ code
void MyClass::loadImages(){
    pthread_t thread;
    SimpleStructure* args = new SimpleStructure();
    args->s_url = url;
    args->s_savename = savename;
    pthread_create(&thread, NULL, &ThreadFunction, args);
}

void* ThreadFunction(void * arg) {
    SimpleStructure * args = (SimpleStructure * ) arg;

    std::string localImagePath;
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        localImagePath = getImageFilePathJNI(args->s_url.c_str(),
                                             args->s_savename.c_str());
    #endif
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        localImagePath = URLSpriteHelper::getUrlSprite(args->s_url, 
                                                       args->s_savename);
    #endif
    
    pthread_mutex_lock(&mutex);
    loadedImages.push_back(localImagePath);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

here is my JNI code:

extern "C"
{
    std::string getImageFilePathJNI(const char* urlStr, const char* saveName)
    {
        std::string ret;
        JniMethodInfo t;
    
        if (JniHelper::getStaticMethodInfo(t, 
                                           "org/cocos2dx/cpp/AppActivity",
                                           "saveImageFromURLtoInternalStorage", 
                                           "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")) {

            jstring StringArg1 = t.env->NewStringUTF(urlStr);
            jstring StringArg2 = t.env->NewStringUTF(saveName);
        
            jstring retFromJava = (jstring)t.env->CallStaticObjectMethod(t.classID, t.methodID, StringArg1, StringArg2);
            const char* str = t.env->GetStringUTFChars(retFromJava, 0);
            ret = str;
        
            t.env->ReleaseStringUTFChars(retFromJava, str);
            t.env->DeleteLocalRef(StringArg1);
            t.env->DeleteLocalRef(StringArg2);
            t.env->DeleteLocalRef(t.classID);
        }
        return ret;
    }
}

Any ideas what I’m doing wrong?
Thank you very much!

Because JniHelper uses cached env in cacheEnv. And env is thread specific. So i think you should write your own jni codes in new thread, or you should change the implementation of JniHelper.

So what you have to do may be

  • modify JniHelper::getEnv
JNIEnv* JniHelper::getEnv() {
        JNIEnv *_env = (JNIEnv *)pthread_getspecific(g_key);
       // if (_env == nullptr)  --> comment this line
            _env = JniHelper::cacheEnv(_psJavaVM);
        return _env;
    }
  • invoke jvm->DetachCurrentThread() when switch to a new thread(from GL thread to new thread or new thread to GL thread).

Hope it can help.

Hi and thanks a lot for your answer. But i dont understand at which point i have to invoke

jvm->DetachCurrentThread()

Is it at the end of my JNI method?

Before using jni method in a new thread.

all right, works perfect. thanks!

Glad to hear that.