tags:

views:

652

answers:

1

I have just started at a new job. Here we are new to using JNI ( for bridging C++ / Java ). I am new to JNI so please forgive my noobness :)

In our (win32) Java app we are loading a C++ DLL. On the Java side we have several instances of "SomeJClass" each of these instances needs access to corresponding instance of "SomeCClass" on the DLL side. The DLL exposes entry-points such as GlobalDoSomethingInC(). Here I must call the instance method of Doer::DoSomethingInC(). So I need a smooth way to map the respective this-pointers. I also need to do the same mapping when a DLL thread discovers something interesting that it needs to notify the corresponding Java-instance of.

I can think of several solutions, but I do not like them too much. My question is, is there a better way than this ?

1 Java calls C:GetNewInstance(). This returns an int that is actually a pointer to the new C instance. Java stores it in m_myCInstance. Then Java calls GlobalDoSomethingInC(), and 1a

// DLL global
void GlobalDoSomethingInC()
{
    // retrive this pointer
    //calling back to Java:  
    jobj tmpJ = NewGlobalRef( env, obj );
    Doer* myDoer = <reinterpret_cast>( Doer )tmpJ->GetMyCInstance();
    myDoer->DoSomething();
    DeleteGlobalRef( env, tmpJ );
    // Arrrrgh
}

1b or:

    // for **every call** that Java adds a parameter, 
    //which is the stored int:m_myCInstance, and
    Doer* myDoer = <reinterpret_cast>( Doer )instanceParam->DoSomethingInC();
    // Can we do better that this?

2 For calling from C to Java, things look, maybe, better

In the constructor C calls back into Java and stores
the Java instance reference 
    in a member variable. m_myJInstance.
    In all subsequent calls m_myJInstance can be used to call back Java.
    In the destructor we need to call DeleteGlobalRef( env, m_myJInstance );

Not too bad I suppose. But it really safe to store the jobject reference. I mean: What happens when the GC moves the object around?

3 Our present solution does "work". But it belongs on rather on http://www.codinghorror.com/blog/ :)

Thanx

+1  A: 

Typically this will depend on your environment somewhat. I've only used KNI, which is even more primitive than JNI. I think a fair bit of ugliness is unavoidable, as you're mixing memory tracking across two systems, only one of which has GC.

In general, I found it best to wrap all of the calls out the C code in functions that took care of the nasty casting, which I think is unavoidable. (BTW, I'll use C to mean non-Java code here)

On the C side, movement of Java objects is definitely a potential problem. It will depend on your platform, but I would expect that as long as you are within the lib, you can expect no Java GC to occur, so your objects are stable. YOU NEED TO BE SURE OF THIS. On the other hand, if it's not the case, you're pretty much screwed. Assuming it is the case, you want to do the same thing of isolating dereferencing/casting to the function that's exposed to JNI, so that you can happily work with normal C objects in all of your called functions.

Where it can get really ugly is if you can have objects go out of scope on either side, as then potentially either side can be holding a reference to your object. Here we used finalizers on the Java side, as well as destructors on the C side. It wasn't pretty, but I think that what somewhat unavoidable.

So, short answer, it will be somewhat ugly, isolate the ugliness around the interface between the two languages, so that for the bulk of the work, in either language, you don't have to worry about such things.

It's also worth having a base class for objects that exist over this interface, as here you can also isolate some ugliness.

Ben