I made a Java program which calls JNI, and in JNI it creates a "JNICallback" thread and executes in it env-attachCurrentThreadAsDaemon()
My goal is to notify from JNICallback thread, the another pure Java thread.
I do standard things like synchronized -> wait, synchronized -> notifyAll
, using static final Object
as a sync.
If I run it under Eclipse, everything works fine.
But if I export it as a "runnable jar file" and run under same JVM, I see my JNI program invokes monitorEnter, notifyAll, monitorExit
with no errors, but Java doesnt notified.
In the debugger I see different ObjectId's for the very same object used from JNI and from Java, this is suspicious for me (it is names onConnectEvent in the code below)
I even move notification code from JNI to Java, and use a static void method call from JNI, doing synchronize->notifyAll in Java, but this only shows me that waiting thread waits on different ObjectId, so I guess this is the reason.
...
static final Object onConnectEvent = new Object();
...
Thread dogServerConnect,...;
...
public MainClass(){
dogServerConnect = runDog(new ServerConnectionWatchDog(), "onServerConnect");
someJNIstuffPlusNewThreadsCreation();
...
static void onJNICallbackEvent(int type) {
switch (type) {
case 1 -> {
synchronized(onConnectEvent) {
onConnectEvent.notifyAll();
^^^^^ got to this line from JNI, just fine
}
...
class ServerConnectionWatchDog implements Runnable {
@Override
public void run() {
while(true)
synchronized(onConnectEvent) {
try {
onConnectEvent.wait();
for (var l : listeners) {
^^^^^^^^^^^^^^^^^^^^^^^^^ never come to this line
...
again, this is really works well under Eclipse, but for some reason fails as separate jar file. Why could same final static object have a different instance ids for different threads, or it's just fine nowadays?
I mean for the waiting thread Eclipse shows me "waiting for: Object (id=48)" and for the native thread on before "notifyAll" execution it shows me "owns: Object (id=50)". Maybe this is because I manually pause the waiting thread, to see object id (then run it again, this doesn't the cause) What could I miss?
Found the answer in the FAQ for Android: why didn't FindClass find my class. Although my environment is not Android, it still makes sence. The thing is that env->FindClass unwinds a Java stack to find a caller class and use it's classloader. And the Java stack is empty for the native threads which started in JNI. So this gives us an only options either get a global ref to the required classes before creating the native thread, or to get in the same place a reference to it's classloader and then use a FindClass from saved classLoader, which is harder. This solution prevents the Eclipse JarRsrcLoader from creation of duplicate static objects. Probably one might say that doing env->FindClass in newly created native thread in JNI is a bad design, at least use it with caution.