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