Reputation: 402
I'm trying to abort an already blocking InputStream.read() by closing the stream, but read() does not return.
public static void main(String args[]) throws Exception
{
ProcessBuilder builder = new ProcessBuilder("sleep", "100000");
Process process = builder.start();
final InputStream is = process.getInputStream();
Thread worker = new Thread() {
public void run() {
try {
int data;
while ((data = is.read()) != -1) {
System.out.println("Read:" + data);
}
} catch(IOException e) {
e.printStackTrace();
}
}
};
worker.start();
Thread.sleep(2000);
// process.destroy();
is.close();
worker.join();
}
Is there some way to abort the already blocking read() by closing the stream, without invoking process.destroy()?
The background to this question is a complex eclipse plugin that most of the time will abort a blocking BufferedReader.readLine() by calling process.destroy(), but sometimes readLine() is not aborted. I suspect some race with the JVM internal ProcessReaper thread and I'm trying to working around that by closing the stream explicitly.
I'm using Linux and JDK 1.7.0_25-b15.
Upvotes: 2
Views: 2597
Reputation: 21
I had a similar problem which I solved by checking a number of available bytes before reading from the stream. Modifying your example:
public static void main(String args[]) throws Exception
{
ProcessBuilder builder = new ProcessBuilder("sleep", "100000");
Process process = builder.start();
final InputStream is = process.getInputStream();
final AtomicBoolean stopReading = new AtomicBoolean(false);
final long CHECK_INTERVAL = 100; // msec.
Thread worker = new Thread() {
public void run() {
try {
workerLoop:
while (true) {
int availableBytes = is.available(); // this call should be non-blocking
if (availableBytes > 0) {
byte[] buffer = new byte[availableBytes];
int c = is.read(buffer); // this call should be non-blocking
for (int i = 0; i < c; i++) {
if (buffer[i] == -1) // standard check for the end of stream
{
break workerLoop;
}
// do something with the data....
}
} else if (stopReading.get()) {
break;
} else {
Thread.sleep(CHECK_INTERVAL);
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
};
worker.start();
Thread.sleep(2000);
stopReading.set(true); // request to terminate the thread
worker.join();
is.close();
}
It is possible to modify the code to interrupt the worker thread instead of using a boolean flag.
I'm using Windows 10 and Oracle Java 8.
Upvotes: 2
Reputation: 1494
I've seen something similar and in my case it turned out that every time when a java thread would block, it was because the child process has given the file descriptor to its own child, which was still alive after Process.destroy()
was called. So, schematically it looks like this:
java thread --> child process --> grandchild process
I actually did go and check the source code of Process.destroy()
for Windows and Linux platforms, and it turned out a kill 9
command is executed. If it was to kill a grandchild, a kill -9
command would be necessary. I am not aware of any good way to solve this issue without using JNI, but since you write for Eclipse, you can try luck with CDT Spawner class instead of java.lang.Runtime
. I recall it provided some features beyond Runtime, but don't recall if it could solve this issue. This class is for example used to launch and debug native processes by Eclipse CDT environment.
Bug JDK-4770092 provides an explanation why it is difficult to kill grandchild processes on Windows. As far as I understand, Sun/Oracle decided to provide the lowest common denominator, and that's why no grandchildren are killed on Unix either.
On a second thought, if I was wrong and Process.destroy() sends a SIGTERM, the following shell script should isolate all subprocesses from java, and would work both on Linux and in Cygwin on Windows.
#!/bin/sh
# Initialize variables
program="$1"; shift
child_pid=0
# Register a signal handler procedure
signalHandler() {
echo "Sending SIGKILL to process $child_pid and all its children"
[0 = $child_pid] || kill -9 -$child_pid
echo "Removing temporary folder: $tmp_folder"
rm -fr "$tmp_folder"
}
trap signalHandler 0 1 2 3 15
# Create a temporary folder
tmp_folder=$(mktemp -d --quiet)
# Launch a child process in background
program "$@" >$(tmp_folder)/somefile 2>&1 &
# Remember the subprocess id
child_pid=$!
# Make tail exit when the subprocess exits
# Maybe, this is a bad idea. I haven’t tested it
# If it doesn't work, one can read from file with Java NIO
tail --follow --pid=$child_pid $(tmp_folder)/somefile
Upvotes: 1
Reputation: 11114
Old question, new answer. The NuProcess library implements non-blocking I/O for processes, so you can avoid this situation altogether.
Disclaimer: I am the author of NuProcess.
Upvotes: 2
Reputation: 3767
I guess I can't say for sure in Java 1.7, but I have done this in Java 1.6. You need 3 threads:
The reading thread should update a reference shared between it and the monitor thread, but more importantly, the monitor needs to have a reference to the reading thread. The reading thread updates a time value on every read. The monitor sleeps for a set time, and each time it wakes up, it checks that time stamp. If the difference between the previous time and current time exceeds some value, the monitor forces an interrupt on the reading thread and then stops itself as well.
This could conceivably be done with only two threads -- the Main and the Monitor, where the main thread will be doing the reading and does monitor.setReaderThread(Thread.currentThread()); monitorThread.start()
. But I have only done this with three threads.
Upvotes: -1
Reputation: 718718
You tried close()
and it doesn't work. The only other thing I can think of to try is calling Thread.interrupt()
on the blocked thread ... but I doubt that that will work either.
The real issue is that the specs (i.e. respective javadocs) don't say whether these things will work. Even if they do work ... for some version of Java on some OS platform ... there is no guarantee that they will work on other version/platform combinations.
Upvotes: 1