Search code examples
c++multithreadingandroid-ndkjava-native-interface

Android JNI - thread synchronization


I have a native C++ code, that I am calling from Android via JNI.

JNIEXPORT void JNICALL
Java_com_myapp_CApi_setFoo(JNIEnv *env, jobject thiz, jstring foo) {
    const char * fooStr = env->GetStringUTFChars(foo, 0);
    MyCApiSetFoo(fooStr);
    env->ReleaseStringUTFChars(foo, fooStr);
}

JNIEXPORT jstring JNICALL
Java_com_myapp_CApi_getFoo(JNIEnv *env, jobject thiz) {
    return env->NewStringUTF(MyCApiGetFoo());
}

All is working. However, get and set methods can be accessed from different threads and in that case, sometimes get is called before set. How can I solve thread synchronization? I am unable to edit underlying API.

I was thinking about use std::unique_lock inside each JNI method and create global std::mutex variable on which I will lock. Is this a good way, or is there some "standard" JNI way (I have found that there are monitors accesible from env).

I am also calling get very often (it is during OpenGL rendering loop), so performance is critical.


Solution

  • The JNI way of locking a mutex is MonitorEnter/MonitorExit.

    That is, you can enter the same monitor that a java synchronized block would from java code.

    JNIEXPORT void JNICALL
    Java_com_myapp_CApi_setFoo(JNIEnv *env, jobject thiz, jstring foo) {
        const char * fooStr = env->GetStringUTFChars(foo, 0);
        env->MonitorEnter(thiz); // same effect as synchronized(thiz) { ...
        MyCApiSetFoo(fooStr);
        env->MonitorExit(thiz);
        env->ReleaseStringUTFChars(foo, fooStr);
    }
    
    JNIEXPORT jstring JNICALL
    Java_com_myapp_CApi_getFoo(JNIEnv *env, jobject thiz) {
        env->MonitorEnter(thiz);
        auto res = MyCApiGetFoo();
        env->MonitorExit(thiz);
        return env->NewStringUTF(res);
    }
    

    You can use any object to lock on, it doesn't have to be thiz if that isn't providing a sufficient level of locking granularity.

    Alternatively, if you just need to lock a structure internal to the C++ code, use a static std::mutex with a lock_guard.