C++ calling Java object method: Access Violation - java

I'm trying to implement a Java/C++ binding for a sound streaming class. To keep my example simple, I will reduce it to its seeking method, which is enough to describe my problem:
public abstract class JSoundStream extends SoundStream {
public abstract void seek(float timeOffset);
}
For testing, I use the following implementation:
#Override public void seek(float timeOffset) {
System.out.println("seek(" + timeOffset + ")");
}
The seek method is a callback method, delegated to by a native C++ functions that serves as a callback for whatever plays the stream. Picture a media player application with a fast forward function as an example:
"Fast forward" button pressed -> Streaming library invokes C++ callback seek -> Delegate to Java method seek
Note this is just an example, neither the Event Dispatch Thread nor anything else funky is involved.
When an instance of JSoundStream gets created, a native method is called that will save back both the Java VM pointer (JavaVM*) as well as the Java object reference (jobject). I do this because I cannot control when exactly the callback is called, and I know of no way to get the JNI environment or the object live with no Java references whatsoever. So I save back that information at the time of object creation, where I do have the references.
Inside of the C++ seek method, I'm trying to invoke the Java seek method this way:
virtual void OnSeek(float timeOffset) {
JNIEnv* env;
jvm->AttachCurrentThread((void**)&env, NULL);
env->CallVoidMethod(binding, m_seek, (jfloat)timeOffset);
}
Where binding is the jobject, jvm the Java VM pointer and m_seek the jmethodID of the seek method I obtained before.
However, that invocation of CallVoidMethod will result in an access violation in jvm.dll. All of the pointers and values are valid for what I can say, and I did make sure the Java object does not get garbage collected. I believe that storing the jobject and / or the Java VM pointer is the source of the problem, but then again I cannot see why, because those values are not changing while the program is running.
Can anybody see a problem in the way I am approaching this? How else - without storing references - would I invoke a Java object method from C++ code?

Your approach should be correct, if
Your jobject has been retrieved with binding = env->NewGlobalRef(binding_passed_as_argument);
You do not call AttachCurrentThread from the same thread multiple times - use TLS to store the JNIEnv pointer.

Related

Can I create a c++ class instance and use it as an entity in Android JNI?

Let's say I create a class 'Car' in cpp.
I want to creat an instance of that class with it's empty constructor in cpp.
Can I do it and use it in java code on android?
For instance:
Java code
Car myCar = new Car();
CPP class
class Car{
std::string model;
int creationYear;
Car(){}
}
thanks for the help
Yes. You can easily have a native object that shadows a Java object - assuming you can call the C++ Car() constructor. You could use a public static method in the C++ Car class to do that.
It's a bit of a hack, but a Java long is guaranteed to be 64 bits, so it's long enough to hold a native pointer value.
In Java:
public class Car
{
// A Java long is 64 bits, so it will
// hold a native pointer
private long nativeCar;
private native long newNativeCar();
private native void deleteNativeCar( long car );
public Car()
{
this.nativeCar = newNativeCar();
}
// allow for explicit native cleanup by caller
public synchronized deleteCar()
{
if ( 0 != this.nativeCar )
{
deleteNativeCar( nativeCar );
nativeCar = 0;
}
}
// handle cases where the native cleanup code
// was not called
#Override
protected void finalize() throws Throwable
{
deleteCar();
super.finalize();
}
}
Compile that, then use javah on the class file to create your C header file. (Note that JNI uses C, not C++. You can write C++ code to implement your logic, but the interface presented to the JVM must be a C interface.)
You'll get a couple of functions in your native header, something like this (I've stripped off the annotation from javah - you will need to keep that...):
jlong some_class_path_newNativeCar( JNIEnv *, jobject );
void some_class_path_deleteNativeCar( JNIEnv *, jobject, jlong );
You can implement your C++ code then:
jlong some_class_path_newNativeCar(
JNIEnv *env, jobject obj )
{
Car *nativeCar = new Car();
// C cast - we're returning a C value
return( ( jlong ) nativeCar );
}
void some_class_path_deleteNativeCar(
JNIEnv *env, jobject obj, jlong jNativeCar )
{
Car *cppNativeCar = ( Car * ) jNativeCar;
delete cppNativeCar;
}
I've deliberately kept the code simple - there's quite a bit I left out. javah can be a bit tricky to figure out how to use properly, for example. But if you can't figure out how to use javah properly you shouldn't be writing JNI code anyway.
Because JNI code is fragile. You can't make any mistakes, or you will wind up getting seemingly random failures or having your JVM crash, and the crash will likely not be easily traced to the bad code that caused the problem. There are lots of rules for making JNI calls using the JNIEnv * pointer supplied to a native call. For example, in general you can't save the values passed to you by the JVM and use them outside of the context you received them in - using them in another thread, or after the function where they were passed to you returns is a great way to cause those JVM crashes I mentioned above.
Nor can you make any JNI calls to Java if there are any exceptions pending from previous calls - again, you risk unpredictable errors and crashes if you do.
So keep your native code simple.
Keep all the logic and processing on only one side of the Java/native interface if you can.
Pass only C or Java strings, primitives or primitive arrays across the Java/native interface if you can.
Interacting with actual complex Java object from native code will take many, many lines of C or C++ code to safely replicate what can be done in Java with one or two lines of code. Even simple one-line get*()/set*() Java calls become 20 or 30 lines or more of C or C++ code if you replicate all the exception and failure checks you need in order to guarantee safe JVM execution after the native call no matter what data gets passed in. If you pass null to a Java method that can't handle a null value it will throw a NullPointerException that you can catch and your JVM runs happily or shuts down cleanly with an uncaught exception. Pass NULL to a JNI function that can't handle it, and if you don't properly check for any failure or any exception and then properly handle it, your JVM will exhibit seemingly unrelated failures or just crash.

