Java Forum / General / February 2007
trouble passing float array from C to java method
Arash Nikkar - 16 Feb 2007 18:35 GMT I have a float array, and I am trying to pass it to a java method, but it craps out during the call to pass the array. Anyone have any ideas?
Here is the header for the java method:
public void addPoints(final float[] scopePoints)
and here is the C++ reference to this method:
addPoints = jenv->GetMethodID(myClass, "addPoints", "([F)V");
lastly, here is the code in question (the printMsg function is just a helper function which calls a println method in my java class). Also, SampleData is defined as such: float SampleData[3072];
jfloat realGraph[3000]; printMsg("created jint array\n"); for (i=0;i<3000;i++) { realGraph[i] = SampleData[i]; }
printMsg("copied array\n");
jfloatArray returnArray = jenv->NewFloatArray(3000); printMsg("created java jint array\n");
jenv->SetFloatArrayRegion(returnArray, 0,3000,realGraph); printMsg("transfered data into java array\n");
jenv->CallVoidMethod(job, addPoints, returnArray); //THIS IS WHERE IT FAILS!!! printMsg("passed the array\n");
when i run the code, all of the print messages come through except the last one. I also added loops which printed out the contents of each array (SampleData, realGraph, & returnArray), and they all contain the correct contents.
Any help is appreciated
Chris Uppal - 16 Feb 2007 19:01 GMT > jenv->CallVoidMethod(job, addPoints, returnArray); //THIS IS > WHERE IT FAILS!!! > printMsg("passed the array\n"); What is the value of jobj at this point ? Your code looks OK to me, so I suspect that jobj might not be properly set up.
In general, it's a very bad idea to call JNI functions without checking for errors. That call, or any of the preceeding JNI functions might have failed, and once something has failed (thrown a Java exception which you haven't checked for), nothing else will work. If you add error checking, then that might help track down the problem.
Incidentally, I think an array of 3000 floats is a little large to be allocating on the C stack; its not large enough that expect that it would routinely cause problems, but not so small that I'd ignore it either.
-- chris
Arash Nikkar - 16 Feb 2007 22:11 GMT On Feb 16, 11:01 am, "Chris Uppal" <chris.up...@metagnostic.REMOVE- THIS.org> wrote:
> > jenv->CallVoidMethod(job, addPoints, returnArray); //THIS IS > > WHERE IT FAILS!!! [quoted text clipped - 14 lines] > > -- chris Hi chris,
job is my jobject reference. For this application, the C code is running in the background and calling my Java function when it gathers a specific number of results.
I tried reducing the size of my array to 500, but I got the same problem, the program crashed as it was sending the array to my java function.
I dont quite understand what you mean by adding error checking. Im assuming you mean in the c++ code...what type of error checking? could you give me an example.
Lastly, Im pretty sure that my jenv and job objects are setup correctly, as the printMsg uses them. Here is the code for my printMsg method:
void printMsg(char *str) { jstring myString = jenv->NewStringUTF(str); jenv->CallVoidMethod(job, print, myString); }
Thanks for all your help!
Daniel Pitts - 17 Feb 2007 00:38 GMT > On Feb 16, 11:01 am, "Chris Uppal" <chris.up...@metagnostic.REMOVE- > [quoted text clipped - 43 lines] > > Thanks for all your help! Does it crash, or simply never return? Could it be a problem in addPoints?
Gordon Beaton - 17 Feb 2007 08:01 GMT > Lastly, Im pretty sure that my jenv and job objects are setup > correctly, as the printMsg uses them. Here is the code for my [quoted text clipped - 4 lines] > jenv->CallVoidMethod(job, print, myString); > } Note that you should call DeleteLocalRef(myString) before returning from this function.
If your C++ code is running in its own thread and producing objects without returning to Java (*calling* Java is not suffucient), then you must dispose of them when you're finished using them. Failing to do so is a serious bug that could very well be the cause of your current problem.
Consider bracketing the code in calls to PushLocalFrame() and PopLocalFrame().
/gordon
 Signature [ don't email me support questions or followups ] g o r d o n + n e w s @ b a l d e r 1 3 . s e
Chris Uppal - 17 Feb 2007 16:31 GMT > > What is the value of jobj at this point ? Your code looks OK to me, so > > I suspect that jobj might not be properly set up.
> job is my jobject reference. For this application, the C code is > running in the background and calling my Java function when it gathers > a specific number of results. Just to check: the jobj is a global ref, or a local ref (jobject) from the JNIEnv associated with your background thread ? If not then that might be your problem.
> > In general, it's a very bad idea to call JNI functions without checking > > for errors. That call, or any of the preceeding JNI functions might > > have failed, and once something has failed (thrown a Java exception > > which you haven't checked for), nothing else will work. If you add > > error checking, then that might help track down the problem.
> I dont quite understand what you mean by adding error checking. Im > assuming you mean in the c++ code...what type of error checking? could > you give me an example. After every call to JNI you should check for errors (there are, in fact, a few JNI functions which have no way to fail, but they are in the minority -- if you see JNI code without an error check, then you should wonder why it's missing).
> printMsg("created jint array\n"); Since you've said that printMsg() uses JNI, it should check for errors too (as below).
> jfloatArray returnArray = jenv->NewFloatArray(3000); This can fail. So you should check the returned value (it may be NULL). I'm not certain, but I /think/ that if the returned value is not NULL then you are not obliged to check for Java exceptions too. (I do anyway, myself, but that's in automatically-generated code so it's easier always to check than to try to spot the cases where a check can be skipped).
> jenv->SetFloatArrayRegion(returnArray, > 0,3000,realGraph); You'll have to check the spec to see whether this can potentially fail. I don't /think/ so, but it's your responsibility to check the specification.
> jenv->CallVoidMethod(job, addPoints, returnArray); This can fail by throwing a Java exception. The only way to check whether that happened is one of ExceptionCheck() or ExceptionOcurred(). If it did then you can clear it with ExceptionClear(). If you don't clear it then subsequent JNI calls will fail. The helper function ExceptionDescribe() may also be useful (but for the life of me, I can't remember whether it implicitly clears the pending exception).
> void printMsg(char *str) { > jstring myString = jenv->NewStringUTF(str); > jenv->CallVoidMethod(job, print, myString); > } You should also do the same kind of messing around inside printMsg(): NewString() can fail, and CallVoidMethod() can throw exceptions. (And you should release the jstring, as Gordon has already explained).
I know that all that checking is /extremely/ tedious, and messes up the code. Unfortunately, it's necessary (which is one of the reasons I don't write JNI code by hand...).
BTW, if you haven't already tried turning on the -Xcheck:jni option, then that is probably worth the effort. NB: I have known it produce false positives (complain about something that was perfectly OK, and abort when there was no need) in one dot.dot release of JDK 1.5, but it is usually helpful (of course, if it /doesn't/ complain then that doesn't imply that the code /is/ OK).
-- chris
Arash Nikkar - 20 Feb 2007 18:26 GMT Thanks for all the replies, here are some answers:
I have made some progress (I believe I found the source of the problem, but I cant figure it out just yet).
I added the Xcheck:jni runtime option, and I got the following:
FATAL ERROR in native method: Using JNIEnv in the wrong thread at RealGraph.startScope(Native Method) at RealGraph$2.run(RealGraph.java:134) at java.lang.Thread.run(Thread.java:595)
Then I realized I was calling this method from within another Java Thread (I didn't know this would have any type of effect). So I moved the call outside of the thread (i.e. it would make my GUI hang, but thats ok for now), but then I got this:
FATAL ERROR in native method: Wrong object class or methodID passed to JNI call at RealGraph.startScope(Native Method) at RealGraph.main(RealGraph.java:124) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 25) at java.lang.reflect.Method.invoke(Method.java:585) at com.intellij.rt.execution.application.AppMain.main(AppMain.java: 90)
Some background on how I have things setup. When I first start my application, I call an initialization method in my C++ class which sets global environment variables for JENV, Jobject, and my method pointers (i.e. addPoints, print), etc. I also do some checks there.
Then, when I am ready to start polling, I call the startScope method. Here I only perform polling operations. I do not replace my environment variables or method pointers. I did try this however, and I got the same exception as the one above.
Lastly, for exception checking, will calls to ExceptionCheck() or ExceptionOcurred() suffice, or should I check for null values as well?
thanks for all your help!
Gordon Beaton - 20 Feb 2007 18:38 GMT > Some background on how I have things setup. When I first start my > application, I call an initialization method in my C++ class which > sets global environment variables for JENV, Jobject, and my method > pointers (i.e. addPoints, print), etc. I also do some checks there. Don't cache the JNIEnv!
Also, be careful when caching object references: it's only safe to cache global references.
/gordon
 Signature [ don't email me support questions or followups ] g o r d o n + n e w s @ b a l d e r 1 3 . s e
Chris Uppal - 20 Feb 2007 19:17 GMT > Some background on how I have things setup. When I first start my > application, I call an initialization method in my C++ class which > sets global environment variables for JENV, Jobject, and my method > pointers (i.e. addPoints, print), etc. I also do some checks there. It OK to put methodIDs (method pointers) into global variables because they can be used from any thread, but jobjects and JNIEnvs are completely tied to the thread which created them and they must never be used from any other thread.
In JNI there are three possible scenarios.
1) (I include this case only for completeness since I don't think it applies to you.) The application itself is in C (or similar), and uses JNI to invoke Java code. In this case the C code will load and launch the JVM itself, and in doing so will create a JNIEnv which is valid in, and only in, that OS thread. Any jobjects (local references) which are created via that JNIEnv are also valid on that thread (and only on that thread). Note that you must release such references explicitly.
2) Your C code is invoked via a Java 'native' call. In that case the Java runtime will supply a JNIEnv for you to use for the duration of that call. That JNIEnv is not valid in any other call, nor in any other thread. Any jobject (local reference) created during that call will be released when the call returns back to Java code, so the jobject is invalid anywhere else too.
3) Your C code is running on a separate thread which the Java VM doesn't know about. Unless you /tell/ the JVM about that thread then no JNIEnv or jobject can validly be used from it. If you call the JNI function AttachCurrentThread() or AttachCurrentThreadAsDemon(), that tells the JVM about the thread from which it was called, and passes back a JNIEnv. The new JNIEnv is valid for use on that thread, and only on that thread. Any jobjects (local references) which are created via that JNIEnv are also valid on that thread (and only on that thread). Note that you must release such references explicitly.
If you need (as I think you do) to store a reference to a specific Java object, and use that later from different places and threads, then you must convert it to a "global reference" using NewGlobalRef(). Such a global reference is valid in any thread. It must be released explicitly.
Incidentally, the JNI function GetEnv() can be used to retrieve the correct JNIEnv to use it whatever context its called from (it'll return an error if called from a thread which hasn't been attached by the JVM). That function is (by design) very quick so it may be more convenient for you to use that instead of global variables.
> Lastly, for exception checking, will calls to ExceptionCheck() or > ExceptionOcurred() suffice, or should I check for null values as well? You are asking about the call to NewFloatArray() ? If so then I believe that either an exception check, or a NULL check, is adequate -- you don't need to do both. That applies to all the array-creation methods.
In the more general case (not just array creation) where a JNI function returns a jobject. I believe (but am not totally certain) that they always return NULL if there is any problem, so you only have to do the exception check if you see a NULL return. (Some of the functions can return NULL without it being an indication of a problem, so you can't in general use an exception check /instead/ of a NULL check). For instance calling this Java method, with CallObjectMethod(), would return NULL, but there'd be no exception pending.
static String someMethod() { return null; }
I think you would benefit from reading the JNI book, which can be downloaded from: http://java.sun.com/docs/books/jni/ It's not too long, and is very helpful.
-- chris
Arash Nikkar - 20 Feb 2007 22:03 GMT (NOTE: This might become a repost, as I tried to post this earlier)
Thanks to everyone for their help. I learned a couple of lessons here, no more global jenv/job variables, error checking & clean up after myself (i.e. release strings/arrays).
I never could get the GetEnv() method to work, so instead I just pass the jenv/job variables between methods, that way the correct thread is using the correct variables.
I have one last question: Do I need to ReleaseFloatArray if I created it using NewFloatArray?
i.e.:
jfloat realGraph[5000];
for (i=0;i<5000;i++) { realGraph[i] = SampleData[i]; }
jfloatArray returnArray = jenv->NewFloatArray(5000);
jenv->SetFloatArrayRegion(returnArray, 0,5000,realGraph);
jenv->CallVoidMethod(job, addPoints, returnArray);
do I need to release returnArray? or realGraph for that matter?
thanks again to all who provided input!!
Chris Uppal - 21 Feb 2007 11:14 GMT > I have one last question: Do I need to ReleaseFloatArray if I created > it using NewFloatArray? There is no function called ReleaseFloatArray(). I don't /think/ you meant ReleaseFloatArrayElements(), but just in case you did then that is always paired with GetFloatArrayElements() and should not be called for any other reason (and always must be called after GetFloatArrayElements()).
But I think you were asking whether you have to do a DeleteLocalRef() on the reference returned by NewFloatArray(). If so then the answer is that you do have to ensure that the reference is released. However, whether you need an /explicit/ call to DeleteLocalRef() depends on how and where your code is running. It'll never do any harm to call it yourself, but under some circumstances the system will do it for you. That will happen if your code has been called from a Java native method (as I mentioned before); or if you make use of PushLocalFrame() and PopLocalFrame() (as Gordon mentioned earlier) -- see the book I recommended for details and more explanation.
> jfloat realGraph[5000]; [...]
> do I need to release [...] realGraph for that matter? No, that's just a normal C array and is subject to normal C rules -- there's no special JNI magic you have to worry about.
-- chris
Arash Nikkar - 22 Feb 2007 00:10 GMT apparently im having problems posting to this group, because my last msg didn't show up.
anywyas, I just wanted to thank you for such a thorough answer. I ended up adding the DeleteLocalRef call, as this function is called very often (its somewhat of a delayed real-time system).
thanks again!
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|