Invoking time-consuming JNI task as a thread

I'm having a tough problem with invoking a native function using JNI from a thread.

The native function is legacy code that performs a computation-intensive task. Since I'd like not to freeze the rest of the program, the computation should be performed in a background thread. EventBus is used to send the calculation result back to the main program.

Basically it should be quite simple, something like this:

public class CalculationEngine {
  private CalculationEngine(){}

  public static void calculateInBackground(final Parameters parameters) {

    new Thread(new Runnable() {
      public void run() {
        // Someone might change the parameters while our thread is running, so:
        final Parameters clonedParameters = parameters.clone();
        Results results = new Results();
        natCalc(clonedParameters, results);
        EventBus.publish("Results", results);
      }
    }).start();

  }

  public static void calculateNormally(final Parameters parameters) {
    Results results = new Results();
    natCalc(parameters, results);
    EventBus.publish("Results", results);
  }

  private static native synchronized void
    natCalc(Parameters parameters, Results results);      
}

Now, the calculateNormally method, which blocks the main program, works fine, but the calculateInBackground method, which just constructs a background thread to do the same thing, causes various crashes in the native code when it's invoked consecutively. By consecutively I mean that it's called again only after the previous thread has finished and returned the result. Note that the native code is marked synchronized to ensure that only one instance of it can be running at a time.

My question is, how on earth can the native code behave differently depending on whether it's invoked from the main thread, or from some other thread? It's like the native code were keeping "state", and not really quitting, when it's called from within a thread other than the main thread. Is there a way to "clean" or "flush" a thread after it's finished? There must be something in JNI & Threads that I simply don't know.

Thanks for any hints!

Answers


I figured out a working solution, after googling and finding the phrase "I've found JNI to be very buggy when called from seperate threads... So make sure only one thread ever calls your native code!". It seems to be true; the solution is to keep a persistent, "reusable" thread around - I used Executors.newSingleThreadExecutor() - and to call the native code only from that thread. It works.

So the difference from JNI point of view was not between main thread vs. some other thread, but in using different threads in consecutive calls. Note that in the problematic code a new thread was constructed each time. It should work that way, but it doesn't. (And no, I'm not caching JNIEnv pointer.)

Whether it's a JNI bug, bug in the native code, something in the interaction between them and OS or whatever, would be interesting to know. But sometimes you just have no chance to debug 10000+ lines of existing code in detail, however, you're happy to get it to work. Here's working version of the example code, let's call this a workaround:

public class CalculationEngine {
  private CalculationEngine(){}

  private static Parameters parameters;
  private static ExecutorService executor = Executors.newSingleThreadExecutor();

  private static Runnable analysis = new Runnable() {
      public synchronized void run() {
        Results results = new Results();
        natCalc(parameters, results);
        EventBus.publish("Results", results);
      }
  };  

  public static synchronized void
    calculateInBackground(final Parameters parameters) {
      CalculationEngine.parameters = parameters.clone();
      executor.submit(analysis);
  }

  private static native synchronized void
    natCalc(Parameters parameters, Results results);      
}

My advice on using JNI is DON'T if you can possibly avoid it. The chances are that it will cause stability issues for you. Here are some possible alternatives:

  1. Recode the native library in Java.
  2. Write a wrapper command for the native library in C / C++ / whatever and run it using java.lang.Process and friends
  3. Turn the native library into a daemon and access it using Sockets.

While you've got an answer, I don't think too much has been provided as to possible root cause. Here's a few possibilities, but there are others. Note, these apply to Windows.

There's an apartment threaded COM object involved. Apartment threaded COM objects, which are the only type VB can create, can only be used on the thread that creates them.

Security features, like impersonation, are often thread isolated. If the initialization code modified the context of the thread, future calls that expect the context to be in place will fail.

Thread specific memory storage is a technique within some applications to support multi-threadedness (Java also has such a feature).


Here there is a good documentation about it: Section 8.1 JNI and Threads. http://java.sun.com/docs/books/jni/download/jni.pdf


Need Your Help

OCaml type gtk+ type error view

types gtk ocaml

Down there at the Error you see the type