Fixed issues with reading from System.in and blocking reads.

This commit is contained in:
nathan 2017-11-28 21:07:11 +01:00
parent d9b565edde
commit ee968be141
1 changed files with 30 additions and 91 deletions

View File

@ -15,40 +15,34 @@
*/
package dorkbox.executor;
import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import dorkbox.console.Console;
import dorkbox.console.input.Terminal;
public
class ProcessProxy extends Thread {
class ProcessStreamProxy extends Thread {
private final InputStream is;
private final OutputStream os;
private final boolean isSystemIn;
private final CountDownLatch startUpLatch = new CountDownLatch(1);
private final CountDownLatch shutDownLatch = new CountDownLatch(1);
// when reading from the stdin and outputting to the process
public
ProcessProxy(String processName, InputStream inputStreamFromConsole, OutputStream outputStreamToProcess) {
boolean isSystemIn = false;
ProcessStreamProxy(String processName, InputStream inputStreamFromConsole, OutputStream outputStreamToProcess) {
// basic check to see if we are System.in
if (inputStreamFromConsole.equals(System.in)) {
// more exact check: basically unwrap everything and see if it's a FileInputStream
// more exact check: basically unwrap everything and see if it's a FileInputStream (which it should be)
try {
Field in = FilterInputStream.class.getDeclaredField("in");
in.setAccessible(true);
@ -59,17 +53,14 @@ class ProcessProxy extends Thread {
unwrapped = in.get(unwrapped);
}
isSystemIn = unwrapped instanceof FileInputStream;
if (isSystemIn) {
inputStreamFromConsole = (InputStream) unwrapped;
if (unwrapped instanceof FileInputStream && ((FileInputStream) unwrapped).getFD().equals(FileDescriptor.in)) {
// if we are actually System.in, we want to use the Console.in INSTEAD, because it will let us do things we could otherwise not do.
inputStreamFromConsole = Console.inputStream();
}
} catch (Exception ignored) {
}
}
// if we are actually System.in, we want to use the Console.in INSTEAD, because it will let us do things we could otherwise not do.
this.isSystemIn = isSystemIn;
this.is = inputStreamFromConsole;
this.os = outputStreamToProcess;
@ -95,9 +86,14 @@ class ProcessProxy extends Thread {
public
void close() {
// this.interrupt();
running.set(false);
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
shutDownLatch.await();
} catch (InterruptedException e) {
@ -105,101 +101,44 @@ class ProcessProxy extends Thread {
}
}
@SuppressWarnings("Duplicates")
@Override
public
void run() {
// if we are system in, we can ONLY read the line input, unless the Console project is present!
if (isSystemIn) {
}
// we rely on buferredReader.ready(), so that we can know if there is input or not (and read/block/etc if necessary)
final BufferedReader reader = new BufferedReader(new InputStreamReader(this.is));
Terminal in = Console.in();
running.set(true);
final OutputStream os = this.os;
// final BufferedReader reader = this.reader;
final long timeout = 200L;
startUpLatch.countDown();
final InputStream is = this.is;
final OutputStream os = this.os;
int readInt;
try {
// this thread will read until there is no more data to read. (this is generally what you want)
// the stream will be closed when the process closes it (usually on exit)
int readInt;
if (os == null) {
while (!reader.ready()) {
Thread.sleep(timeout);
if (!running.get()) {
if (isSystemIn) {
System.err.println("DONE sysin " + this);
// should attempt to process anything more.
return;
}
// should process whatever is left.
System.err.println("DONE a " + this);
break;
}
//noinspection StatementWithEmptyBody
while (is.read() != -1 && running.get()) {
// just read so it won't block or backup.
}
// just read so it won't block.
reader.readLine();
}
else {
while (running.get()) {
try {
while (!reader.ready()) {
Thread.sleep(timeout);
while ((readInt = is.read()) != -1 && running.get()) {
os.write(readInt);
if (!running.get()) {
if (isSystemIn) {
System.err.println("DONE sysin " + this);
// should attempt to process anything more.
return;
}
// flush the output on new line. (same for both windows '\r\n' and linux '\n')
if (readInt == '\n') {
os.flush();
// should process whatever is left.
System.err.println("DONE a " + this);
break;
}
}
} catch (InterruptedException ignored) {
}
while ((readInt = reader.read()) != -1) {
System.err.println(".");
os.write(readInt);
// flush the output on new line. (same for both windows '\r\n' and linux '\n')
if (readInt == '\n') {
os.flush();
synchronized (os) {
os.notifyAll();
}
synchronized (os) {
os.notifyAll();
}
}
}
}
} catch (Exception ignore) {
ignore.printStackTrace();
} catch (Exception ignored) {
} finally {
System.err.println("DONE c " + this);
try {
// this.reader.close();
if (os != null) {