Added interruption flag

Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
This commit is contained in:
Emmanuel Bigeon 2018-12-02 13:33:15 -05:00
parent 79ee5394d4
commit 901469792d
6 changed files with 92 additions and 44 deletions

View File

@ -49,7 +49,7 @@
<dependency>
<groupId>net.bigeon</groupId>
<artifactId>gclc</artifactId>
<version>2.0.12</version>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>net.bigeon.test</groupId>

View File

@ -68,6 +68,7 @@ package net.bigeon.gclc.process.io;
* #L%
*/
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.function.Supplier;
import net.bigeon.gclc.manager.ConsoleInput;
@ -82,25 +83,27 @@ public final class ConnectingConsoleInput implements ConsoleInput {
private static final ConstantString EMPTY_STRING = new ConstantString("");
/** If the input is closed. */
private boolean close = false;
private boolean close = false;
/** The prompt string. */
private Supplier<String> prompt = EMPTY_STRING;
/** If the input is currently in prompting state.
* <p>
* To change it you should be in a promptLock. */
private boolean prompting = false;
private boolean prompting = false;
/** The synchronization lock for the prompting status. */
private final Object promptLock = new Object();
private final Object promptLock = new Object();
/** The synchronization lock for the connection status. */
private final Object connectionLock = new Object();
private final Object connectionLock = new Object();
/** The connected console input.
* <p>
* To use it, you should be in a promptLock and connectionLock. */
private ConsoleInput connected = null;
private ConsoleInput connected = null;
/** The connection state.
* <p>
* To read or modify it, you should be in a connectionLock synchronize block. */
private boolean disconnection = false;
private boolean disconnection = false;
private boolean interrupting = false;
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleInput#close() */
@ -149,6 +152,9 @@ public final class ConnectingConsoleInput implements ConsoleInput {
public void interruptPrompt() {
synchronized (connectionLock) {
synchronized (promptLock) {
if (prompting) {
interrupting = true;
}
connectionLock.notifyAll();
if (connected != null) {
connected.interruptPrompt();
@ -201,14 +207,21 @@ public final class ConnectingConsoleInput implements ConsoleInput {
actualConnected = connected;
}
if (connect) {
final String res = actualConnected.prompt(message);
synchronized (promptLock) {
try {
final String res = actualConnected.prompt(message);
synchronized (promptLock) {
if (prompting) {
return res;
}
}
} catch (final InterruptedIOException e) {
// The inner console was interrupted. This can mean we are
// disconnecting or actually interrupted.
if (disconnection) {
disconnection = false;
} else if (prompting) {
return res;
} else {
// prompt interrupted, lose the result.
interrupting = false;
throw e;
}
}
}
@ -241,14 +254,21 @@ public final class ConnectingConsoleInput implements ConsoleInput {
}
if (connect) {
synchronized (promptLock) {
final String res = actualConnected.prompt(message,
end - System.currentTimeMillis());
if (disconnection) {
disconnection = false;
} else if (prompting) {
return res;
} else {
// prompt interrupted, lose the result.
try {
final String res = actualConnected.prompt(message,
end - System.currentTimeMillis());
if (prompting) {
return res;
}
} catch (final InterruptedIOException e) {
// The inner console was interrupted. This can mean we are
// disconnecting or actually interrupted.
if (disconnection) {
disconnection = false;
} else {
throw e;
}
}
}
}
@ -257,7 +277,7 @@ public final class ConnectingConsoleInput implements ConsoleInput {
}
private void getConnection(final long timeout) {
private void getConnection(final long timeout) throws InterruptedIOException {
boolean connect;
synchronized (connectionLock) {
connect = connected != null;
@ -268,6 +288,10 @@ public final class ConnectingConsoleInput implements ConsoleInput {
Thread.currentThread().interrupt();
}
}
if (interrupting) {
interrupting = false;
throw new InterruptedIOException("Prompt ws interrupted");
}
}
}

View File

@ -12,14 +12,16 @@ import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.utils.PipedConsoleInput;
import net.bigeon.gclc.utils.PipedConsoleOutput;
import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.manager.PipedConsoleOutput;
/** @author Emmanuel Bigeon */
public class ForkTaskTest {
@Test
public void testGenericForkTask() throws IOException {
// FIXME This test fail to complete on Jenkins.
final ForkTask task = new ForkTask(5) {
@Override
@ -36,15 +38,11 @@ public class ForkTaskTest {
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
"Unable to prompt user");
}
if ("ok".equals(msg)) {
out.println("Message");
} else {
out.println("fail");
}
out.println(msg);
}
};
final Thread execThread = new Thread(task);
final Thread execThread = new Thread(task, "Task");
execThread.start();
try {
execThread.join(100);
@ -55,11 +53,11 @@ public class ForkTaskTest {
try (PipedConsoleOutput pco = new PipedConsoleOutput();
PipedConsoleInput pci = new PipedConsoleInput(null)) {
pci.type("ok");
while (!pco.available()) {
pci.type("ok");
task.join(pco, pci, 1000);
}
assertEquals("Execution should work", "Message", pco.readNextLine());
assertEquals("Positive execution", "ok", pco.readNextLine());
}
assertFalse("Running state should be updated by task on its completion",
task.isRunning());

View File

@ -14,8 +14,8 @@ import org.junit.Test;
import net.bigeon.gclc.exception.CommandRunException;
import net.bigeon.gclc.exception.CommandRunExceptionType;
import net.bigeon.gclc.manager.PipedConsoleOutput;
import net.bigeon.gclc.process.mocks.TaskMock;
import net.bigeon.gclc.utils.PipedConsoleOutput;
/** @author Emmanuel Bigeon */
public class ProcessListTest {

View File

@ -4,8 +4,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
@ -13,9 +15,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import net.bigeon.gclc.manager.PipedConsoleInput;
import net.bigeon.gclc.manager.StreamConsoleInput;
import net.bigeon.gclc.tools.ConstantString;
import net.bigeon.gclc.utils.PipedConsoleInput;
import net.bigeon.gclc.utils.StreamConsoleInput;
import net.bigeon.test.junitmt.ATestRunnable;
import net.bigeon.test.junitmt.FunctionalTestRunnable;
import net.bigeon.test.junitmt.TestFunction;
@ -81,10 +83,26 @@ public class ConnectingConsoleInputTest {
@Override
public void apply() throws Exception {
assertNull("Interrupted should return null", in.prompt("m1", -1));
assertNull("Interrupted should return null", in.prompt("m2", 5000));
ended.set(true);
assertNull("Overtime should return null", in.prompt("m3", 200));
try {
in.prompt("m1", -1);
fail("interruption of infinite waiting prompt should cause error");
} catch (final InterruptedIOException e) {
// ok
}
try {
in.prompt("m2", 5000);
fail("interruption of finite waiting prompt should cause error");
} catch (final InterruptedIOException e) {
// ok
}
synchronized (ended) {
ended.set(true);
try {
assertNull("Overtime should return null", in.prompt("m3", 200));
} catch (final InterruptedIOException e) {
fail("Unexpected interruption error in overtime");
}
}
}
};
final ATestRunnable runnable = new FunctionalTestRunnable(one);
@ -95,12 +113,16 @@ public class ConnectingConsoleInputTest {
public void run() {
while (!ended.get()) {
try {
th.join(500);
th.join(100);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
in.interruptPrompt();
synchronized (ended) {
if (!ended.get()) {
in.interruptPrompt();
}
}
}
}
});
@ -112,7 +134,7 @@ public class ConnectingConsoleInputTest {
final PipedOutputStream os = new PipedOutputStream();
final PipedInputStream pis = new PipedInputStream(os);
in.connect(new StreamConsoleInput(System.out, pis, StandardCharsets.UTF_8));
in.connect(new StreamConsoleInput(null, pis, StandardCharsets.UTF_8));
final ATestRunnable runnable2 = new FunctionalTestRunnable(one);
final Thread th2 = new Thread(runnable2);
final Thread inter2 = new Thread(new Runnable() {
@ -121,12 +143,16 @@ public class ConnectingConsoleInputTest {
public void run() {
while (!ended.get()) {
try {
th2.join(500);
th2.join(100);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
in.interruptPrompt();
synchronized (ended) {
if (!ended.get()) {
in.interruptPrompt();
}
}
}
}
});

View File

@ -8,7 +8,7 @@ import java.io.IOException;
import org.junit.Test;
import net.bigeon.gclc.utils.PipedConsoleOutput;
import net.bigeon.gclc.manager.PipedConsoleOutput;
public class ConnectingConsoleOutputTest {