Skip to content →

C++ callbacks into Java via JNI made easy(ier)

I just finished writing a Java wrapper for a complex C++ library, and had a fair bit of trouble finding solutions to (or even basic documentation pertaining to) some of the problems that came up.

Things seem pretty easy if you just need to call some C++ functions from Java: you use JNI and do a little gruntwork to translate data to and from Java objects, or let SWIG do the heavy lifting for you. However, in my case things were harder, because the library is multithreaded (using boost threads) and makes heavy use of callbacks into the client (i.e., Java).

The main complication is that each native thread that calls a Java method must do so through its own JNIEnv pointer, obtained by calling AttachCurrentThread on a JavaVM instance. Each such native thread (which may be created and managed by code you can’t change) must eventually be detached from the JVM by calling DetachCurrentThread within it, or it seems memory leaks and JVM shutdown issues can result. There is no obvious way to detach the thread from the outside, so if you let the thread die or get away before it calls DetachCurrentThread you’re stuck.

Since a C++ wrapper for the Java callbacks is needed anyway, one easy solution is to attach before each callback and detach afterwards. However, this may be very expensive; I haven’t measured the cost, but one post I found quoted a factor of 6 slowdown. My alternative solution uses boost::thread_specific_ptr to detach each native thread just before it dies:

#include <boost/thread/tss.hpp>
 
static JavaVM *vm = NULL;
 
class ThreadJNIEnv {
public:
    bool _detach;
    JNIEnv *env;
    ThreadJNIEnv() {
        cout << "Attaching " << boost::this_thread::get_id() << endl;
        vm->AttachCurrentThread((void **) &env, NULL);
        ASSERT(env != NULL);
        _detach = true;
    }
 
    ThreadJNIEnv(JNIEnv *e) {
        env = e;
        _detach = false;
    }
 
    ~ThreadJNIEnv() {
        if (_detach) {
            cout << "Detaching " << boost::this_thread::get_id() << endl;
            vm->DetachCurrentThread();
        }
    }
};
 
static boost::thread_specific_ptr<ThreadJNIEnv> envs;
 
JNIEnv *getJNIEnv(){
    ThreadJNIEnv *tenv = envs.get();
    if (tenv == NULL) {
        tenv = new ThreadJNIEnv();
        envs.reset(tenv);
    }
    return tenv->env;
}
 
bool init(JNIEnv *env) {
   if (!env->GetJavaVM(&vm) < 0) return false;
   envs.reset(new ThreadJNIEnv(env));
   return true;
}

If your main function is in Java, you initialize the library by calling init(env) with the current thread’s JNIEnv (with multiple Java threads, you may have to take care). If it’s C++, you just set “vm” directly when you create the JVM instance.

Then, any function not in a direct JNI call just uses getJNIEnv() to get a JNIEnv pointer that’s valid for the current thread. This will work correctly both for indirect calls from Java, and from native threads, which will be automatically attached on the first call to getJNIEnv and automatically detached before death.

Seemed to work for me on Ubuntu; YMMV.

Published in Java Programming

11 Comments

  1. Oty

    Hi,

    I had the same library. But after N call to java from C++ thread.
    application crashed!

    have you a idea.

  2. Timothy Wall

    You should only detach if not already attached (pseudo-code follows):

    attached = (*jvm)->GetEnv(…)
    if (!attached) (*jvm)->AttachCrrentThread(…)

    // do java callback

    if (!attached) (*jvm)->DetachCurrentThread(jvm)

Leave a Reply

Your email address will not be published. Required fields are marked *