trouble passing float array from C to java method

A

Arash Nikkar

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 = SampleData;
}

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
 
C

Chris Uppal

Arash said:
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
 
A

Arash Nikkar

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

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!
 
D

Daniel Pitts

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!


Does it crash, or simply never return? Could it be a problem in
addPoints?
 
G

Gordon Beaton

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

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
 
C

Chris Uppal

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.

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
 
A

Arash Nikkar

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!
 
G

Gordon Beaton

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
 
C

Chris Uppal

Arash said:
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
 
A

Arash Nikkar

(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 = SampleData;
}

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!!
 
C

Chris Uppal

Arash said:
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
 
A

Arash Nikkar

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!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top