JNI's throw new does not throw an exception

Y

yarona

Hi,

I'm trying to throw an exception from my native C++ code, the code
snippet looks like this:

/////////
jclass clsException = pEnv->FindClass("MyExceptionClass");
if(clsException )
{
pEnv->ThrowNew(clsException, "HelloWorld");
}
return;
/////////

The 'ThrowNew' method is called, but no exception seems to be thrown or
caught on the Java side.

ThrowNew returns 0 if it's any help, and if I call 'ExceptionCheck'
afterwards, it returns 'true', before that it returns 'false' meaning
that the exception pending is the one that I have just thrown.

Any ideas would be appreciated,

Thanks,

Yaron
 
G

Gordon Beaton

I'm trying to throw an exception from my native C++ code, the code
snippet looks like this:

/////////
jclass clsException = pEnv->FindClass("MyExceptionClass");
if(clsException )
{
pEnv->ThrowNew(clsException, "HelloWorld");
}
return;
/////////

The 'ThrowNew' method is called, but no exception seems to be thrown
or caught on the Java side.

ThrowNew returns 0 if it's any help, and if I call 'ExceptionCheck'
afterwards, it returns 'true', before that it returns 'false'
meaning that the exception pending is the one that I have just
thrown.

There's nothing wrong with the code you've posted, but there might be
something wrong in the code you didn't post.

For example, what is "ExceptionCheck()"? Do you mean
ExceptionOccurred()?

Have you tried calling ExceptionDescribe() after ThrowNew() to see
whether it really is your exception that's been thrown?

How are you handling this in the caller?

/gordon
 
R

Roedy Green

jclass clsException = pEnv->FindClass("MyExceptionClass");
if(clsException )
{
pEnv->ThrowNew(clsException, "HelloWorld");
}
return;

Is there a debugger that lets you debug Java when in Java and C++ or
MASM when in C++?
 
S

Stefan Schulz

Any ideas would be appreciated,

I think your problem might be that after a ThrowNew, the native method
does not abruptly terminate automatically. That is, flow of control
returns to your native method, and the new exception is pending at this
point. You need to "abort" your method manually if ExceptionOccurred()
returns true (or handle the exception in native code, which is something
i'd call "severe pain").
 
R

Roedy Green

pEnv->ThrowNew(clsException, "HelloWorld");

Insert this after the Throw:

pEnv->DeleteLocalRef( clsException );

Your problem basically is that C++ knows nothing about Java
exceptions. Exceptions are just control blocks lying about. It is up
to you in some C++ish way, after you set up the exception, return
quickly and gracefully up the call stack to your caller back in Java
who can then handle the exception. On your way back, C++ can handle or
notice the exception by explicitly testing for it with ExceptionCheck.

In Abundance this is known as a "graceful bailout".
 
G

Gordon Beaton

Insert this after the Throw:

pEnv->DeleteLocalRef( clsException );

No, don't. There is seldom any need for DeleteLocalRef(), and
certainly not in this case.

This is akin to telling him to null method local references before
returning from a Java method.

/gordon
 
Y

yarona

Hello all, and many thanks for your replies,

"ExceptionCheck()" is a method which is much like "ExceptionOccured"
only it returns jboolean rather than a jthrowable.

I am returning from the native method immidiatly after "ThrowNew" (
actually I'm calling "ExceptionDescribe" first, and it is my exception
that is thrown).
I also put a break point in the exception object's c'tor on the Java
side, and sure enough it's getting called, meaning that it's being
created.

The Java side looks something like this:

try
{
MyThrower thrower = new MyThrower();
thrower.ExceptionThrowingMethod(); // a native method
}
catch(MyExceptionClass e)
{
//handle exception
}

MyExceptionClass is derived from java.lang.Exception

But I never reach the catch clause - even though as I've mentioned, the
"MyExceptionClass" object was constructed.

Thanks,

Yaron
 
G

Gordon Beaton

"ExceptionCheck()" is a method which is much like "ExceptionOccured"
only it returns jboolean rather than a jthrowable.

Hmm, I wasn't even aware that that method existed, but I see it now in
the docs...
I am returning from the native method immidiatly after "ThrowNew" (
actually I'm calling "ExceptionDescribe" first, and it is my exception
that is thrown).

This could very well be the problem: In my experience,
ExceptionDescribe() seems to clear the exception, at least sometimes.

Apparently others have also seen this behaviour:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4067541

Try removing these checks from your native code. Just throw the
exception, then return.

/gordon
 
Y

yarona

Also there's no need to call DeleteLocalRef() since I'm running from a
native method, and not a utility method - the JVM releases all local
refs for me.
 
C

Chris Uppal

Gordon said:
No, don't. There is seldom any need for DeleteLocalRef(), and
certainly not in this case.

It's not relevant to this case, but for the record, there is one important
category of application where DeleteLocalRef() is not optional.

That is when your application uses JNI to call into Java, rather than the other
way around. I.e. your main program is written in something like C, and it
creates a JVM in order to make use of some Java library or other. In such
cases, the C code that creates the reference has not been called /from/ Java
so the automatic clean-up of local references does not occur (since that
happens when the C code returns /to/ Java).

There are other occasions when you might want to DeleteLocalRef(), but -- as
Gordon says -- they are atypical. And anyway they are discussed in the JNI
tutorial and documentation (which is, however, vague on the above point -- the
writers preferring to pretend that the only reason for using JNI is to call
"legacy" code from Java, an attitude I find insufferably arrogant).

I have to make heavy use of the above technique, since I'm using JNI to call
legacy Java code from Smalltalk...

-- chris
 
G

Gordon Beaton

Also there's no need to call DeleteLocalRef() since I'm running from
a native method, and not a utility method - the JVM releases all
local refs for me.

I'll assume that "utiltity method" here refers to other methods in
your native library, i.e. not your native methods themselves, but
other functions in the native library that might be called by your
native methods.

If that's what you mean (and please explain if it isn't), then
contrary to what you've implied, you wouldn't have needed to use
DeleteLocalRef() in that case either.