Passing multiple arguments for callback function in java from native c code (JNI)

Below code calls a callback function in java from native c code passing some string data as argument.
Native C layer
jmethodID statusId = env->GetMethodID(pctx->jniHelperClz, "CallbackHandler", "(Ljava/lang/String;)V");
jstring string_data = env->NewStringUTF((const char*)"SOME_STRING_DATA");
env->CallVoidMethod(pctx->jniHelperObj, statusId, string_data);
env->DeleteLocalRef(string_data);
Android / Java (Callback handler)
#Keep
private void CallbackHandler(String string_data) {
// Some Code
}
Along with string I want to pass a int type data also. My java Callback handler looks like below. What should I change in my native layer to support two arguments.
#Keep
private void CallbackHandler(String string_data, int int_data) {
// Some Code
}
You need to change method signature from (Ljava/lang/String;)V to (Ljava/lang/String;I)V:
jmethodID statusId = env->GetMethodID(pctx->jniHelperClz, “CallbackHandler”, "(Ljava/lang/String;I)V”);
Also, you use DeleteLocalRef() not proper way. This method used to delete the local references created via NewLocalRef(), but NewStringUTF() not create them. Method NewStringUTF() create jstring object in java heap which under garbage collector control. You don't need to delete this manually.
Take note:
Local references are valid for the duration of a native method call. They are freed automatically after the native method returns. Each local reference costs some amount of Java Virtual Machine resource. Programmers need to make sure that native methods do not excessively allocate local references. Although local references are automatically freed after the native method returns to Java, excessive allocation of local references may cause the VM to run out of memory during the execution of a native method.
You need to use DeleteLocalRef() to immediately delete large objects (for example in loop).

Pass C++ object to Java vs set object parameters one by one using JNI

I have searched for this question and found a few answers but have not really found what I am looking for.
I call Java using JNI from C++ and set a Java object's fields one by one. Something like below:
jobject jSomeObject = (jobject) JVM_ENV->CallObjectMethod(myObj, getObjMethodID, "");
JVM_ENV->CallVoidMethod(jSomeObject , setMethodID1, SomeIntVal);
JVM_ENV->CallVoidMethod(jSomeObject , setMethodID2, SomeStringVal);
All parameters inside the jSomeObject are set one by one like this. And you see that there are multiple JNI calls going on which is expensive. What I am thinking is, if there is a way that I set all the parameters in the native environment and send the object just once to avoid multiple JNI calls.
Some of the posts says that it is not possible to pass a custom object to JNI. Can I do it ?
Edit:
Above calls changed to something:
jobject jSomeObject = (jobject) JVM_ENV->CallObjectMethod(myObj, getObjMethodID, "");
someClass obj = new someClass();
obj.setMethod1(someInvVal);
obj.setMethod2(someStringVal); // so on...
JVM_ENV->CallVoidMethod(jSomeObject , setMethodID1, obj);
No: You can only call methods and constructors and get and set fields that are defined in Java.
Yes: You can possibly define additional classes and methods in Java that will do what you need in one call. For example, myObj:getObjMethodID seems to be a factory method. You could add a method overload that takes all the values you want to initialize the created object with.
In general, if you can make things powerful in Java, the tasks done in JNI will be simpler.

Safe to pass objects to C functions when working in JNI Invocation API?

I am coding up something using the JNI Invocation API. A C program starts up a JVM and makes calls into it. The JNIenv pointer is global to the C file. I have numerous C functions which need to perform the same operation on a given class of jobject. So I wrote helper functions which take a jobject and process it, returning the needed data (a C data type...for example, an int status value). Is it safe to write C helper functions and pass jobjects to them as arguments?
i.e. (a simple example - designed to illustrate the question):
int getStatusValue(jobject jStatus)
{
return (*jenv)->CallIntMethod(jenv,jStatus,statusMethod);
}
int function1()
{
int status;
jobject aObj = (*jenv)->NewObject
(jenv,
aDefinedClass,
aDefinedCtor);
jobject j = (*jenv)->CallObjectMethod
(jenv,
aObj,
aDefinedObjGetMethod)
status = getStatusValue(j);
(*jenv)->DeleteLocalRef(jenv,aObj);
(*jenv)->DeleteLocalRef(jenv,j);
return status;
}
Thanks.
I'm not acquainted with the details of JNI, but once thing I noticed is this:
return (*jenv)->CallIntMethod(jenv,jStatus,statusMethod);
That looks like the official JNI code and it is taking a jobect as a parameter. If it works for JNI, there is no reason it can't work for your code.
All jni objects are valid until the native method returns. As long as you dont store non global jni objects between two jni calls everything should work.
The invocation of a jni function should work like this:
Java function call
create native local references
call native function
do your stuff
exit native function
release existing local references
return to java
The step 4 can contain any code, local references stay valid until step 6 if not release before.
If you want to store jni objects on the c side between two calls to a native java function you have to create global references and release them later. Not releasing a global reference leads to memory leaks as the garbage collector is unable to free the related java objects.

Passing pointers between C and Java through JNI

At the moment, i'm trying to create a Java-application which uses CUDA-functionality. The connection between CUDA and Java works fine, but i've got another problem and wanted to ask, if my thoughts about it are correct.
When i call a native function from Java, i pass some data to it, the functions calculates something and returns a result. Is it possible, to let the first function return a reference (pointer) to this result which i can pass to JNI and call another function that does further calculations with the result?
My idea was to reduce the overhead that comes from copying data to and from the GPU by leaving the data in the GPU memory and just passing a reference to it so other functions can use it.
After trying some time, i thought for myself, this shouldn't be possible, because pointers get deleted after the application ends (in this case, when the C-function terminates). Is this correct? Or am i just to bad in C to see the solution?
Edit:
Well, to expand the question a little bit (or make it more clearly): Is memory allocated by JNI native functions deallocated when the function ends? Or may i still access it until either the JNI application ends or when i free it manually?
Thanks for your input :)
I used the following approach:
in your JNI code, create a struct that would hold references to objects you need. When you first create this struct, return its pointer to java as a long. Then, from java you just call any method with this long as a parameter, and in C cast it to a pointer to your struct.
The structure will be in the heap, so it will not be cleared between different JNI calls.
EDIT: I don't think you can use long ptr = (long)&address; since address is a static variable. Use it the way Gunslinger47 suggested, i.e. create new instance of class or a struct (using new or malloc) and pass its pointer.
In C++ you can use any mechanism you want to allocate/free memory: the stack, malloc/free, new/delete or any other custom implementation. The only requirement is that if you allocated a block of memory with one mechanism, you have to free it with the same mechanism, so you can't call free on a stack variable and you can't call delete on malloced memory.
JNI has its own mechanisms for allocating/freeing JVM memory:
NewObject/DeleteLocalRef
NewGlobalRef/DeleteGlobalRef
NewWeakGlobalRef/DeleteWeakGlobalRef
These follow the same rule, the only catch is that local refs can be deleted "en masse" either explicitly, with PopLocalFrame, or implicitly, when the native method exits.
JNI doesn't know how you allocated your memory, so it can't free it when your function exits. Stack variables will obviously be destroyed because you're still writing C++, but your GPU memory will remain valid.
The only problem then is how to access the memory on subsequent invocations, and then you can use Gunslinger47's suggestion:
JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
MyClass* pObject = new MyClass(...);
return (long)pObject;
}
JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
MyClass* pObject = (MyClass*)lp;
...
}
While the accepted answer from #denis-tulskiy does make sense, I've personnally followed suggestions from here.
So instead of using a pseudo-pointer type such as jlong (or jint if you want to save some space on 32bits arch), use instead a ByteBuffer. For example:
MyNativeStruct* data; // Initialized elsewhere.
jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));
which you can later re-use with:
jobject bb; // Initialized elsewhere.
MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);
For very simple cases, this solution is very easy to use. Suppose you have:
struct {
int exampleInt;
short exampleShort;
} MyNativeStruct;
On the Java side, you simply need to do:
public int getExampleInt() {
return bb.getInt(0);
}
public short getExampleShort() {
return bb.getShort(4);
}
Which saves you from writing lots of boilerplate code ! One should however pay attention to byte ordering as explained here.
Java wouldn't know what to do with a pointer, but it should be able to store a pointer from a native function's return value then hand it off to another native function for it to deal with. C pointers are nothing more than numeric values at the core.
Another contibutor would have to tell you whether or not the pointed to graphics memory would be cleared between JNI invocations and if there would be any work-arounds.
I know this question was already officially answered, but I'd like to add my solution:
Instead of trying to pass a pointer, put the pointer in a Java array (at index 0) and pass that to JNI. JNI code can get and set the array element using GetIntArrayRegion/SetIntArrayRegion.
In my code, I need the native layer to manage a file descriptor (an open socket). The Java class holds a int[1] array and passes it to the native function. The native function can do whatever with it (get/set) and put back the result in the array.
If you are allocating memory dynamically (on the heap) inside of the native function, it is not deleted. In other words, you are able to retain state between different calls into native functions, using pointers, static vars, etc.
Think of it a different way: what could you do safely keep in an function call, called from another C++ program? The same things apply here. When a function is exited, anything on the stack for that function call is destroyed; but anything on the heap is retained unless you explicitly delete it.
Short answer: as long as you don't deallocate the result you're returning to the calling function, it will remain valid for re-entrance later. Just make sure to clean it up when you're done.
Its best to do this exactly how Unsafe.allocateMemory does.
Create your object then type it to (uintptr_t) which is a 32/64 bit unsigned integer.
return (uintptr_t) malloc(50);
void * f = (uintptr_t) jlong;
This is the only correct way to do it.
Here is the sanity checking Unsafe.allocateMemory does.
inline jlong addr_to_java(void* p) {
assert(p == (void*)(uintptr_t)p, "must not be odd high bits");
return (uintptr_t)p;
}
UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
UnsafeWrapper("Unsafe_AllocateMemory");
size_t sz = (size_t)size;
if (sz != (julong)size || size < 0) {
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
}
if (sz == 0) {
return 0;
}
sz = round_to(sz, HeapWordSize);
void* x = os::malloc(sz, mtInternal);
if (x == NULL) {
THROW_0(vmSymbols::java_lang_OutOfMemoryError());
}
//Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize);
return addr_to_java(x);
UNSAFE_END

Categories

Resources