Changed prompt interruption contract

Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
This commit is contained in:
2018-12-01 10:54:51 -05:00
parent c4bbfd8434
commit c206b5b22c
5 changed files with 86 additions and 18 deletions

View File

@@ -94,8 +94,7 @@ public interface ConsoleInput extends AutoCloseable {
/** Indicate to the input that is should interrompt the prompting, if possible.
* <p>
* The pending {@link #prompt()} or {@link #prompt(String)} operations should
* return immediatly. However the returned value can be anything (from the
* partial prompt content to an empty string or even a null pointer). */
* return immediately by throwing an InterruptedIOException. */
void interruptPrompt();
/** Test if the input is closed.

View File

@@ -111,6 +111,7 @@ public final class ReadingRunnable implements Runnable {
private final Map<String, Object> messageBlocker = new ConcurrentHashMap<>();
/** The lock. */
private final Object messageBlockerLock = new Object();
private boolean interrupting = false;
/** Create a reading runnable.
*
* @param reader the input to read from */
@@ -146,8 +147,7 @@ public final class ReadingRunnable implements Runnable {
public String getMessage() throws IOException {
synchronized (lock) {
if (!messages.isEmpty()) {
notifyMessage(messages.peek());
return messages.poll();
return pollMessage();
}
if (!running) {
throw new IOException(CLOSED_PIPE);
@@ -156,8 +156,7 @@ public final class ReadingRunnable implements Runnable {
waitMessage(TIMEOUT);
LOGGER.log(Level.FINEST, "Polled: {0}", messages.peek()); //$NON-NLS-1$
waiting = false;
notifyMessage(messages.peek());
return messages.poll();
return pollMessage();
}
}
@@ -169,8 +168,7 @@ public final class ReadingRunnable implements Runnable {
public String getNextMessage(final long timeout) throws IOException {
synchronized (lock) {
if (!messages.isEmpty()) {
notifyMessage(messages.peek());
return messages.poll();
return pollMessage();
}
if (!running) {
throw new IOException(CLOSED_PIPE);
@@ -181,10 +179,18 @@ public final class ReadingRunnable implements Runnable {
if (messages.isEmpty()) {
return null;
}
notifyMessage(messages.peek());
return messages.poll();
return pollMessage();
}
}
private String pollMessage() throws InterruptedIOException {
final String msg = messages.poll();
if (msg.isEmpty() && interrupting) {
throw new InterruptedIOException();
}
notifyMessage(msg);
return msg;
}
/** Wait for a message to be delivered.
*
@@ -221,7 +227,8 @@ public final class ReadingRunnable implements Runnable {
public void interrupt() {
synchronized (lock) {
if (waiting) {
messages.offer(""); //$NON-NLS-1$
messages.offer("");
interrupting = true;
lock.notifyAll();
}
}

View File

@@ -80,6 +80,7 @@ import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
@@ -90,6 +91,9 @@ import org.junit.Before;
import org.junit.Test;
import net.bigeon.gclc.utils.ReadingRunnable;
import net.bigeon.test.junitmt.FunctionalTestRunnable;
import net.bigeon.test.junitmt.TestFunction;
import net.bigeon.test.junitmt.ThreadTest;
/**
* <p>
@@ -198,9 +202,11 @@ public class ReadingRunnableTest {
public final void testGetPendingMessages() throws IOException, InterruptedException {
final PipedOutputStream out = new PipedOutputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(new PipedInputStream(out), StandardCharsets.UTF_8));
final BufferedReader reader = new BufferedReader(
new InputStreamReader(new PipedInputStream(out), StandardCharsets.UTF_8));
final ReadingRunnable runnable = new ReadingRunnable(reader);
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
final BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(out, StandardCharsets.UTF_8));
writer.write("one");
writer.newLine();
writer.write("two");
@@ -222,4 +228,30 @@ public class ReadingRunnableTest {
// ok
}
}
@Test
public void testInterruption() throws IOException, InterruptedException {
final PipedOutputStream out = new PipedOutputStream();
final BufferedReader reader = new BufferedReader(
new InputStreamReader(new PipedInputStream(out), StandardCharsets.UTF_8));
final ReadingRunnable runnable = new ReadingRunnable(reader);
final FunctionalTestRunnable test = new FunctionalTestRunnable(
new TestFunction() {
@Override
public void apply() throws IOException {
try {
runnable.getMessage();
fail("Message interruption should cause an exception");
} catch (final InterruptedIOException e) {
// ok
}
}
});
final Thread th = new Thread(test, "Prompt wait");
th.start();
th.join(200);
runnable.interrupt();
ThreadTest.assertRuns(th, test);
}
}