Skip to content

Commit

Permalink
Move the non blocking reads at the input stream level, #140
Browse files Browse the repository at this point in the history
This commit is currently mostly about using non blocking input streams in the Terminal.
  • Loading branch information
gnodet committed Jan 8, 2018
1 parent a89f820 commit ac77a8a
Show file tree
Hide file tree
Showing 16 changed files with 1,341 additions and 293 deletions.
19 changes: 8 additions & 11 deletions reader/src/test/java/org/jline/reader/impl/LineReaderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,27 @@
*/
package org.jline.reader.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import org.jline.reader.Candidate;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.terminal.impl.DumbTerminal;
import org.jline.utils.AttributedString;
import org.jline.utils.Display;
import org.junit.Ignore;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class LineReaderTest {

@Test(expected = EndOfFileException.class)
@Ignore
public void emptyStringGivesEOFWithJna() throws Exception {
String inputString = "";
InputStream inputStream = new ByteArrayInputStream(inputString.getBytes());
Expand All @@ -52,6 +48,7 @@ public void emptyStringGivesEOFWithJna() throws Exception {
}

@Test(expected = EndOfFileException.class)
@Ignore
public void emptyStringGivesEOFNoJna() throws Exception {
String inputString = "";
InputStream inputStream = new ByteArrayInputStream(inputString.getBytes());
Expand Down
45 changes: 45 additions & 0 deletions terminal/src/main/java/org/jline/terminal/Terminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,51 @@ interface SignalHandler {

OutputStream output();

//
// Input control
//

/**
* Whether this terminal supports {@link #pause()} and {@link #resume()} calls.
* @return whether this terminal supports {@link #pause()} and {@link #resume()} calls.
* @see #paused()
* @see #pause()
* @see #resume()
*/
boolean canPauseResume();

/**
* Stop reading the input stream.
*
* @see #resume()
* @see #paused()
*/
void pause();

/**
* Resume reading the input stream.
*
* @see #pause()
* @see #paused()
*/
void resume();

/**
* Check whether the terminal is currently reading the input stream or not.
* In order to process signal as quickly as possible, the terminal need to read
* the input stream and buffer it internally so that it can detect specific
* characters in the input stream (Ctrl+C, Ctrl+D, etc...) and raise the
* appropriate signals.
* However, there are some cases where this processing should be disabled, for
* example when handing the terminal control to a subprocess.
*
* @return whether the terminal is currently reading the input stream or not
*
* @see #pause()
* @see #resume()
*/
boolean paused();

//
// Pty settings
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ public Attributes enterRawMode() {
Attributes newAttr = new Attributes(prvAttr);
newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.ECHO, LocalFlag.IEXTEN), false);
newAttr.setInputFlags(EnumSet.of(InputFlag.IXON, InputFlag.ICRNL, InputFlag.INLCR), false);
newAttr.setControlChar(ControlChar.VMIN, 1);
newAttr.setControlChar(ControlChar.VTIME, 0);
newAttr.setControlChar(ControlChar.VMIN, 0);
newAttr.setControlChar(ControlChar.VTIME, 1);
setAttributes(newAttr);
return prvAttr;
}
Expand Down Expand Up @@ -204,4 +204,25 @@ public MouseEvent readMouseEvent() {
public MouseEvent readMouseEvent(IntSupplier reader) {
return lastMouseEvent = MouseSupport.readMouse(reader, lastMouseEvent);
}

@Override
public boolean canPauseResume() {
return false;
}

@Override
public void pause() {
throw new UnsupportedOperationException();
}

@Override
public void resume() {
throw new UnsupportedOperationException();
}

@Override
public boolean paused() {
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import org.jline.utils.Curses;
import org.jline.utils.InfoCmp;
import org.jline.utils.Log;
import org.jline.utils.NonBlocking;
import org.jline.utils.NonBlockingInputStream;
import org.jline.utils.NonBlockingPumpReader;
import org.jline.utils.NonBlockingReader;
import org.jline.utils.PumpReader;
import org.jline.utils.ShutdownHooks;
Expand Down Expand Up @@ -58,7 +61,7 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal {
protected static final int ENABLE_QUICK_EDIT_MODE = 0x0040;

protected final Writer slaveInputPipe;
protected final InputStream input;
protected final NonBlockingInputStream input;
protected final OutputStream output;
protected final NonBlockingReader reader;
protected final PrintWriter writer;
Expand All @@ -73,10 +76,10 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal {

public AbstractWindowsTerminal(Writer writer, String name, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler) throws IOException {
super(name, TYPE_WINDOWS, selectCharset(encoding, codepage), signalHandler);
PumpReader reader = new PumpReader();
NonBlockingPumpReader reader = NonBlocking.nonBlockingPumpReader();
this.slaveInputPipe = reader.getWriter();
this.reader = new NonBlockingReader(getName(), reader);
this.input = reader.createInputStream(encoding());
this.reader = reader;
this.input = NonBlocking.nonBlockingStream(reader, encoding());
this.writer = new PrintWriter(writer);
this.output = new WriterOutputStream(writer, encoding());
parseInfoCmp();
Expand Down
28 changes: 8 additions & 20 deletions terminal/src/main/java/org/jline/terminal/impl/DumbTerminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.ControlChar;
import org.jline.terminal.Size;
import org.jline.utils.NonBlocking;
import org.jline.utils.NonBlockingInputStream;
import org.jline.utils.NonBlockingReader;

public class DumbTerminal extends AbstractTerminal {

private final InputStream input;
private final NonBlockingInputStream input;
private final OutputStream output;
private final NonBlockingReader reader;
private final PrintWriter writer;
Expand All @@ -41,11 +43,12 @@ public DumbTerminal(String name, String type, InputStream in, OutputStream out,

public DumbTerminal(String name, String type, InputStream in, OutputStream out, Charset encoding, SignalHandler signalHandler) throws IOException {
super(name, type, encoding, signalHandler);
this.input = new InputStream() {
NonBlockingInputStream nbis = NonBlocking.nonBlocking(getName(), in);
this.input = new NonBlockingInputStream() {
@Override
public int read() throws IOException {
public int read(long timeout, boolean isPeek) throws IOException {
for (;;) {
int c = in.read();
int c = nbis.read(timeout, isPeek);
if (attributes.getLocalFlag(Attributes.LocalFlag.ISIG)) {
if (c == attributes.getControlChar(ControlChar.VINTR)) {
raise(Signal.INT);
Expand Down Expand Up @@ -74,24 +77,9 @@ public int read() throws IOException {
return c;
}
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
return 1;
}
};
this.output = out;
this.reader = new NonBlockingReader(getName(), new InputStreamReader(input, encoding()));
this.reader = NonBlocking.nonBlocking(getName(), input, encoding());
this.writer = new PrintWriter(new OutputStreamWriter(output, encoding()));
this.attributes = new Attributes();
this.attributes.setControlChar(ControlChar.VERASE, (char) 127);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
import org.jline.utils.InputStreamReader;
import org.jline.utils.NonBlocking;
import org.jline.utils.NonBlockingInputStream;
import org.jline.utils.NonBlockingPumpInputStream;
import org.jline.utils.NonBlockingReader;

/**
Expand Down Expand Up @@ -77,7 +80,7 @@ public class LineDisciplineTerminal extends AbstractTerminal {
/*
* Slave streams
*/
protected final InputStream slaveInput;
protected final NonBlockingInputStream slaveInput;
protected final NonBlockingReader slaveReader;
protected final PrintWriter slaveWriter;
protected final OutputStream slaveOutput;
Expand All @@ -101,14 +104,10 @@ public LineDisciplineTerminal(String name,
Charset encoding,
SignalHandler signalHandler) throws IOException {
super(name, type, encoding, signalHandler);
PipedInputStream input = new PipedInputStream(PIPE_SIZE);
this.slaveInputPipe = new PipedOutputStream(input);
// This is a hack to fix a problem in gogo where closure closes
// streams for commands if they are instances of PipedInputStream.
// So we need to get around and make sure it's not an instance of
// that class by using a dumb FilterInputStream class to wrap it.
this.slaveInput = new FilterInputStream(input) {};
this.slaveReader = new NonBlockingReader(getName(), new InputStreamReader(slaveInput, encoding()));
NonBlockingPumpInputStream input = NonBlocking.nonBlockingPumpInputStream(PIPE_SIZE);
this.slaveInputPipe = input.getOutputStream();
this.slaveInput = input;
this.slaveReader = NonBlocking.nonBlocking(getName(), slaveInput, encoding());
this.slaveOutput = new FilteringOutputStream();
this.slaveWriter = new PrintWriter(new OutputStreamWriter(slaveOutput, encoding()));
this.masterOutput = masterOutput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import org.jline.terminal.spi.Pty;
import org.jline.utils.ClosedException;
import org.jline.utils.InputStreamReader;
import org.jline.utils.NonBlocking;
import org.jline.utils.NonBlockingInputStream;
import org.jline.utils.NonBlockingReader;

public class PosixPtyTerminal extends AbstractPosixTerminal {

private final InputStreamWrapper input;
private final NonBlockingInputStream input;
private final OutputStream output;
private final InputStreamReader innerReader;
private final NonBlockingReader reader;
private final PrintWriter writer;
private final Thread inputPumpThread;
Expand All @@ -40,10 +41,9 @@ public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, Outpu
super(name, type, pty, encoding, signalHandler);
Objects.requireNonNull(in);
Objects.requireNonNull(out);
this.input = new InputStreamWrapper(pty.getSlaveInput());
this.input = new InputStreamWrapper(NonBlocking.nonBlocking(name, pty.getSlaveInput()));
this.output = pty.getSlaveOutput();
this.innerReader = new InputStreamReader(input, encoding());
this.reader = new NonBlockingReader(name, innerReader);
this.reader = NonBlocking.nonBlocking(name, input, encoding());
this.writer = new PrintWriter(new OutputStreamWriter(output, encoding()));
this.inputPumpThread = new PumpThread(in, getPty().getMasterOutput());
this.outputPumpThread = new PumpThread(getPty().getMasterInput(), out);
Expand Down Expand Up @@ -74,21 +74,21 @@ public void close() throws IOException {
reader.close();
}

private class InputStreamWrapper extends InputStream {
private class InputStreamWrapper extends NonBlockingInputStream {

private final InputStream in;
private final NonBlockingInputStream in;
private final AtomicBoolean closed = new AtomicBoolean();

protected InputStreamWrapper(InputStream in) {
protected InputStreamWrapper(NonBlockingInputStream in) {
this.in = in;
}

@Override
public int read() throws IOException {
public int read(long timeout, boolean isPeek) throws IOException {
if (closed.get()) {
throw new ClosedException();
}
return in.read();
return in.read(timeout, isPeek);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@
import java.util.HashMap;
import java.util.Map;

import org.jline.utils.NonBlocking;
import org.jline.terminal.spi.Pty;
import org.jline.utils.InputStreamReader;
import org.jline.utils.NonBlockingInputStream;
import org.jline.utils.NonBlockingReader;
import org.jline.utils.ShutdownHooks;
import org.jline.utils.ShutdownHooks.Task;
import org.jline.utils.Signals;

public class PosixSysTerminal extends AbstractPosixTerminal {

protected final InputStream input;
protected final NonBlockingInputStream input;
protected final OutputStream output;
protected final NonBlockingReader reader;
protected final PrintWriter writer;
Expand All @@ -36,9 +37,9 @@ public class PosixSysTerminal extends AbstractPosixTerminal {
public PosixSysTerminal(String name, String type, Pty pty, Charset encoding,
boolean nativeSignals, SignalHandler signalHandler) throws IOException {
super(name, type, pty, encoding, signalHandler);
this.input = pty.getSlaveInput();
this.input = NonBlocking.nonBlocking(getName(), pty.getSlaveInput());
this.output = pty.getSlaveOutput();
this.reader = new NonBlockingReader(getName(), new InputStreamReader(input, encoding()));
this.reader = NonBlocking.nonBlocking(getName(), input, encoding());
this.writer = new PrintWriter(new OutputStreamWriter(output, encoding()));
parseInfoCmp();
if (nativeSignals) {
Expand Down
Loading

0 comments on commit ac77a8a

Please sign in to comment.