From d3e639eaee142ff3f15c6714079b43f2f0e9c04b Mon Sep 17 00:00:00 2001 From: Vaci Koblizek Date: Thu, 19 Nov 2020 18:34:21 +0000 Subject: [PATCH] add capability tail call test --- .../java/org/capnproto/CapabilityTest.java | 36 ++++++++++++++++++- .../main/java/org/capnproto/Capability.java | 10 +++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/runtime-rpc/src/test/java/org/capnproto/CapabilityTest.java b/runtime-rpc/src/test/java/org/capnproto/CapabilityTest.java index df32650..23e005f 100644 --- a/runtime-rpc/src/test/java/org/capnproto/CapabilityTest.java +++ b/runtime-rpc/src/test/java/org/capnproto/CapabilityTest.java @@ -66,7 +66,7 @@ class TestExtendsImpl extends Test.TestExtends2.Server { } } -public class CapabilityTest { +public final class CapabilityTest { @org.junit.Test public void testBasic() { @@ -157,6 +157,40 @@ public class CapabilityTest { Assert.assertEquals(1, chainedCallCount.value()); } + @org.junit.Test + public void testTailCall() { + var calleeCallCount = new Counter(); + var callerCallCount = new Counter(); + var callee = new Test.TestTailCallee.Client( + new RpcTestUtil.TestTailCalleeImpl(calleeCallCount)); + + var caller = new Test.TestTailCaller.Client( + new RpcTestUtil.TestTailCallerImpl(callerCallCount)); + + var request = caller.fooRequest(); + request.getParams().setI(456); + request.getParams().setCallee(callee); + + var promise = request.send(); + + var dependentCall0 = promise.getC().getCallSequenceRequest().send(); + + var response = promise.join(); + Assert.assertEquals(456, response.getI()); + Assert.assertEquals(456, response.getI()); + + var dependentCall1 = promise.getC().getCallSequenceRequest().send(); + + var dependentCall2 = response.getC().getCallSequenceRequest().send(); + + Assert.assertEquals(0, dependentCall0.join().getN()); + Assert.assertEquals(1, dependentCall1.join().getN()); + Assert.assertEquals(2, dependentCall2.join().getN()); + + Assert.assertEquals(1, calleeCallCount.value()); + Assert.assertEquals(1, callerCallCount.value()); + } + class TestThisCap extends Test.TestInterface.Server { Counter counter; diff --git a/runtime/src/main/java/org/capnproto/Capability.java b/runtime/src/main/java/org/capnproto/Capability.java index 5f9475a..f5738d2 100644 --- a/runtime/src/main/java/org/capnproto/Capability.java +++ b/runtime/src/main/java/org/capnproto/Capability.java @@ -185,10 +185,18 @@ public final class Capability { return null; } + // We don't want to actually dispatch the call synchronously, because we don't want the callee + // to have any side effects before the promise is returned to the caller. This helps avoid + // race conditions. + // + // So, we do an evalLater() here. + // + // Note also that QueuedClient depends on this evalLater() to ensure that pipelined calls don't + // complete before 'whenMoreResolved()' promises resolve. + // TODO fix the above comment! we don't have the option of evalLater (yes) var promise = this.whenResolved().thenCompose( void_ -> this.callInternal(interfaceId, methodId, ctx)); - var pipelinePromise = promise.thenApply(x -> { ctx.releaseParams(); return (PipelineHook)new LocalPipeline(ctx);