How to Obtain Jni Interface Pointer (Jnienv *) for Asynchronous Calls

How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls

Within synchronous calls using JNI from Java to C++ the "environment" has already been setup by the JVM, however going in the other direction from an arbitrary C++ thread it may not have been

Therefore you need to follow these steps

  • get hold of the JVM environment context using GetEnv
  • attach the context if necessary using AttachCurrentThread
  • call the method as normal using CallVoidMethod
  • detach using DetachCurrentThread

Full example. Note I have written about this in the past in more detail on my blog

JavaVM* g_vm;
env->GetJavaVM(&g_vm);

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();
}

Why does the JNI C API use a pointer-to-pointer instead of straight pointers for JNIEnv?

JNIEnv is not really a pointer to a pointer, but to a data structure containing other (private) thread-specific information. The JNINativeInterface* is just the first field in the struct, and the rest aren't public. This allows for more flexibility in VM's implementation of JNI function tables.

Some links here for the benefit of those who might come across this:

  1. Threads and JNI - here it explains:

    The JNI interface pointer (JNIEnv *) is only valid in the current thread. You must not pass the interface pointer from one thread to another, or cache an interface pointer and use it in multiple threads. The Java Virtual Machine will pass you the same interface pointer in consecutive invocations of a native method from the same thread, but different threads pass different interface pointers to native methods.

  2. JNI spec

Does JNI really need interface pointer to provide multiple versions of JNI function tables

No, they (Sun) could have come up with some other design. This JniEnv is with us for historical reasons, and does contribute to JNI overhead, albeit not significant.

The idea was to let Java side control whether to use debugging set of functions at runtime per thread. I don't believe this idea has ever helped somebody debugging their native code.

Note that the C++ wrapper reduces the hassle significantly (sure, this is syntax only; the overhead doesn't go).

How do I get a JavaVM or JNIEnv from an already-running JVM using JNI?

With the help of the comments, I got that crash to stop happening. The trick was to pre-allocate an array.

let jvm_ptr = Vec::with_capacity(1).as_mut_ptr();
let count = null_mut();

JNI_GetCreatedJavaVMs(jvm_ptr, 1, count);

JNI: Does C++ calls Java asynchronous?

CallVoidMethod is executed synchronously.

Maybe you have an excepion on c++ side?, do you use c++ jni exception checks?:

env->CallVoidMethod(javaClassObject, javaReceiveMethod, intParam, byteParam, objectParam);
if(env->ExceptionOccurred()) {
// Print exception caused by CallVoidMethod
env->ExceptionDescribe();
env->ExceptionClear();
}

The C++ code (in a shared object library) is running in it's own C++ thread. It is using a existing JavaVM of a java app that is already up and running.

its not clear whether you have attached current thread to virtual machine. Make sure env is comming from AttachCurrentThread call. You will find example here: How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls.

Is calling AttachCurrentThread on JNI costly?

Although you CAN attach the calling thread on a per-JNI-call basic, you really SHOULD NOT, unless you have no other choice. A native thread MUST attach itself to the JVM before it can make JNI calls. To avoid unnecessary overhead, the thread really should attach itself as soon as possible (at least before the 1st JNI call), and stay attached until it no longer needs to make any further JNI calls.

JNI IMU code not compiling base operand of ‘-’ has non-pointer type ‘JNIEnv’

JNIEnv is declared a bit different for C and C++, see jni.h for details. In few words in C we use

(*env)->NewStringUTF(env,imuData.temperature);

In C++ it should be

env->NewStringUTF(imuData.temperature);

As you see C++ version doesn't need env dereference and passing it as first parameter to JNI functions.



Related Topics



Leave a reply



Submit