1

I have a gobbler that reads the output from a Process.

There is a case where we kill the process programatically using its PID and the external Windows taskkill command.

It is a 16-Bit DOS process

We taskkill because it is a DOS 16-bit process and Process.destroyForcibly() does not work with it because it resides in the ntvdm subsystem and the best way is to get the PID and use 'taskkill /T /F' which does indeed kill it and any children.

Normally, we have no problem with our DOS 16-bit (or 32 bit) processes. This one has some file locks in place. It is especially important that we ensure it is dead to have the OS release the locks.

We close all streams before and after the kill

Prior to calling taskkill, we attempt to flush and close all streams in an executor: in,out,err. After calling taskkill, we verify that all streams are closed by re-closing them.

We call Thread.interrupt() on all gobblers after the kill

Now, after the kill success, which is confirmed in the OS as well, the gobbler is still running and it does not respond to Thread.interrupt().

We even do a last-ditch Thread.stop (gasp!)

And furthermore, we have invoked Thread.stop() on it and it still stays waiting at the read stage ...

So, it seems, we are unable to stop the std-out and std-in gobblers on our Processes streams.

We know Thread.stop() is deprecated. To be somewhat safe, we catch ThreadDeath then clean any monitors and then rethrow ThreadDeath. However, ThreadDeath never in fact gets thrown and the thread just keeps on waiting on inputStream.read .. so Thread.stop being deprecated in this case is a moot point ... because it does not do anything. Just so no one flames me and so that I have a clean conscience, We have removed Thread.stop() from our production code.

I am not surprised that the Thread does not interrupt since that only happens on some InputStreams and not all reads are incorruptible. But I am surprised that the Thread will not stop when Thread.stop is invoked.

Thread trace shows

A thread trace shows that both main-in and main-er (the two outputs from the process) are still running even after the streams are closed, thread is interrupted and last ditch Thread.stop is called.

The task is dead, so why care about idle blocked gobblers?

It is not that we care that the gobblers won't quit. But we hate threads running that just pile up and clog the system. This particular process is called by a webserver and then .. it could amount to several hundred idle threads in a blocking state on dead processes...

We have tried launching the process two ways with no difference ...

run(working, "cmd", "/c", "start", "/B", "/W", "/SEPARATE", "C:\\workspace\\dotest.exe");

run(working, "cmd", "/c", "C:\\workspace\\dotest.exe");

The gobbler is in a read like this:

try (final InputStream is = inputStream instanceof BufferedInputStream
                ? inputStream : new BufferedInputStream(inputStream, 1024 * 64);
                final BufferedReader br = new BufferedReader(new InputStreamReader(is, charset))) {

            String line;
            while ((line = br.readLine()) != null) {
                lineCount++;
                lines.add(line);
                if (Thread.interrupted()) {
                    Thread.currentThread().interrupt();
                    throw new InterruptedException();
                }
            }

            eofFound = true;   
        }

Our destroyer calls this on the gobbler thread after the taskkill:

int timeLimit = 500;
t.interrupt();
try {
    t.join(timeLimit);
    if (t.isAlive()) {
        t.stop();
        // we knows it's deprecated but we catch ThreadDeath
        // then clean any monitors and then rethrow ThreadDeath
        // But ThreadDeath never in fact gets thrown and the thread 
        // just keeps on waiting on inputStream.read .. 
        logger.warn("Thread stopped because it did not interrupt within {}ms: {}", timeLimit, t);
        if (t.isAlive()) {
            logger.warn("But thread is still alive! {}", t);
        }
    }
} catch (InterruptedException ie) {
    logger.info("Interrupted exception while waiting on join({}) with {}", timeLimit, t, ie);
} 

This is a snippet of the log output:

59.841 [main] INFO  Destroying process '5952'

04.863 [main] WARN  Timeout waiting for 'Close java.io.BufferedInputStream@193932a' to finish
09.865 [main] WARN  Timeout waiting for 'Close java.io.FileInputStream@159f197' to finish

09.941 [main] DEBUG Executing [taskkill, /F, /PID, 5952].
10.243 [Thread-1] DEBUG SUCCESS: The process with PID 5952 has been terminated.
10.249 [main] DEBUG java.lang.ProcessImpl@620197 stopped with exit code 0
10.638 [main] INFO  Destroyed WindowsProcess(5952) forcefully in 738 ms.

11.188 [main] WARN  Thread stop called because it did not interrupt within 500ms: Thread[main-in,5,main]
11.188 [main] WARN  But thread is still alive! Thread[main-in,5,main]

11.689 [main] WARN  Thread stop because it did not interrupt within 500ms: Thread[main-err,5,main]
11.689 [main] WARN  But thread is still alive! Thread[main-err,5,main]

Note: prior to calling taskkill, the Process std-out and std-err will not close. But they are closed manually after the taskkill (not shown in log because success).

The Coordinator
  • 13,007
  • 11
  • 44
  • 73
  • 1
    Thread.stop() is deprecated and for good reason. Please include the code for listen() method in your snippet. But most likely your problem is using uninterruptable I/O, check out http://stackoverflow.com/questions/8494763/any-way-of-using-java-nio-to-interrupt-a-inputstreamread-without-closing-so on making inputstream interruptable. – Pawel Veselov May 24 '15 at 08:00
  • Thread trace shows that it is stuck on the read. Listen just adds the line to an ArrayList. I'll add that to make it clear. Thread.stop is just a last ditch effort to see what might work. – The Coordinator May 24 '15 at 08:12
  • @PawelVeselov The link you gave only applies to SocketInputStream. Not all InputStreams can be interrupted. Process InputStream does not seem to be -- at least not this one. Fact, the stream is closed and even that does not stop the current read. – The Coordinator May 24 '15 at 08:15
  • This should not be necessary. If the process has exited there is nothing for the reads to block on. There is something wrong with your observations. Where are the first two WARN messages in your log printed from? – user207421 May 24 '15 at 08:29
  • @EJP We wish that was the case. You want a logmein session and you can see it yourself. The Two reader theads are stuck in read, the streams are closed and the thread is interrupted and stopped. Yuppers. Indeed. BTW - I have been working with DOS, Windows and Java for years doing systems programming. All logging for stream closing shows success. So, the question is, is Process InputStream interruptible? If so, does taskkill negate that ability? Sorry, this one is a doozer or I would not post it here. – The Coordinator May 24 '15 at 08:35
  • Process.inputStream() is not interruptible, or else Thread.interrupt() would have interrupted it. taskkill wouldn't affect an ability of JDK to determine the other end of the stream just died. This is probably Java/Windows/NTVDM bug. If this is vital, create a Windows process that pipes and joins DOS process. – Pawel Veselov May 24 '15 at 08:39
  • @PawelVeselov "This is probably Java/Windows/NTVDM bug" That seems the case. Unfortunately I have no experience creating Windows process that pipe and joins DOS process. – The Coordinator May 24 '15 at 08:44

0 Answers0