refactor connection and disconnection
This commit is contained in:
parent
37aa04b262
commit
ad17a4c148
12 changed files with 161 additions and 223 deletions
|
@ -5,9 +5,11 @@ import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -288,7 +290,7 @@ final class RpcState<VatId> {
|
||||||
startMessageLoop();
|
startMessageLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<java.lang.Void> getMessageLoop() {
|
CompletableFuture<java.lang.Void> onDisconnection() {
|
||||||
return this.messageLoop;
|
return this.messageLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,6 +365,12 @@ final class RpcState<VatId> {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (ioExc instanceof CompletionException) {
|
||||||
|
var compExc = (CompletionException)ioExc;
|
||||||
|
if (compExc.getCause() instanceof ClosedChannelException) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CompletableFuture.failedFuture(ioExc);
|
return CompletableFuture.failedFuture(ioExc);
|
||||||
});
|
});
|
||||||
|
@ -371,9 +379,7 @@ final class RpcState<VatId> {
|
||||||
this.disconnectFulfiller.complete(new DisconnectInfo(shutdownPromise));
|
this.disconnectFulfiller.complete(new DisconnectInfo(shutdownPromise));
|
||||||
|
|
||||||
for (var pipeline: pipelinesToRelease) {
|
for (var pipeline: pipelinesToRelease) {
|
||||||
if (pipeline instanceof RpcState<?>.RpcPipeline) {
|
pipeline.cancel(networkExc);
|
||||||
((RpcPipeline) pipeline).redirectLater.completeExceptionally(networkExc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,8 +1562,8 @@ final class RpcState<VatId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void cancel(Throwable exc) {
|
||||||
this.question.finish();
|
this.question.reject(exc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,21 @@
|
||||||
package org.capnproto;
|
package org.capnproto;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class RpcSystem<VatId extends StructReader> {
|
public class RpcSystem<VatId extends StructReader> {
|
||||||
|
|
||||||
private final VatNetwork<VatId> network;
|
private final VatNetwork<VatId> network;
|
||||||
private final BootstrapFactory<VatId> bootstrapFactory;
|
private final BootstrapFactory<VatId> bootstrapFactory;
|
||||||
private final Map<VatNetwork.Connection<VatId>, RpcState<VatId>> connections = new HashMap<>();
|
private final Map<VatNetwork.Connection<VatId>, RpcState<VatId>> connections = new ConcurrentHashMap<>();
|
||||||
private final CompletableFuture<java.lang.Void> messageLoop;
|
|
||||||
private final CompletableFuture<java.lang.Void> acceptLoop;
|
|
||||||
|
|
||||||
public RpcSystem(VatNetwork<VatId> network) {
|
public RpcSystem(VatNetwork<VatId> network) {
|
||||||
this.network = network;
|
this(network, (BootstrapFactory)null);
|
||||||
this.bootstrapFactory = null;
|
|
||||||
this.acceptLoop = new CompletableFuture<>();
|
|
||||||
this.messageLoop = doMessageLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public VatNetwork<VatId> getNetwork() {
|
|
||||||
return this.network;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RpcSystem(VatNetwork<VatId> network,
|
public RpcSystem(VatNetwork<VatId> network,
|
||||||
|
@ -49,8 +43,7 @@ public class RpcSystem<VatId extends StructReader> {
|
||||||
BootstrapFactory<VatId> bootstrapFactory) {
|
BootstrapFactory<VatId> bootstrapFactory) {
|
||||||
this.network = network;
|
this.network = network;
|
||||||
this.bootstrapFactory = bootstrapFactory;
|
this.bootstrapFactory = bootstrapFactory;
|
||||||
this.acceptLoop = doAcceptLoop();
|
this.startAcceptLoop();
|
||||||
this.messageLoop = doMessageLoop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Capability.Client bootstrap(VatId vatId) {
|
public Capability.Client bootstrap(VatId vatId) {
|
||||||
|
@ -68,21 +61,19 @@ public class RpcSystem<VatId extends StructReader> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RpcState<VatId> getConnectionState(VatNetwork.Connection<VatId> connection) {
|
public VatNetwork<VatId> getNetwork() {
|
||||||
var state = this.connections.get(connection);
|
return this.network;
|
||||||
if (state == null) {
|
|
||||||
var onDisconnect = new CompletableFuture<RpcState.DisconnectInfo>()
|
|
||||||
.whenComplete((info, exc) -> {
|
|
||||||
this.connections.remove(connection);
|
|
||||||
try {
|
|
||||||
connection.close();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
state = new RpcState<>(this.bootstrapFactory, connection, onDisconnect);
|
RpcState<VatId> getConnectionState(VatNetwork.Connection<VatId> connection) {
|
||||||
this.connections.put(connection, state);
|
var state = this.connections.computeIfAbsent(connection, conn -> {
|
||||||
}
|
var onDisconnect = new CompletableFuture<RpcState.DisconnectInfo>();
|
||||||
|
onDisconnect.thenCompose(info -> {
|
||||||
|
this.connections.remove(connection);
|
||||||
|
return info.shutdownPromise.thenRun(() -> connection.close());
|
||||||
|
});
|
||||||
|
return new RpcState<>(this.bootstrapFactory, conn, onDisconnect);
|
||||||
|
});
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,27 +81,10 @@ public class RpcSystem<VatId extends StructReader> {
|
||||||
getConnectionState(connection);
|
getConnectionState(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<java.lang.Void> doAcceptLoop() {
|
private void startAcceptLoop() {
|
||||||
return this.network.baseAccept().thenCompose(connection -> {
|
this.network.baseAccept()
|
||||||
this.accept(connection);
|
.thenAccept(this::accept)
|
||||||
return this.doAcceptLoop();
|
.thenRunAsync(this::startAcceptLoop);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<java.lang.Void> doMessageLoop() {
|
|
||||||
var accept = this.getAcceptLoop();
|
|
||||||
for (var conn: this.connections.values()) {
|
|
||||||
accept = accept.acceptEither(conn.getMessageLoop(), x -> {});
|
|
||||||
}
|
|
||||||
return accept.thenCompose(x -> this.doMessageLoop());
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<java.lang.Void> getMessageLoop() {
|
|
||||||
return this.messageLoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<java.lang.Void> getAcceptLoop() {
|
|
||||||
return this.acceptLoop;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <VatId extends StructReader>
|
public static <VatId extends StructReader>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.capnproto;
|
package org.capnproto;
|
||||||
|
|
||||||
import java.nio.channels.AsynchronousSocketChannel;
|
import java.nio.channels.AsynchronousByteChannel;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class TwoPartyClient {
|
public class TwoPartyClient {
|
||||||
|
@ -8,15 +8,15 @@ public class TwoPartyClient {
|
||||||
private final TwoPartyVatNetwork network;
|
private final TwoPartyVatNetwork network;
|
||||||
private final RpcSystem<RpcTwoPartyProtocol.VatId.Reader> rpcSystem;
|
private final RpcSystem<RpcTwoPartyProtocol.VatId.Reader> rpcSystem;
|
||||||
|
|
||||||
public TwoPartyClient(AsynchronousSocketChannel channel) {
|
public TwoPartyClient(AsynchronousByteChannel channel) {
|
||||||
this(channel, null);
|
this(channel, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TwoPartyClient(AsynchronousSocketChannel channel, Capability.Client bootstrapInterface) {
|
public TwoPartyClient(AsynchronousByteChannel channel, Capability.Client bootstrapInterface) {
|
||||||
this(channel, bootstrapInterface, RpcTwoPartyProtocol.Side.CLIENT);
|
this(channel, bootstrapInterface, RpcTwoPartyProtocol.Side.CLIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TwoPartyClient(AsynchronousSocketChannel channel,
|
public TwoPartyClient(AsynchronousByteChannel channel,
|
||||||
Capability.Client bootstrapInterface,
|
Capability.Client bootstrapInterface,
|
||||||
RpcTwoPartyProtocol.Side side) {
|
RpcTwoPartyProtocol.Side side) {
|
||||||
this.network = new TwoPartyVatNetwork(channel, side);
|
this.network = new TwoPartyVatNetwork(channel, side);
|
||||||
|
@ -31,4 +31,8 @@ public class TwoPartyClient {
|
||||||
: RpcTwoPartyProtocol.Side.CLIENT);
|
: RpcTwoPartyProtocol.Side.CLIENT);
|
||||||
return rpcSystem.bootstrap(vatId.asReader());
|
return rpcSystem.bootstrap(vatId.asReader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompletableFuture<java.lang.Void> onDisconnect() {
|
||||||
|
return this.network.onDisconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,135 +10,80 @@ import java.util.concurrent.CompletableFuture;
|
||||||
public class TwoPartyServer {
|
public class TwoPartyServer {
|
||||||
|
|
||||||
private class AcceptedConnection {
|
private class AcceptedConnection {
|
||||||
final AsynchronousSocketChannel channel;
|
final AsynchronousSocketChannel connection;
|
||||||
final TwoPartyVatNetwork network;
|
final TwoPartyVatNetwork network;
|
||||||
final RpcSystem<RpcTwoPartyProtocol.VatId.Reader> rpcSystem;
|
final RpcSystem<RpcTwoPartyProtocol.VatId.Reader> rpcSystem;
|
||||||
private final CompletableFuture<?> messageLoop;
|
|
||||||
|
|
||||||
AcceptedConnection(Capability.Client bootstrapInterface, AsynchronousSocketChannel channel) {
|
AcceptedConnection(Capability.Client bootstrapInterface, AsynchronousSocketChannel connection) {
|
||||||
this.channel = channel;
|
this.connection = connection;
|
||||||
this.network = new TwoPartyVatNetwork(channel, RpcTwoPartyProtocol.Side.SERVER);
|
this.network = new TwoPartyVatNetwork(this.connection, RpcTwoPartyProtocol.Side.SERVER);
|
||||||
this.rpcSystem = new RpcSystem<>(network, bootstrapInterface);
|
this.rpcSystem = new RpcSystem<>(network, bootstrapInterface);
|
||||||
this.messageLoop = this.rpcSystem.getMessageLoop().exceptionally(exc -> {
|
|
||||||
connections.remove(this);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<?> getMessageLoop() {
|
|
||||||
return this.messageLoop;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectionReceiver {
|
class ConnectionReceiver {
|
||||||
AsynchronousServerSocketChannel listener;
|
final AsynchronousServerSocketChannel listener;
|
||||||
final CompletableFuture<?> messageLoop;
|
|
||||||
public ConnectionReceiver(AsynchronousServerSocketChannel listener) {
|
ConnectionReceiver(AsynchronousServerSocketChannel listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.messageLoop = doMessageLoop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<?> getMessageLoop() {
|
CompletableFuture<AsynchronousSocketChannel> accept() {
|
||||||
return this.messageLoop;
|
CompletableFuture<AsynchronousSocketChannel> result = new CompletableFuture<>();
|
||||||
}
|
this.listener.accept(null, new CompletionHandler<>() {
|
||||||
|
|
||||||
private CompletableFuture<?> doMessageLoop() {
|
|
||||||
final var accepted = new CompletableFuture<AsynchronousSocketChannel>();
|
|
||||||
listener.accept(null, new CompletionHandler<>() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void completed(AsynchronousSocketChannel channel, Object attachment) {
|
public void completed(AsynchronousSocketChannel channel, Object attachment) {
|
||||||
accepted.complete(channel);
|
result.complete(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable exc, Object attachment) {
|
public void failed(Throwable exc, Object attachment) {
|
||||||
accepted.completeExceptionally(exc);
|
result.completeExceptionally(exc);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return accepted.thenCompose(channel -> CompletableFuture.allOf(
|
return result.copy();
|
||||||
accept(channel),
|
|
||||||
doMessageLoop()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Capability.Client bootstrapInterface;
|
private final Capability.Client bootstrapInterface;
|
||||||
private final List<AcceptedConnection> connections = new ArrayList<>();
|
private final List<AcceptedConnection> connections = new ArrayList<>();
|
||||||
private final List<ConnectionReceiver> listeners = new ArrayList<>();
|
|
||||||
private final CompletableFuture<?> messageLoop;
|
|
||||||
|
|
||||||
public TwoPartyServer(Capability.Client bootstrapInterface) {
|
public TwoPartyServer(Capability.Client bootstrapInterface) {
|
||||||
this.bootstrapInterface = bootstrapInterface;
|
this.bootstrapInterface = bootstrapInterface;
|
||||||
this.messageLoop = doMessageLoop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TwoPartyServer(Capability.Server bootstrapServer) {
|
public TwoPartyServer(Capability.Server bootstrapServer) {
|
||||||
this(new Capability.Client(bootstrapServer));
|
this(new Capability.Client(bootstrapServer));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<?> getMessageLoop() {
|
public void accept(AsynchronousSocketChannel channel) {
|
||||||
return this.messageLoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<?> drain() {
|
|
||||||
CompletableFuture<java.lang.Void> done = new CompletableFuture<>();
|
|
||||||
for (var conn: this.connections) {
|
|
||||||
done = CompletableFuture.allOf(done, conn.getMessageLoop());
|
|
||||||
}
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<java.lang.Void> accept(AsynchronousSocketChannel channel) {
|
|
||||||
var connection = new AcceptedConnection(this.bootstrapInterface, channel);
|
var connection = new AcceptedConnection(this.bootstrapInterface, channel);
|
||||||
this.connections.add(connection);
|
this.connections.add(connection);
|
||||||
return connection.network.onDisconnect().whenComplete((x, exc) -> {
|
connection.network.onDisconnect().whenComplete((x, exc) -> {
|
||||||
this.connections.remove(connection);
|
this.connections.remove(connection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
private final CompletableFuture<?> acceptLoop(AsynchronousServerSocketChannel listener) {
|
|
||||||
final var accepted = new CompletableFuture<AsynchronousSocketChannel>();
|
|
||||||
listener.accept(null, new CompletionHandler<>() {
|
|
||||||
|
|
||||||
@Override
|
public CompletableFuture<java.lang.Void> listen(AsynchronousServerSocketChannel listener) {
|
||||||
public void completed(AsynchronousSocketChannel channel, Object attachment) {
|
return this.listen(wrapListenSocket(listener));
|
||||||
accepted.complete(channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
CompletableFuture<java.lang.Void> listen(ConnectionReceiver listener) {
|
||||||
public void failed(Throwable exc, Object attachment) {
|
return listener.accept().thenCompose(channel -> {
|
||||||
accepted.completeExceptionally(exc);
|
this.accept(channel);
|
||||||
}
|
return this.listen(listener);
|
||||||
});
|
});
|
||||||
return accepted.thenCompose(channel -> CompletableFuture.anyOf(
|
|
||||||
accept(channel),
|
|
||||||
acceptLoop(listener)));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
public CompletableFuture<?> listen(AsynchronousServerSocketChannel listener) {
|
|
||||||
var receiver = new ConnectionReceiver(listener);
|
|
||||||
this.listeners.add(receiver);
|
|
||||||
return receiver.getMessageLoop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<?> doMessageLoop() {
|
CompletableFuture<java.lang.Void> drain() {
|
||||||
var done = new CompletableFuture<>();
|
CompletableFuture<java.lang.Void> loop = CompletableFuture.completedFuture(null);
|
||||||
for (var conn: this.connections) {
|
for (var conn: this.connections) {
|
||||||
done = CompletableFuture.anyOf(done, conn.getMessageLoop());
|
loop = CompletableFuture.allOf(loop, conn.network.onDisconnect());
|
||||||
}
|
}
|
||||||
for (var listener: this.listeners) {
|
return loop;
|
||||||
done = CompletableFuture.anyOf(done, listener.getMessageLoop());
|
|
||||||
}
|
|
||||||
return done.thenCompose(x -> doMessageLoop());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
ConnectionReceiver wrapListenSocket(AsynchronousServerSocketChannel channel) {
|
||||||
public CompletableFuture<?> runOnce() {
|
return new ConnectionReceiver(channel);
|
||||||
var done = new CompletableFuture<>();
|
|
||||||
for (var conn: connections) {
|
|
||||||
done = CompletableFuture.anyOf(done, conn.runOnce());
|
|
||||||
}
|
}
|
||||||
return done;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,15 @@ public class TwoPartyVatNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() {
|
||||||
|
try {
|
||||||
this.channel.close();
|
this.channel.close();
|
||||||
this.disconnectPromise.complete(null);
|
this.disconnectPromise.complete(null);
|
||||||
}
|
}
|
||||||
|
catch (Exception exc) {
|
||||||
|
this.disconnectPromise.completeExceptionally(exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public RpcTwoPartyProtocol.Side getSide() {
|
public RpcTwoPartyProtocol.Side getSide() {
|
||||||
return side;
|
return side;
|
||||||
|
@ -113,13 +118,13 @@ public class TwoPartyVatNetwork
|
||||||
public CompletableFuture<java.lang.Void> shutdown() {
|
public CompletableFuture<java.lang.Void> shutdown() {
|
||||||
assert this.previousWrite != null: "Already shut down";
|
assert this.previousWrite != null: "Already shut down";
|
||||||
|
|
||||||
var result = this.previousWrite.thenRun(() -> {
|
var result = this.previousWrite.whenComplete((void_, exc) -> {
|
||||||
try {
|
try {
|
||||||
if (this.channel instanceof AsynchronousSocketChannel) {
|
if (this.channel instanceof AsynchronousSocketChannel) {
|
||||||
((AsynchronousSocketChannel)this.channel).shutdownOutput();
|
((AsynchronousSocketChannel)this.channel).shutdownOutput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ioExc) {
|
catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ public interface VatNetwork<VatId>
|
||||||
CompletableFuture<IncomingRpcMessage> receiveIncomingMessage();
|
CompletableFuture<IncomingRpcMessage> receiveIncomingMessage();
|
||||||
CompletableFuture<java.lang.Void> shutdown();
|
CompletableFuture<java.lang.Void> shutdown();
|
||||||
VatId getPeerVatId();
|
VatId getPeerVatId();
|
||||||
void close() throws IOException;
|
void close();
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<Connection<VatId>> baseAccept();
|
CompletableFuture<Connection<VatId>> baseAccept();
|
||||||
|
|
|
@ -4,78 +4,78 @@ import org.capnproto.rpctest.*;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.function.ThrowingRunnable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.AsynchronousByteChannel;
|
||||||
import java.nio.channels.AsynchronousServerSocketChannel;
|
import java.nio.channels.AsynchronousServerSocketChannel;
|
||||||
import java.nio.channels.AsynchronousSocketChannel;
|
import java.nio.channels.AsynchronousSocketChannel;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class TwoPartyTest {
|
public class TwoPartyTest {
|
||||||
|
|
||||||
private Thread runServer(org.capnproto.TwoPartyVatNetwork network) {
|
static final class PipeThread {
|
||||||
var thread = new Thread(() -> {
|
Thread thread;
|
||||||
|
AsynchronousByteChannel channel;
|
||||||
|
|
||||||
|
static PipeThread newPipeThread(Consumer<AsynchronousByteChannel> startFunc) throws Exception {
|
||||||
|
var pipeThread = new PipeThread();
|
||||||
|
var serverAcceptSocket = AsynchronousServerSocketChannel.open();
|
||||||
|
serverAcceptSocket.bind(null);
|
||||||
|
var clientSocket = AsynchronousSocketChannel.open();
|
||||||
|
|
||||||
|
pipeThread.thread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
network.onDisconnect().get();
|
var serverSocket = serverAcceptSocket.accept().get();
|
||||||
} catch (InterruptedException e) {
|
startFunc.accept(serverSocket);
|
||||||
e.printStackTrace();
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
} catch (ExecutionException e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}, "Server");
|
});
|
||||||
|
pipeThread.thread.start();
|
||||||
|
pipeThread.thread.setName("TwoPartyTest server");
|
||||||
|
|
||||||
thread.start();
|
clientSocket.connect(serverAcceptSocket.getLocalAddress()).get();
|
||||||
return thread;
|
pipeThread.channel = clientSocket;
|
||||||
|
return pipeThread;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsynchronousServerSocketChannel serverAcceptSocket;
|
PipeThread runServer(Capability.Server bootstrapInterface) throws Exception {
|
||||||
AsynchronousSocketChannel serverSocket;
|
return runServer(new Capability.Client(bootstrapInterface));
|
||||||
AsynchronousSocketChannel clientSocket;
|
}
|
||||||
TwoPartyClient client;
|
|
||||||
org.capnproto.TwoPartyVatNetwork serverNetwork;
|
PipeThread runServer(Capability.Client bootstrapInterface) throws Exception {
|
||||||
Thread serverThread;
|
return PipeThread.newPipeThread(channel -> {
|
||||||
|
var network = new TwoPartyVatNetwork(channel, RpcTwoPartyProtocol.Side.SERVER);
|
||||||
|
var system = new RpcSystem<>(network, bootstrapInterface);
|
||||||
|
network.onDisconnect().join();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
this.serverAcceptSocket = AsynchronousServerSocketChannel.open();
|
|
||||||
this.serverAcceptSocket.bind(null);
|
|
||||||
|
|
||||||
this.clientSocket = AsynchronousSocketChannel.open();
|
|
||||||
this.clientSocket.connect(this.serverAcceptSocket.getLocalAddress()).get();
|
|
||||||
this.client = new TwoPartyClient(clientSocket);
|
|
||||||
//this.client.getNetwork().setTap(new Tap());
|
|
||||||
|
|
||||||
this.serverSocket = serverAcceptSocket.accept().get();
|
|
||||||
this.serverNetwork = new org.capnproto.TwoPartyVatNetwork(this.serverSocket, RpcTwoPartyProtocol.Side.SERVER);
|
|
||||||
//this.serverNetwork.setTap(new Tap());
|
|
||||||
//this.serverNetwork.dumper.addSchema(Demo.TestCap1);
|
|
||||||
this.serverThread = runServer(this.serverNetwork);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() {
|
||||||
this.clientSocket.close();
|
|
||||||
this.serverSocket.close();
|
|
||||||
this.serverAcceptSocket.close();
|
|
||||||
this.serverThread.join();
|
|
||||||
this.client = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.Test
|
@org.junit.Test
|
||||||
public void testNullCap() throws ExecutionException, InterruptedException {
|
public void testNullCap() throws Exception {
|
||||||
var server = new RpcSystem<>(this.serverNetwork, new Capability.Client());
|
var pipe = runServer(new Capability.Client());
|
||||||
var cap = this.client.bootstrap();
|
var rpcClient = new TwoPartyClient(pipe.channel);
|
||||||
var resolved = cap.whenResolved();
|
var client = rpcClient.bootstrap();
|
||||||
|
var resolved = client.whenResolved();
|
||||||
resolved.get();
|
resolved.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.Test
|
@org.junit.Test
|
||||||
public void testBasic() throws InterruptedException, IOException {
|
public void testBasic() throws Exception {
|
||||||
|
|
||||||
var callCount = new Counter();
|
var callCount = new Counter();
|
||||||
var server = new RpcSystem<>(this.serverNetwork, new RpcTestUtil.TestInterfaceImpl(callCount));
|
var pipe = runServer(new RpcTestUtil.TestInterfaceImpl(callCount));
|
||||||
|
var rpcClient = new TwoPartyClient(pipe.channel);
|
||||||
var client = new Test.TestInterface.Client(this.client.bootstrap());
|
var client = new Test.TestInterface.Client(rpcClient.bootstrap());
|
||||||
var request1 = client.fooRequest();
|
var request1 = client.fooRequest();
|
||||||
request1.getParams().setI(123);
|
request1.getParams().setI(123);
|
||||||
request1.getParams().setJ(true);
|
request1.getParams().setJ(true);
|
||||||
|
@ -99,24 +99,22 @@ public class TwoPartyTest {
|
||||||
promise3.join();
|
promise3.join();
|
||||||
|
|
||||||
Assert.assertEquals(2, callCount.value());
|
Assert.assertEquals(2, callCount.value());
|
||||||
this.clientSocket.shutdownOutput();
|
|
||||||
serverThread.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.Test
|
@org.junit.Test
|
||||||
public void testDisconnect() throws IOException {
|
public void testDisconnect() throws IOException {
|
||||||
this.serverSocket.shutdownOutput();
|
//this.serverSocket.shutdownOutput();
|
||||||
this.serverNetwork.close();
|
//this.serverNetwork.close();
|
||||||
this.serverNetwork.onDisconnect().join();
|
//this.serverNetwork.onDisconnect().join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.Test
|
@org.junit.Test
|
||||||
public void testPipelining() throws IOException {
|
public void testPipelining() throws Exception {
|
||||||
var callCount = new Counter();
|
var callCount = new Counter();
|
||||||
var chainedCallCount = new Counter();
|
var chainedCallCount = new Counter();
|
||||||
|
var pipe = runServer(new RpcTestUtil.TestPipelineImpl(callCount));
|
||||||
var server = new RpcSystem<>(this.serverNetwork, new RpcTestUtil.TestPipelineImpl(callCount));
|
var rpcClient = new TwoPartyClient(pipe.channel);
|
||||||
var client = new Test.TestPipeline.Client(this.client.bootstrap());
|
var client = new Test.TestPipeline.Client(rpcClient.bootstrap());
|
||||||
|
|
||||||
{
|
{
|
||||||
var request = client.getCapRequest();
|
var request = client.getCapRequest();
|
||||||
|
@ -146,11 +144,9 @@ public class TwoPartyTest {
|
||||||
Assert.assertEquals(1, chainedCallCount.value());
|
Assert.assertEquals(1, chainedCallCount.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// disconnect the client
|
||||||
// disconnect the server
|
((AsynchronousSocketChannel)pipe.channel).shutdownOutput();
|
||||||
//this.serverSocket.shutdownOutput();
|
rpcClient.onDisconnect().join();
|
||||||
this.serverNetwork.close();
|
|
||||||
this.serverNetwork.onDisconnect().join();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Use the now-broken capability.
|
// Use the now-broken capability.
|
||||||
|
@ -173,8 +169,11 @@ public class TwoPartyTest {
|
||||||
Assert.assertEquals(3, callCount.value());
|
Assert.assertEquals(3, callCount.value());
|
||||||
Assert.assertEquals(1, chainedCallCount.value());
|
Assert.assertEquals(1, chainedCallCount.value());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.junit.Test
|
||||||
|
public void testAbort() {
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@org.junit.Test
|
@org.junit.Test
|
||||||
|
|
|
@ -167,6 +167,11 @@ public final class AnyPointer {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(Throwable exc) {
|
||||||
|
this.hook.cancel(exc);
|
||||||
|
}
|
||||||
|
|
||||||
public Pipeline noop() {
|
public Pipeline noop() {
|
||||||
return new Pipeline(this.hook, this.ops.clone());
|
return new Pipeline(this.hook, this.ops.clone());
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,11 +388,6 @@ public final class Capability {
|
||||||
public final ClientHook getPipelinedCap(PipelineOp[] ops) {
|
public final ClientHook getPipelinedCap(PipelineOp[] ops) {
|
||||||
return this.results.getPipelinedCap(ops);
|
return this.results.getPipelinedCap(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
this.ctx.allowCancellation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LocalResponse implements ResponseHook {
|
private static final class LocalResponse implements ResponseHook {
|
||||||
|
@ -542,7 +537,7 @@ public final class Capability {
|
||||||
: new QueuedClient(this.promise.thenApply(
|
: new QueuedClient(this.promise.thenApply(
|
||||||
pipeline -> pipeline.getPipelinedCap(ops)));
|
pipeline -> pipeline.getPipelinedCap(ops)));
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (this.redirect != null) {
|
if (this.redirect != null) {
|
||||||
|
@ -552,6 +547,7 @@ public final class Capability {
|
||||||
this.promise.cancel(false);
|
this.promise.cancel(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ClientHook which simply queues calls while waiting for a ClientHook to which to forward them.
|
// A ClientHook which simply queues calls while waiting for a ClientHook to which to forward them.
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package org.capnproto;
|
package org.capnproto;
|
||||||
|
|
||||||
public interface Pipeline {
|
public interface Pipeline {
|
||||||
|
|
||||||
AnyPointer.Pipeline typelessPipeline();
|
AnyPointer.Pipeline typelessPipeline();
|
||||||
|
|
||||||
|
default void cancel(Throwable exc) {
|
||||||
|
this.typelessPipeline().cancel(exc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
package org.capnproto;
|
package org.capnproto;
|
||||||
|
|
||||||
public interface PipelineHook extends AutoCloseable {
|
public interface PipelineHook {
|
||||||
|
|
||||||
ClientHook getPipelinedCap(PipelineOp[] ops);
|
ClientHook getPipelinedCap(PipelineOp[] ops);
|
||||||
|
|
||||||
|
default void cancel(Throwable exc) {
|
||||||
|
}
|
||||||
|
|
||||||
static PipelineHook newBrokenPipeline(Throwable exc) {
|
static PipelineHook newBrokenPipeline(Throwable exc) {
|
||||||
return ops -> Capability.newBrokenCap(exc);
|
return ops -> Capability.newBrokenCap(exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default void close() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ public class RemotePromise<Results>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() {
|
||||||
this.pipeline.hook.close();
|
this.pipeline.cancel(RpcException.failed("Cancelled"));
|
||||||
this.join();
|
this.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue