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.
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
.