When a native method is invoked from Java, a local context is created
to hold all object references created at (or below) that point. When
you return back to the JVM (and thereby pass that point as you move up
the stack), the local context is freed and any objects referred to
(potentially) become eligible for garbage collection.

So it doesn't matter if you pass the JNIEnv* to a helper function and
create your objects there instead, as far as the JVM is concerned
there is no difference.

Have you solved the original problem?

/gordon
 
Y

yarona

Hi Gordon, thanks for your replies.

This is going to be long so bare with me, trust me it's interesting :)

You are correct, When I said "utility functions" I meant helper
functions in my native code that are called from my native methods.

You are also correct when you are saying that when returning to the JVM
all local refs are released, but what happens if I allocate a local ref
within a helper function, and then return from it to my native method,
and after that go on doing some more native work? the local refs
allocated inside the helper would not be released until I return from
the native method to the JVM, taking up some precious space!!!

As for the original problem - yes I have solved it, the problem was a
wierd and suttle one:

As I've gathered from all the posts here,and the JNI documentation,
calling JNI functions after a call to ThrowNew, and before returning to
the JVM is not safe (putting functions like ExceptionOccured and
ExceptionCheck aside...)

This means that posting an exception should preferably look like this:

pEnv->ThrowNew(clsException);
//no JNI calls here!!!
return;

otherwise the exception might never be thrown on the Java side.

The interesting thing is that my code looks just like the above code(I
took out the DecribeExceptionCall) - and still no exception - so there
must have been something between the call to ThreadNew and the return
that has been implicily put there by the compiler.

I opened my disassembler, and sure enough I saw what happened:
I wanted to make my native method thread synchronized, yet release the
monitor upon an excetpion or returning from the method, in order to
prevent a deadlock - I didnt want to return from the method and forget
to call ExitMonitor, so I've written a little guard C++ class:

class MonitorGuard
{
public:
MonitorGuard(JNIEnv* pEnv, jobject monitor)
:m_pEnv(pEnv), m_monitor(monitor)
{
if(m_pEnv && m_monitor)
{
m_pEnv->MonitorEnter(m_monitor);
}
}

~MonitorGuard()
{
if(m_pEnv && m_monitor)
{
m_pEnv->ExitMonitor(m_monitor);
}
}

private:
JNIEnv* m_pEnv;
jobject m_monitor;
};

As you can see this class takes the monitor upon construction, and
releases it upon destruction, this way, when I return from the native
method, ExitMonitor is automatically called, and there's no danger for
a deadlock.

BUT... I fell in another pittfall, after I do this:

pEnv->ThrowNew(clsException);
return;

the guard's destructor was implicitly called, after ThrowNew, and
before returning to the JVM, thus calling ExitMonitor, which is
apparently not included in the functions which are safe to be called
after ThrowNew - so no exception was thrown.

I solved it by createing a special scope for the synchronized code,
throwing the exception outside of it like so:
////////////////////////////////////
bool bThrow = false;
jclass clsException;
do
{
CMonitorGuard(pEnv, objThis); //objThis is the 'this' object passed
in the JNI interface.
//do some native work...
if(clsException = pEnv->FindClass("MyExceptionClass")
{
bThrow = true;
break;
}
//do some more native work;

}while(0)

if(bThrow)
{
pEnv->ThrowNew(clsException);
return;
}
///////////////////////////////////////////////////

And behold, the exception was thrown on the Java side!!! hip hip
hurray!!!

That sums it up - JNI has many suttle pit falls, and this is one of
them - so one has to be carful not to fall in it, be careful of
implicit JNI code called after ThrowNew!

Thanks once again for the great help,

Yaron
 
G

Gordon Beaton

You are also correct when you are saying that when returning to the JVM
all local refs are released, but what happens if I allocate a local ref
within a helper function, and then return from it to my native method,
and after that go on doing some more native work? the local refs
allocated inside the helper would not be released until I return from
the native method to the JVM, taking up some precious space!!!

Granted, but in most non-looping code the number of objects created
this way is usually rather limited, and I'd usually let the JVM handle
these things automatically for me.

Still if you want to avoid keeping unused objects around any longer
than necessary, one suggestion is to wrap those helper functions in
calls to PushLocalFrame() and PopLocalFrame(), so you at least don't
need to manage each reference individually.

[...]
I wanted to make my native method thread synchronized, yet release the
monitor upon an excetpion or returning from the method, in order to
prevent a deadlock - I didnt want to return from the method and forget
to call ExitMonitor, so I've written a little guard C++ class:

Have you considered simply declaring the native method synchronized? I
realize that only synchronizes among calls to methods on the same
object, but perhaps that's the case here.
As for the original problem - yes I have solved it, the problem was a
wierd and suttle one:

It was, and good work finding the solution!

/gordon
 
R

Roedy Green

Additionally, the JNI docs state clearly which JNI functions are safe
to call when there is a pending exception (as is the case here), and
DeleteLocalRef() isn't among them:

Odd. That came straight from Sun's textbook.
 

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

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,999
Latest member
MakersCBDGummiesReview

Latest Threads

Top