Call JNI multitask task as a thread

I have a difficult problem calling my own function using JNI from a thread.

A native function is legacy code that performs a task requiring computation. Since I do not want to freeze the rest of the program, the calculation should be done in the background thread. EventBus is used to send the calculation result to the main program.

Basically it should be pretty 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 simply creates a background thread to do the same, causes various crashes in its own code when it is called sequentially, I call it again again only after the previous thread completed and returned the result. Note that native code is marked synchronized to ensure that only one instance can work at a time.

My question is, how can the native code actually behave differently depending on whether it is called from the main thread or from some other thread? It, like its own code, retained a “state” and did not stop when it was called from a thread other than the main thread. Is there a way to “clean” or “flush” a stream after it is completed? There should be something in JNI and Threads that I just don't know.

Thanks for any tips!

+4
source share
4 answers

I figured out a working solution after searching on Google and found the phrase "I found that JNI works very poorly when called from separate threads ... So make sure that only one thread ever calls your own code!" . It seems to be true; the solution is to maintain a constant "reusable" thread around - I used Executors.newSingleThreadExecutor() - and call native code only from that thread. He works.

Thus, the difference from the JNI point of view was not between the main thread and another thread, but when using different threads in consecutive calls. Note that a new thread was created in the problematic code each time. It should work this way, but it is not. (And no, I do not cache the JNIEnv pointer.)

It would be interesting to know if this will be a JNI error, an error in the native code, something in the interaction between them and the OS, or something else. But sometimes you just don’t have the ability to debug more than 10,000 lines of existing code, however you are happy to make it work. Here's a working version of the sample code, let this 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); } 
+7
source

My advice on using JNI MUST NOT, if you can avoid it. Most likely, this will cause stability problems for you. Here are a few possible alternatives:

  • Recode your own library in Java.
  • Write a shell command for your own library in C / C ++ / whatever and run it using java.lang.Process and friends
  • Turn your own library into a daemon and access it using sockets.
+3
source

As long as you have the answer, I don't think too much has been provided regarding the possible root cause. There are several possibilities, but there are others. Please note that this applies to Windows.

An object with a COM thread is involved. Threaded COM objects that can only be created for VB can only be used in the thread that creates them.

Security features, such as impersonation, are often isolated from threads. If the initialization code changed the context of the thread, future calls that expect the context to be in place will fail.

Backing up data on a particular type is a method in some applications to support multithreading (Java also has this capability).

+1
source

There is good documentation here: Section 8.1 of JNI and Threads. http://java.sun.com/docs/books/jni/download/jni.pdf

+1
source

All Articles