Terminate a thread that runs native code

In my application, I have a wrapper over some native code that gets called through the JNI bridge. This native code must be executed in a separate thread (parallel processing). However, the problem is that the code sometimes freezes, so the thread must be stopped by force. Unfortunately, I did not find any “delicate” method for this: the general advice is to tell the code in the stream in order to exit gracefully, but I cannot do it with this native code (which is the third batch code, all of the above )

I am using the Java Concurrent API to submit a task:

Future<Integer> processFuture = taskExecutor.submit(callable); try { result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue(); } catch (TimeoutException e) { // How to kill the thread here? throw new ExecutionTimeoutException("Execution timed out (max " + this.executionTimeout / 60 + "min)"); } catch (...) { ... exception handling for other cases } 

Future#cancel() will abort the thread only, but it will not terminate it. So I used the following trick:

 class DestroyableCallable implements Callable<Integer> { private Thread workerThread; @Override public Integer call() { workerThread = Thread.currentThread(); return Integer.valueOf(JniBridge.process(...)); } public void stopWorkerThread() { if (workerThread != null) { workerThread.stop(); } } } DestroyableCallable callable = new DestroyableCallable(); Future<Integer> processFuture = taskExecutor.submit(callable); try { result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue(); } catch (TimeoutException e) { processFuture.cancel(true); // Dirty: callable.stopWorkerThread(); ThreadPoolTaskExecutor threadPoolTaskExecutor = (ThreadPoolTaskExecutor) taskExecutor; logger.debug("poolSize: " + threadPoolTaskExecutor.getPoolSize() + ", maxPoolSize:" + threadPoolTaskExecutor.getMaxPoolSize() + ", activeCount:" + threadPoolTaskExecutor.getActiveCount()); } throw new ...; } catch (...) { ... exception handling for other cases } 

Questions / issues with this code:

  • Is this generally the right way to do this? Any other more elegant alternatives?
  • activeCount on the task executor does not decrease, so the task executor still "thinks" that the thread is working
  • I had to add workerThread != null check to the stopWorkerThread() method, as in some cases this variable turned out to be null . I can’t understand what these things are ...

Notes:

  • Internal code does not use file descriptors (sockets). Everything is passed to him as a data block and returned in the same way.
  • The source code has processor intensity. Although it guarantees a shutdown, it can take a long time.

Editing Bounty . The approach / suggestion to revise your own code is clear, please do not offer it in your answer. I need a pure-java solution / workaround.

+6
source share
5 answers

Java has clean options for terminating a forced thread. This is an ancient and obsolete Thread.stop () only (AFAIK). And there is no way to safely terminate the stream (why .stop () was deprecated and allowed to not even be implemented by the JVM developers).

The reason is that all threads within the application share memory and resources - therefore, if you forcefully terminate a thread at some arbitrary point, you cannot be sure that the completed thread did not leave some shared memory / resources in an inconsistent state. And you can't even (generally) guess what resources are (possibly) dirty ("because you don’t know exactly at what point the thread was stopped).

So, if you want some threads of your application to be able to interrupt, the only solution is to provide at the design stage some notation for “savepoints” - locations in the target code of the stream that are guaranteed not to mutate the general state, so it’s safe for the stream to exit here . And this is exactly what javaadocs Thread.stop () says: the only way to safely interrupt a thread is to develop the code for the thread so that it can respond to some kind of interrupt request. Some kind of flag that is checked by the thread from time to time.

I am trying to tell you: you cannot do what you are asked about using java threading / concurrency. The way I can offer you (this was given earlier) is to do my work in a separate process. The process of forced destruction is much safer than a thread, since 1) the processes are much more separated from each other and 2) the OS takes care of many cleanings after the process is completed. The killing process is not completely safe, because there is some kind of resource (for example, files), which by default is not cleared by the OS, but in your case it seems safe.

Thus, you are developing a small standalone application (possibly even in java) if your third-party library does not provide other bindings or even in a shell script), and only the task should do the calculation for you. You start such a process from the main application, give it the task and start the watchdog timer. This watchdog detects a timeout - it kills the process forcibly.

This is the only draft decision. You can implement some kind of process pool if you want to increase productivity (starting a process can take time), etc ...

+9
source

You can transfer this single call to the JNI method to a separate Java application, and then deploy another java process using java.lang.Process . You can then call Process.destroy() to kill the process at the OS level.

Depending on your environment and other considerations, you may need to do some tricks to find out how to find the java executable, especially if you are creating some kind of redistributable software that can be run on different platforms. Another problem will be IPC for you, but it can be done using process I / O streams.

+2
source

Since you are dealing with third-party code, I would suggest creating your own shell application that handles calls, tracking and terminating these threads. Better yet, ask this third party to do this for you if your license agreement offers any support.

http://java.sun.com/docs/books/jni/html/other.html

+1
source

Definitely the ugly hack you have here ...

First of all, thread pool threads are not designed for individual quenching and, as a rule, they should be left until completion, especially not stopped with Thread.stop() , which is not recommended even for regular threads.

Using Thread.stop() , as I said, is never encouraged and usually leaves the thread in an inconsistent state, which is probably the reason that the thread pool does not see the thread as dead. It may not even kill him at all.

Any idea why the native code is hanging? I think the root of your problem is here, not part of the flow stop. Threads should work properly until completion, when possible. Perhaps you can find a better implementation that works correctly (or implement something else if you wrote it).

Change As for point 3, you probably need to declare the link to the current thread as volatile , because you assign it in one thread and read it in another:

 private volatile Thread workerThread; 

Change 2 . I am starting to realize that your JNI code only performs numerical calculations and does not open any descriptors that may remain in an inconsistent state if the thread is suddenly killed. Can you confirm this?

In this case, let me go against my own advice and tell you that in this case you can safely kill the thread with Thread.stop() . However, I recommend that you use a separate thread instead of a thread pool thread to avoid leaving the thread pool in an inconsistent state (as you mentioned, it does not see the thread as dead). It is also more practical because you don’t have to do all these tricks to stop a thread, because you can just call stop() directly on it from the main thread, unlike thread threads.

+1
source

I will not repeat all the valuable advice given by Tudor .... I will simply add an alternative architectural point for you, using any method related to silence, to handle the connection between your main Java application and running your own thread .... This thread can to be a client of the broker and be notified if any special events occur (termination) and acts as a result (termination of long work) Of course, this adds some complexity, but it is a rather elegant solution. Of course, if the native thread is not reliable, it will not change anything for all reliability. One way to handle the connection between your own thread and the broker is to use the STOMP interface (many Apache activemq, MQ brokers from Oracle expose such an interface) ...

NTN Jerome

0
source

Source: https://habr.com/ru/post/905331/


All Articles