From ec0df4872fa4fc8e2a29a4cc568e4835d1ef05cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6llner?= Date: Sun, 22 Mar 2020 00:12:50 +0100 Subject: [PATCH] bug fixes, stability improvements --- .../DeserializationTests.cs | 24 ++ Capnp.Net.Runtime.Tests/Dtbdct.cs | 6 + Capnp.Net.Runtime.Tests/LocalRpc.cs | 86 +++++- .../Mock/TestCapImplementations.cs | 52 ++-- Capnp.Net.Runtime.Tests/Mock/test.cs | 6 +- Capnp.Net.Runtime.Tests/TcpRpcInterop.cs | 39 +-- Capnp.Net.Runtime.Tests/TcpRpcPorted.cs | 27 ++ Capnp.Net.Runtime.Tests/Testsuite.cs | 105 ++++---- Capnp.Net.Runtime.Tests/Util/TestBase.cs | 46 +++- Capnp.Net.Runtime/Rpc/ConsumedCapability.cs | 5 + Capnp.Net.Runtime/Rpc/Impatient.cs | 38 +-- Capnp.Net.Runtime/Rpc/LocalAnswer.cs | 2 +- .../Rpc/LocalAnswerCapability.cs | 15 ++ .../Rpc/LocalAnswerCapabilityDeprecated.cs | 84 ------ Capnp.Net.Runtime/Rpc/PendingQuestion.cs | 47 ++-- Capnp.Net.Runtime/Rpc/PromisedCapability.cs | 6 +- Capnp.Net.Runtime/Rpc/Proxy.cs | 5 + .../Rpc/RemoteAnswerCapabilityDeprecated.cs | 253 ------------------ .../Rpc/ResolvingCapabilityExtensions.cs | 18 +- Capnp.Net.Runtime/Rpc/RpcEngine.cs | 3 + Capnp.Net.Runtime/Rpc/TcpRpcServer.cs | 2 + .../Embedded Resources/test.cs | 6 +- .../CodeGen/InterfaceSnippetGen.cs | 23 +- 23 files changed, 402 insertions(+), 496 deletions(-) delete mode 100644 Capnp.Net.Runtime/Rpc/LocalAnswerCapabilityDeprecated.cs delete mode 100644 Capnp.Net.Runtime/Rpc/RemoteAnswerCapabilityDeprecated.cs diff --git a/Capnp.Net.Runtime.Tests/DeserializationTests.cs b/Capnp.Net.Runtime.Tests/DeserializationTests.cs index 51e50b8..d9b2861 100644 --- a/Capnp.Net.Runtime.Tests/DeserializationTests.cs +++ b/Capnp.Net.Runtime.Tests/DeserializationTests.cs @@ -767,5 +767,29 @@ namespace Capnp.Net.Runtime.Tests CollectionAssert.AreEqual(expected[i], voids[i].ToArray()); } } + + [TestMethod] + public void ListOfEmpty() + { + var expected = new TestEnum[] { TestEnum.bar, TestEnum.baz, TestEnum.corge }; + + var b = MessageBuilder.Create(); + var loes = b.CreateObject(); + loes.Init(12345678); + DeserializerState d = loes; + var ld = d.RequireList(); + Assert.AreEqual(ListKind.ListOfEmpty, ld.Kind); + if (!(ld is ListOfEmptyDeserializer loed)) + { + Assert.Fail("List did not deserialize back to ListOfEmptyDeserializer"); + return; + } + Assert.AreEqual(12345678, loed.Count); + Assert.ThrowsException(() => { var _ = loed[-1]; }); + Assert.ThrowsException(() => { var _ = loed[12345678]; }); + _ = loed[12345677]; + var kind = loed.Cast(_ => _.Kind).Take(1).Single(); + Assert.AreEqual(ObjectKind.Nil, kind); + } } } diff --git a/Capnp.Net.Runtime.Tests/Dtbdct.cs b/Capnp.Net.Runtime.Tests/Dtbdct.cs index 75d2a93..e5c8653 100644 --- a/Capnp.Net.Runtime.Tests/Dtbdct.cs +++ b/Capnp.Net.Runtime.Tests/Dtbdct.cs @@ -92,5 +92,11 @@ namespace Capnp.Net.Runtime.Tests { NewDtbdctTestbed().RunTest(Testsuite.Basic); } + + [TestMethod] + public void BootstrapReuse() + { + NewDtbdctTestbed().RunTest(Testsuite.BootstrapReuse); + } } } diff --git a/Capnp.Net.Runtime.Tests/LocalRpc.cs b/Capnp.Net.Runtime.Tests/LocalRpc.cs index fd4f87b..095e0d7 100644 --- a/Capnp.Net.Runtime.Tests/LocalRpc.cs +++ b/Capnp.Net.Runtime.Tests/LocalRpc.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace Capnp.Net.Runtime.Tests { [TestClass] - public class LocalRpc + public class LocalRpc: TestBase { [TestMethod] public void DeferredLocalAnswer() @@ -29,5 +29,89 @@ namespace Capnp.Net.Runtime.Tests Assert.AreEqual("bar", foo.Result); } } + + [TestMethod] + public void Embargo() + { + NewLocalTestbed().RunTest(Testsuite.Embargo); + } + + [TestMethod] + public void EmbargoError() + { + NewLocalTestbed().RunTest(Testsuite.EmbargoError); + } + + [TestMethod] + public void EmbargoNull() + { + NewLocalTestbed().RunTest(Testsuite.EmbargoNull); + } + + [TestMethod] + public void CallBrokenPromise() + { + NewLocalTestbed().RunTest(Testsuite.CallBrokenPromise); + } + + [TestMethod] + public void TailCall() + { + NewLocalTestbed().RunTest(Testsuite.TailCall); + } + + [TestMethod] + public void SendTwice() + { + NewLocalTestbed().RunTest(Testsuite.SendTwice); + } + + [TestMethod] + public void Cancel() + { + NewLocalTestbed().RunTest(Testsuite.Cancel); + } + + [TestMethod] + public void RetainAndRelease() + { + NewLocalTestbed().RunTest(Testsuite.RetainAndRelease); + } + + [TestMethod] + public void PromiseResolve() + { + NewLocalTestbed().RunTest(Testsuite.PromiseResolve); + } + + [TestMethod] + public void Cancelation() + { + NewLocalTestbed().RunTest(Testsuite.Cancelation); + } + + [TestMethod] + public void ReleaseOnCancel() + { + NewLocalTestbed().RunTest(Testsuite.ReleaseOnCancel); + } + + [TestMethod] + public void Release() + { + NewLocalTestbed().RunTest(Testsuite.Release); + } + + [TestMethod] + public void Pipeline() + { + NewLocalTestbed().RunTest(Testsuite.Pipeline); + } + + [TestMethod] + public void Basic() + { + NewLocalTestbed().RunTest(Testsuite.Basic); + } } } diff --git a/Capnp.Net.Runtime.Tests/Mock/TestCapImplementations.cs b/Capnp.Net.Runtime.Tests/Mock/TestCapImplementations.cs index 34fd9fe..6f65819 100644 --- a/Capnp.Net.Runtime.Tests/Mock/TestCapImplementations.cs +++ b/Capnp.Net.Runtime.Tests/Mock/TestCapImplementations.cs @@ -420,9 +420,12 @@ namespace Capnp.Net.Runtime.Tests.GenImpls public void Dispose() { - _tcs?.SetResult(0); + _tcs?.TrySetResult(0); + IsDisposed = true; } + public bool IsDisposed { get; private set; } + public virtual Task Foo(uint i, bool j, CancellationToken cancellationToken) { Interlocked.Increment(ref _counters.CallCount); @@ -512,20 +515,26 @@ namespace Capnp.Net.Runtime.Tests.GenImpls public async Task<(string, TestPipeline.AnyBox)> GetAnyCap(uint n, BareProxy inCap, CancellationToken cancellationToken_) { - Interlocked.Increment(ref _counters.CallCount); - Assert.AreEqual(234u, n); - var s = await inCap.Cast(true).Foo(123, true, cancellationToken_); - Assert.AreEqual("foo", s); - return ("bar", new TestPipeline.AnyBox() { Cap = BareProxy.FromImpl(new TestExtendsImpl(_counters)) }); + using (inCap) + { + Interlocked.Increment(ref _counters.CallCount); + Assert.AreEqual(234u, n); + var s = await inCap.Cast(true).Foo(123, true, cancellationToken_); + Assert.AreEqual("foo", s); + return ("bar", new TestPipeline.AnyBox() { Cap = BareProxy.FromImpl(new TestExtendsImpl(_counters)) }); + } } public async Task<(string, TestPipeline.Box)> GetCap(uint n, ITestInterface inCap, CancellationToken cancellationToken_) { - Interlocked.Increment(ref _counters.CallCount); - Assert.AreEqual(234u, n); - var s = await inCap.Foo(123, true, cancellationToken_); - Assert.AreEqual("foo", s); - return ("bar", new TestPipeline.Box() { Cap = new TestExtendsImpl(_counters) }); + using (inCap) + { + Interlocked.Increment(ref _counters.CallCount); + Assert.AreEqual(234u, n); + var s = await inCap.Foo(123, true, cancellationToken_); + Assert.AreEqual("foo", s); + return ("bar", new TestPipeline.Box() { Cap = new TestExtendsImpl(_counters) }); + } } public Task TestPointers(ITestInterface cap, object obj, IReadOnlyList list, CancellationToken cancellationToken_) @@ -558,8 +567,11 @@ namespace Capnp.Net.Runtime.Tests.GenImpls public async Task<(string, TestPipeline.Box)> GetCap(uint n, ITestInterface inCap, CancellationToken cancellationToken_ = default) { - await _deblock; - return ("hello", new TestPipeline.Box() { Cap = _timpl2 }); + using (inCap) + { + await _deblock; + return ("hello", new TestPipeline.Box() { Cap = _timpl2 }); + } } public Task TestPointers(ITestInterface cap, object obj, IReadOnlyList list, CancellationToken cancellationToken_ = default) @@ -661,10 +673,15 @@ namespace Capnp.Net.Runtime.Tests.GenImpls public void Dispose() { + IsDisposed = true; } + public bool IsDisposed { get; private set; } + public Task Foo(int i, string t, CancellationToken cancellationToken_) { + Assert.IsFalse(IsDisposed); + Interlocked.Increment(ref _counters.CallCount); var result = new TestTailCallee.TailResult() @@ -710,9 +727,12 @@ namespace Capnp.Net.Runtime.Tests.GenImpls public async Task CallFooWhenResolved(ITestInterface cap, CancellationToken cancellationToken_) { Interlocked.Increment(ref _counters.CallCount); - await ((Proxy)cap).WhenResolved; - string s = await cap.Foo(123, true, cancellationToken_); - Assert.AreEqual("foo", s); + using (cap) + { + await ((Proxy)cap).WhenResolved; + string s = await cap.Foo(123, true, cancellationToken_); + Assert.AreEqual("foo", s); + } return "bar"; } diff --git a/Capnp.Net.Runtime.Tests/Mock/test.cs b/Capnp.Net.Runtime.Tests/Mock/test.cs index f7930f2..8108823 100644 --- a/Capnp.Net.Runtime.Tests/Mock/test.cs +++ b/Capnp.Net.Runtime.Tests/Mock/test.cs @@ -17501,21 +17501,21 @@ namespace Capnproto_test.Capnp.Test public static Capnproto_test.Capnp.Test.ITestInterface OutBox_Cap(this Task<(string, Capnproto_test.Capnp.Test.TestPipeline.Box)> task) { async Task AwaitProxy() => (await task).Item2?.Cap; - return (Capnproto_test.Capnp.Test.ITestInterface)CapabilityReflection.CreateProxy(Impatient.GetAnswer(task).Access(Path_capnproto_test_capnp_test_TestPipeline_getCap_OutBox_Cap, AwaitProxy())); + return (Capnproto_test.Capnp.Test.ITestInterface)CapabilityReflection.CreateProxy(Impatient.Access(task, Path_capnproto_test_capnp_test_TestPipeline_getCap_OutBox_Cap, AwaitProxy())); } static readonly MemberAccessPath Path_capnproto_test_capnp_test_TestPipeline_getAnyCap_OutBox_Cap = new MemberAccessPath(1U, 0U); public static BareProxy OutBox_Cap(this Task<(string, Capnproto_test.Capnp.Test.TestPipeline.AnyBox)> task) { async Task AwaitProxy() => (await task).Item2?.Cap; - return (BareProxy)CapabilityReflection.CreateProxy(Impatient.GetAnswer(task).Access(Path_capnproto_test_capnp_test_TestPipeline_getAnyCap_OutBox_Cap, AwaitProxy())); + return (BareProxy)CapabilityReflection.CreateProxy(Impatient.Access(task, Path_capnproto_test_capnp_test_TestPipeline_getAnyCap_OutBox_Cap, AwaitProxy())); } static readonly MemberAccessPath Path_capnproto_test_capnp_test_TestTailCallee_foo_C = new MemberAccessPath(1U); public static Capnproto_test.Capnp.Test.ITestCallOrder C(this Task task) { async Task AwaitProxy() => (await task).C; - return (Capnproto_test.Capnp.Test.ITestCallOrder)CapabilityReflection.CreateProxy(Impatient.GetAnswer(task).Access(Path_capnproto_test_capnp_test_TestTailCallee_foo_C, AwaitProxy())); + return (Capnproto_test.Capnp.Test.ITestCallOrder)CapabilityReflection.CreateProxy(Impatient.Access(task, Path_capnproto_test_capnp_test_TestTailCallee_foo_C, AwaitProxy())); } } } \ No newline at end of file diff --git a/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs b/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs index 2f16eff..97228ca 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs @@ -2,6 +2,7 @@ using Capnp.Net.Runtime.Tests.GenImpls; using Capnp.Rpc; using Capnproto_test.Capnp.Test; +using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; @@ -63,21 +64,25 @@ namespace Capnp.Net.Runtime.Tests try { - _currentProcess.StandardError.ReadToEndAsync().ContinueWith(t => Console.Error.WriteLine(t.Result)); - var firstLine = _currentProcess.StandardOutput.ReadLineAsync(); - Assert.IsTrue(firstLine.Wait(MediumNonDbgTimeout), "Problem after launching test process"); - Assert.IsNotNull(firstLine.Result, "Problem after launching test process"); - Assert.IsTrue(firstLine.Result.StartsWith("Listening") || firstLine.Result.StartsWith("Connecting"), - "Problem after launching test process"); + try + { + _currentProcess.StandardError.ReadToEndAsync().ContinueWith(t => Console.Error.WriteLine(t.Result)); + var firstLine = _currentProcess.StandardOutput.ReadLineAsync(); + Assert.IsTrue(firstLine.Wait(MediumNonDbgTimeout), "Problem after launching test process"); + Assert.IsNotNull(firstLine.Result, "Problem after launching test process"); + Assert.IsTrue(firstLine.Result.StartsWith("Listening") || firstLine.Result.StartsWith("Connecting"), + "Problem after launching test process"); + } + catch (AssertFailedException exception) + { + Logger.LogError(exception.Message); + return false; + } test(_currentProcess.StandardOutput); return true; } - catch (AssertFailedException) - { - return false; - } finally { try @@ -220,6 +225,7 @@ namespace Capnp.Net.Runtime.Tests { AssertOutput(stdout, "Pipelining test start"); AssertOutput(stdout, "foo 123 1"); + AssertOutput(stdout, "~"); AssertOutput(stdout, "Pipelining test end"); Assert.AreEqual(3, counters.CallCount); }); @@ -342,17 +348,19 @@ namespace Capnp.Net.Runtime.Tests for (int i = 0; i < iterationCount; i++) { var task = main.GetHandle(default); - taskList.Add(task.ContinueWith(t => + async Task TerminateAnswer() { try { - t.Result.Dispose(); + using (var proxy = await task) + { + } } - catch (AggregateException ex) when (ex.InnerException is TaskCanceledException) + catch (TaskCanceledException) { } - })); - Impatient.GetAnswer(task).Dispose(); + } + taskList.Add(TerminateAnswer()); } // Ensure that all answers return (probably in canceled state) @@ -558,6 +566,7 @@ namespace Capnp.Net.Runtime.Tests AssertOutput(stdout, "PromiseResolve test start"); AssertOutput(stdout, "foo 123 1"); AssertOutput(stdout, "foo 123 1"); + AssertOutput(stdout, "~"); AssertOutput(stdout, "PromiseResolve test end"); Assert.AreEqual(3, counters.CallCount); }); diff --git a/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs b/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs index 293e595..0066c40 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs @@ -139,5 +139,32 @@ namespace Capnp.Net.Runtime.Tests { NewLocalhostTcpTestbed().RunTest(Testsuite.CallBrokenPromise); } + + [TestMethod] + public void BootstrapReuse() + { + (var server, var client) = SetupClientServerPair(); + + var counters = new Counters(); + var impl = new TestInterfaceImpl(counters); + + using (server) + using (client) + { + client.WhenConnected.Wait(); + + server.Main = impl; + for (int i = 0; i < 10; i++) + { + using (var main = client.GetMain()) + { + ((Proxy)main).WhenResolved.Wait(MediumNonDbgTimeout); + } + Assert.IsFalse(impl.IsDisposed); + } + } + + Assert.IsTrue(impl.IsDisposed); + } } } diff --git a/Capnp.Net.Runtime.Tests/Testsuite.cs b/Capnp.Net.Runtime.Tests/Testsuite.cs index 0d9821a..dc48702 100644 --- a/Capnp.Net.Runtime.Tests/Testsuite.cs +++ b/Capnp.Net.Runtime.Tests/Testsuite.cs @@ -60,8 +60,8 @@ namespace Capnp.Net.Runtime.Tests using (var main = testbed.ConnectMain(impl)) { - var resolving = main as IResolvingCapability; - testbed.MustComplete(resolving.WhenResolved); + if (main is IResolvingCapability resolving) + testbed.MustComplete(resolving.WhenResolved); var cap = new TestCallOrderImpl(); cap.CountToDispose = 6; @@ -70,7 +70,7 @@ namespace Capnp.Net.Runtime.Tests var echo = main.Echo(cap, default); testbed.MustComplete(Task.CompletedTask); - using (var pipeline = echo.Eager()) + using (var pipeline = echo.Eager(true)) { var call0 = pipeline.GetCallSequence(0, default); var call1 = pipeline.GetCallSequence(1, default); @@ -120,15 +120,15 @@ namespace Capnp.Net.Runtime.Tests var impl = new TestMoreStuffImpl(counters); using (var main = testbed.ConnectMain(impl)) { - var resolving = main as IResolvingCapability; - testbed.MustComplete(resolving.WhenResolved); + if (main is IResolvingCapability resolving) + testbed.MustComplete(resolving.WhenResolved); var cap = new TaskCompletionSource(); var earlyCall = main.GetCallSequence(0, default); var echo = main.Echo(cap.Task.Eager(true), default); - using (var pipeline = echo.Eager()) + using (var pipeline = echo.Eager(true)) { var call0 = pipeline.GetCallSequence(0, default); var call1 = pipeline.GetCallSequence(1, default); @@ -161,20 +161,21 @@ namespace Capnp.Net.Runtime.Tests var impl = new TestMoreStuffImpl(counters); using (var main = testbed.ConnectMain(impl)) { - var resolving = main as IResolvingCapability; - testbed.MustComplete(resolving.WhenResolved); + if (main is IResolvingCapability resolving) + testbed.MustComplete(resolving.WhenResolved); var promise = main.GetNull(default); - var cap = promise.Eager(); + using (var cap = promise.Eager(true)) + { + var call0 = cap.GetCallSequence(0, default); - var call0 = cap.GetCallSequence(0, default); + testbed.MustComplete(promise); - testbed.MustComplete(promise); + var call1 = cap.GetCallSequence(1, default); - var call1 = cap.GetCallSequence(1, default); - - testbed.ExpectPromiseThrows(call0, call1); + testbed.ExpectPromiseThrows(call0, call1); + } // Verify that we're still connected (there were no protocol errors). testbed.MustComplete(main.GetCallSequence(1, default)); @@ -184,11 +185,11 @@ namespace Capnp.Net.Runtime.Tests public static void CallBrokenPromise(ITestbed testbed) { var counters = new Counters(); - var impl = new TestMoreStuffImpl(counters); + using (var impl = new TestMoreStuffImpl(counters)) using (var main = testbed.ConnectMain(impl)) { - var resolving = main as IResolvingCapability; - testbed.MustComplete(resolving.WhenResolved); + if (main is IResolvingCapability resolving) + testbed.MustComplete(resolving.WhenResolved); var tcs = new TaskCompletionSource(); @@ -231,6 +232,7 @@ namespace Capnp.Net.Runtime.Tests testbed.MustComplete(dependentCall0, dependentCall1, dependentCall2); + Assert.IsTrue(callee.IsDisposed); Assert.AreEqual(1, counters.CallCount); Assert.AreEqual(1, calleeCallCount.CallCount); } @@ -313,7 +315,7 @@ namespace Capnp.Net.Runtime.Tests var destructionTask = destructionPromise.Task; var counters = new Counters(); - var impl = new TestMoreStuffImpl(counters); + using (var impl = new TestMoreStuffImpl(counters)) using (var main = testbed.ConnectMain(impl)) { var holdTask = main.Hold(new TestInterfaceImpl(new Counters(), destructionPromise), default); @@ -366,12 +368,6 @@ namespace Capnp.Net.Runtime.Tests // Can't be destroyed, we haven't released it. Assert.IsFalse(destructionTask.IsCompleted); } - - // In deviation from original test, we have null the held capability on the main interface. - // This is because the main interface is the bootstrap capability and, as such, won't be disposed - // after disconnect. - var holdNullTask = main.Hold(null, default); - testbed.MustComplete(holdNullTask); } testbed.MustComplete(destructionTask); @@ -384,26 +380,27 @@ namespace Capnp.Net.Runtime.Tests using (var main = testbed.ConnectMain(impl)) { var tcs = new TaskCompletionSource(); - var eager = tcs.Task.Eager(true); + using (var eager = tcs.Task.Eager(true)) + { + var request = main.CallFoo(Proxy.Share(eager), default); + var request2 = main.CallFooWhenResolved(eager, default); - var request = main.CallFoo(eager, default); - var request2 = main.CallFooWhenResolved(eager, default); + var gcs = main.GetCallSequence(0, default); + testbed.MustComplete(gcs); + Assert.AreEqual(2u, gcs.Result); + Assert.AreEqual(3, counters.CallCount); - var gcs = main.GetCallSequence(0, default); - testbed.MustComplete(gcs); - Assert.AreEqual(2u, gcs.Result); - Assert.AreEqual(3, counters.CallCount); + var chainedCallCount = new Counters(); + var tiimpl = new TestInterfaceImpl(chainedCallCount); + tcs.SetResult(tiimpl); - var chainedCallCount = new Counters(); - var tiimpl = new TestInterfaceImpl(chainedCallCount); - tcs.SetResult(tiimpl); + testbed.MustComplete(request, request2); - testbed.MustComplete(request, request2); - - Assert.AreEqual("bar", request.Result); - Assert.AreEqual("bar", request2.Result); - Assert.AreEqual(3, counters.CallCount); - Assert.AreEqual(2, chainedCallCount.CallCount); + Assert.AreEqual("bar", request.Result); + Assert.AreEqual("bar", request2.Result); + Assert.AreEqual(3, counters.CallCount); + Assert.AreEqual(2, chainedCallCount.CallCount); + } } } @@ -488,15 +485,18 @@ namespace Capnp.Net.Runtime.Tests using (var outBox = request.OutBox_Cap()) { var pipelineRequest = outBox.Foo(321, false, default); - var pipelineRequest2 = ((Proxy)outBox).Cast(false).Grault(default); + using (var testx = ((Proxy)outBox).Cast(false)) + { + var pipelineRequest2 = testx.Grault(default); - testbed.MustComplete(pipelineRequest, pipelineRequest2); + testbed.MustComplete(pipelineRequest, pipelineRequest2); - Assert.AreEqual("bar", pipelineRequest.Result); - Common.CheckTestMessage(pipelineRequest2.Result); + Assert.AreEqual("bar", pipelineRequest.Result); + Common.CheckTestMessage(pipelineRequest2.Result); - Assert.AreEqual(3, counters.CallCount); - Assert.AreEqual(1, chainedCallCount.CallCount); + Assert.AreEqual(3, counters.CallCount); + Assert.AreEqual(1, chainedCallCount.CallCount); + } } } } @@ -519,5 +519,18 @@ namespace Capnp.Net.Runtime.Tests Assert.AreEqual(2, counters.CallCount); } } + + public static void BootstrapReuse(ITestbed testbed) + { + var counters = new Counters(); + var impl = new TestInterfaceImpl(counters); + for (int i = 0; i < 10; i++) + { + using (var main = testbed.ConnectMain(impl)) + { + } + Assert.IsFalse(impl.IsDisposed); + } + } } } diff --git a/Capnp.Net.Runtime.Tests/Util/TestBase.cs b/Capnp.Net.Runtime.Tests/Util/TestBase.cs index b64377f..422e7b3 100644 --- a/Capnp.Net.Runtime.Tests/Util/TestBase.cs +++ b/Capnp.Net.Runtime.Tests/Util/TestBase.cs @@ -147,6 +147,35 @@ namespace Capnp.Net.Runtime.Tests public int Channel2SendCount => _channel2.FrameCounter; } + protected class LocalTestbed : ITestbed, ITestController + { + long ITestbed.ClientSendCount => 0; + + public void RunTest(Action action) + { + action(this); + } + + T ITestbed.ConnectMain(T main) + { + return main; + } + + void ITestbed.FlushCommunication() + { + } + + void ITestbed.MustComplete(params Task[] tasks) + { + Assert.IsTrue(Task.WhenAll(tasks).IsCompleted); + } + + void ITestbed.MustNotComplete(params Task[] tasks) + { + Assert.IsFalse(Task.WhenAny(tasks).IsCompleted); + } + } + protected class DtbdctTestbed : ITestbed, ITestController { readonly DecisionTree _decisionTree = new DecisionTree(); @@ -154,7 +183,20 @@ namespace Capnp.Net.Runtime.Tests public void RunTest(Action action) { - _decisionTree.Iterate(() => action(this)); + _decisionTree.Iterate(() => { + + action(this); + _enginePair.FlushChannels(() => false); + + Assert.AreEqual(0, _enginePair.Endpoint1.ExportedCapabilityCount); + Assert.AreEqual(0, _enginePair.Endpoint1.ImportedCapabilityCount); + Assert.AreEqual(0, _enginePair.Endpoint2.ExportedCapabilityCount); + Assert.AreEqual(0, _enginePair.Endpoint2.ImportedCapabilityCount); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + }); } T ITestbed.ConnectMain(T main) @@ -290,6 +332,8 @@ namespace Capnp.Net.Runtime.Tests protected static DtbdctTestbed NewDtbdctTestbed() => new DtbdctTestbed(); protected static LocalhostTcpTestbed NewLocalhostTcpTestbed() => new LocalhostTcpTestbed(); + protected static LocalTestbed NewLocalTestbed() => new LocalTestbed(); + [TestInitialize] public void InitConsoleLogging() { diff --git a/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs b/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs index 4342b84..452bcd3 100644 --- a/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs +++ b/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs @@ -25,5 +25,10 @@ namespace Capnp.Rpc [System.Runtime.CompilerServices.CallerMemberName] string methodName = "", [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0); + +#if DebugFinalizers + internal Proxy? OwningProxy { get; set; } + internal ConsumedCapability? ResolvingCap { get; set; } +#endif } } \ No newline at end of file diff --git a/Capnp.Net.Runtime/Rpc/Impatient.cs b/Capnp.Net.Runtime/Rpc/Impatient.cs index b07e5ba..e0ede4e 100644 --- a/Capnp.Net.Runtime/Rpc/Impatient.cs +++ b/Capnp.Net.Runtime/Rpc/Impatient.cs @@ -92,35 +92,11 @@ namespace Capnp.Rpc return answer; } - static async Task AwaitProxy(Task task) where T: class + public static ConsumedCapability? Access(Task task, MemberAccessPath access, Task proxyTask) { - T item; - - try - { - item = await task; - } - catch (TaskCanceledException exception) - { - return new Proxy(LazyCapability.CreateCanceledCap(exception.CancellationToken)); - } - catch (System.Exception exception) - { - return new Proxy(LazyCapability.CreateBrokenCap(exception.Message)); - } - - switch (item) - { - case Proxy proxy: - return proxy; - - case null: - return CapabilityReflection.CreateProxy(null); - } - - var skel = Skeleton.GetOrCreateSkeleton(item!, false); - var localCap = LocalCapability.Create(skel); - return CapabilityReflection.CreateProxy(localCap); + var answer = TryGetAnswer(task); + if (answer != null) return answer.Access(access, proxyTask); + return new LazyCapability(proxyTask.AsProxyTask()); } /// @@ -138,9 +114,9 @@ namespace Capnp.Rpc /// quality as capability interface. [Obsolete("Call Eager(task, true) instead")] public static TInterface PseudoEager(this Task task) - where TInterface : class + where TInterface : class, IDisposable { - var lazyCap = new LazyCapability(AwaitProxy(task)); + var lazyCap = new LazyCapability(task.AsProxyTask()); return (CapabilityReflection.CreateProxy(lazyCap) as TInterface)!; } @@ -177,7 +153,7 @@ namespace Capnp.Rpc throw new ArgumentException("The task was not returned from a remote method invocation. See documentation for details."); } - var lazyCap = new LazyCapability(AwaitProxy(task)); + var lazyCap = new LazyCapability(task.AsProxyTask()); return (CapabilityReflection.CreateProxy(lazyCap) as TInterface)!; } else diff --git a/Capnp.Net.Runtime/Rpc/LocalAnswer.cs b/Capnp.Net.Runtime/Rpc/LocalAnswer.cs index f88e27b..f1f3f86 100644 --- a/Capnp.Net.Runtime/Rpc/LocalAnswer.cs +++ b/Capnp.Net.Runtime/Rpc/LocalAnswer.cs @@ -27,7 +27,7 @@ namespace Capnp.Rpc public ConsumedCapability Access(MemberAccessPath access) { - return new LocalAnswerCapabilityDeprecated(WhenReturned, access); + return new LocalAnswerCapability(WhenReturned, access); } public ConsumedCapability Access(MemberAccessPath _, Task task) diff --git a/Capnp.Net.Runtime/Rpc/LocalAnswerCapability.cs b/Capnp.Net.Runtime/Rpc/LocalAnswerCapability.cs index 9d6093e..ef5cc29 100644 --- a/Capnp.Net.Runtime/Rpc/LocalAnswerCapability.cs +++ b/Capnp.Net.Runtime/Rpc/LocalAnswerCapability.cs @@ -7,6 +7,15 @@ namespace Capnp.Rpc class LocalAnswerCapability : RefCountingCapability, IResolvingCapability { + static async Task TransferOwnershipToDummyProxy(Task answer, MemberAccessPath access) + { + var result = await answer; + var cap = access.Eval(result); + var proxy = new Proxy(cap); + cap?.Release(false); + return proxy; + } + readonly Task _whenResolvedProxy; public LocalAnswerCapability(Task proxyTask) @@ -17,6 +26,12 @@ namespace Capnp.Rpc WhenResolved = AwaitResolved(); } + public LocalAnswerCapability(Task answer, MemberAccessPath access): + this(TransferOwnershipToDummyProxy(answer, access)) + { + + } + internal override void Freeze(out IRpcEndpoint? boundEndpoint) { boundEndpoint = null; diff --git a/Capnp.Net.Runtime/Rpc/LocalAnswerCapabilityDeprecated.cs b/Capnp.Net.Runtime/Rpc/LocalAnswerCapabilityDeprecated.cs deleted file mode 100644 index 3688638..0000000 --- a/Capnp.Net.Runtime/Rpc/LocalAnswerCapabilityDeprecated.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Capnp.Rpc -{ - class LocalAnswerCapabilityDeprecated : RefCountingCapability, IResolvingCapability - { - readonly Task _answer; - readonly MemberAccessPath _access; - - public LocalAnswerCapabilityDeprecated(Task answer, MemberAccessPath access) - { - _answer = answer; - _access = access; - - async Task AwaitResolved() => access.Eval(await _answer); - WhenResolved = AwaitResolved(); - } - - internal override void Freeze(out IRpcEndpoint? boundEndpoint) - { - boundEndpoint = null; - } - - internal override void Unfreeze() - { - } - - - public Task WhenResolved { get; private set; } - - internal override Action? Export(IRpcEndpoint endpoint, CapDescriptor.WRITER writer) - { - if (_answer.IsCompleted) - { - DeserializerState result; - try - { - result = _answer.Result; - } - catch (AggregateException exception) - { - throw exception.InnerException!; - } - - using var proxy = new Proxy(_access.Eval(result)); - proxy.Export(endpoint, writer); - return null; - } - else - { - return this.ExportAsSenderPromise(endpoint, writer); - } - } - - async Task CallImpl(ulong interfaceId, ushort methodId, DynamicSerializerState args, CancellationToken cancellationToken) - { - var cap = await WhenResolved; - - cancellationToken.ThrowIfCancellationRequested(); - - if (cap == null) - throw new RpcException("Broken capability"); - - using var proxy = new Proxy(cap); - var call = proxy.Call(interfaceId, methodId, args, default); - var whenReturned = call.WhenReturned; - - using var registration = cancellationToken.Register(() => call.Dispose()); - return await whenReturned; - } - - internal override IPromisedAnswer DoCall(ulong interfaceId, ushort methodId, DynamicSerializerState args) - { - var cts = new CancellationTokenSource(); - return new LocalAnswer(cts, CallImpl(interfaceId, methodId, args, cts.Token)); - } - - protected override void ReleaseRemotely() - { - } - } -} \ No newline at end of file diff --git a/Capnp.Net.Runtime/Rpc/PendingQuestion.cs b/Capnp.Net.Runtime/Rpc/PendingQuestion.cs index dcdd7be..548f907 100644 --- a/Capnp.Net.Runtime/Rpc/PendingQuestion.cs +++ b/Capnp.Net.Runtime/Rpc/PendingQuestion.cs @@ -126,17 +126,17 @@ namespace Capnp.Rpc lock (ReentrancyBlocker) { SetReturned(); + } - if (StateFlags.HasFlag(State.TailCall)) + if (StateFlags.HasFlag(State.TailCall)) + { + _tcs.TrySetException(new RpcException(ReturnDespiteTailCallMessage)); + } + else + { + if (!_tcs.TrySetResult(results)) { - _tcs.TrySetException(new RpcException(ReturnDespiteTailCallMessage)); - } - else - { - if (!_tcs.TrySetResult(results)) - { - ReleaseOutCaps(results); - } + ReleaseOutCaps(results); } } } @@ -146,15 +146,15 @@ namespace Capnp.Rpc lock (ReentrancyBlocker) { SetReturned(); + } - if (!StateFlags.HasFlag(State.TailCall)) - { - _tcs.TrySetException(new RpcException("Peer sent the results of this questions somewhere else. This was not expected and is a protocol error.")); - } - else - { - _tcs.TrySetResult(default); - } + if (!StateFlags.HasFlag(State.TailCall)) + { + _tcs.TrySetException(new RpcException("Peer sent the results of this questions somewhere else. This was not expected and is a protocol error.")); + } + else + { + _tcs.TrySetResult(default); } } @@ -163,9 +163,9 @@ namespace Capnp.Rpc lock (ReentrancyBlocker) { SetReturned(); - - _tcs.TrySetException(new RpcException(exception.Reason)); } + + _tcs.TrySetException(new RpcException(exception.Reason)); } internal void OnException(System.Exception exception) @@ -173,9 +173,9 @@ namespace Capnp.Rpc lock (ReentrancyBlocker) { SetReturned(); - - _tcs.TrySetException(exception); } + + _tcs.TrySetException(exception); } internal void OnCanceled() @@ -183,9 +183,9 @@ namespace Capnp.Rpc lock (ReentrancyBlocker) { SetReturned(); - - _tcs.TrySetCanceled(); } + + _tcs.TrySetCanceled(); } void DeleteMyQuestion() @@ -234,7 +234,6 @@ namespace Capnp.Rpc /// Access path /// Low-level capability /// The referenced member does not exist or does not resolve to a capability pointer. - [Obsolete("Please re-generate. Replaced by Access(MemberAccessPath access, Task task)")] public ConsumedCapability? Access(MemberAccessPath access) { lock (ReentrancyBlocker) diff --git a/Capnp.Net.Runtime/Rpc/PromisedCapability.cs b/Capnp.Net.Runtime/Rpc/PromisedCapability.cs index ec9a97b..0099383 100644 --- a/Capnp.Net.Runtime/Rpc/PromisedCapability.cs +++ b/Capnp.Net.Runtime/Rpc/PromisedCapability.cs @@ -203,6 +203,9 @@ namespace Capnp.Rpc lock (_reentrancyBlocker) { +#if DebugFinalizers + resolvedCap.ResolvingCap = this; +#endif _resolvedCap.SetResult(resolvedCap); if (_pendingCallsOnPromise == 0) @@ -247,11 +250,10 @@ namespace Capnp.Rpc { if (!_released) { + _released = true; _ep.ReleaseImport(_remoteId); } - _ep.ReleaseImport(_remoteId); - try { using var _ = await _whenResolvedProxy; } catch { } } diff --git a/Capnp.Net.Runtime/Rpc/Proxy.cs b/Capnp.Net.Runtime/Rpc/Proxy.cs index 1d6c9e0..c4d0eed 100644 --- a/Capnp.Net.Runtime/Rpc/Proxy.cs +++ b/Capnp.Net.Runtime/Rpc/Proxy.cs @@ -120,6 +120,11 @@ namespace Capnp.Rpc ConsumedCap = cap; cap.AddRef(); + +#if DebugFinalizers + if (ConsumedCap != null) + ConsumedCap.OwningProxy = this; +#endif } internal Skeleton? GetProvider() diff --git a/Capnp.Net.Runtime/Rpc/RemoteAnswerCapabilityDeprecated.cs b/Capnp.Net.Runtime/Rpc/RemoteAnswerCapabilityDeprecated.cs deleted file mode 100644 index f950659..0000000 --- a/Capnp.Net.Runtime/Rpc/RemoteAnswerCapabilityDeprecated.cs +++ /dev/null @@ -1,253 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Capnp.Rpc -{ -#if false - - class RemoteAnswerCapabilityDeprecated : RemoteResolvingCapability - { - // Set DebugEmbargos to true to get logging output for calls. RPC calls are expected to - // be on the critical path, hence very relevant for performance. We just can't afford - // additional stuff on this path. Even if the logger filters the outputs away, there is - // overhead for creating the Logger object, calling the Logger methods and deciding to - // filter the output. This justifies the precompiler switch. -#if DebugEmbargos - ILogger Logger { get; } = Logging.CreateLogger(); -#endif - - readonly PendingQuestion _question; - readonly MemberAccessPath _access; - ConsumedCapability? _resolvedCap; - - public RemoteAnswerCapabilityDeprecated(PendingQuestion question, MemberAccessPath access): base(question.RpcEndpoint) - { - _question = question ?? throw new ArgumentNullException(nameof(question)); - _access = access ?? throw new ArgumentNullException(nameof(access)); - - async Task AwaitWhenResolved() - { - await _question.WhenReturned; - - if (_question.IsTailCall) - throw new InvalidOperationException("Question is a tail call, so won't resolve back."); - - return ResolvedCap!; - } - - WhenResolved = AwaitWhenResolved(); - } - - async void ReAllowFinishWhenDone(Task task) - { - try - { - ++_pendingCallsOnPromise; - - await task; - } - catch - { - } - finally - { - lock (_question.ReentrancyBlocker) - { - --_pendingCallsOnPromise; - _question.AllowFinish(); - } - } - } - - protected override ConsumedCapability? ResolvedCap - { - get - { - lock (_question.ReentrancyBlocker) - { - if (_resolvedCap == null && !_question.IsTailCall && _question.IsReturned) - { - DeserializerState result; - try - { - result = _question.WhenReturned.Result; - } - catch (AggregateException exception) - { - throw exception.InnerException!; - } - - _resolvedCap = _access.Eval(result); - } - return _resolvedCap; - } - } - } - - public override Task WhenResolved { get; } - - protected override void GetMessageTarget(MessageTarget.WRITER wr) - { - wr.which = MessageTarget.WHICH.PromisedAnswer; - wr.PromisedAnswer.QuestionId = _question.QuestionId; - _access.Serialize(wr.PromisedAnswer); - } - - internal override IPromisedAnswer DoCall(ulong interfaceId, ushort methodId, DynamicSerializerState args) - { - lock (_question.ReentrancyBlocker) - { - if (_question.StateFlags.HasFlag(PendingQuestion.State.Returned) && - !_question.StateFlags.HasFlag(PendingQuestion.State.TailCall)) - { - if (ResolvedCap == null) - { - throw new RpcException("Answer did not resolve to expected capability"); - } - - return CallOnResolution(interfaceId, methodId, args); - } - else - { -#if DebugEmbargos - Logger.LogDebug("Call by proxy"); -#endif - if (_question.StateFlags.HasFlag(PendingQuestion.State.Disposed)) - { - throw new ObjectDisposedException(nameof(PendingQuestion)); - } - - if (_question.StateFlags.HasFlag(PendingQuestion.State.FinishRequested)) - { - throw new InvalidOperationException("Finish request was already sent"); - } - - _question.DisallowFinish(); - ++_pendingCallsOnPromise; - var promisedAnswer = base.DoCall(interfaceId, methodId, args); - ReAllowFinishWhenDone(promisedAnswer.WhenReturned); - - async void DecrementPendingCallsOnPromiseWhenReturned() - { - try - { - await promisedAnswer.WhenReturned; - } - catch - { - } - finally - { - lock (_question.ReentrancyBlocker) - { - --_pendingCallsOnPromise; - } - } - } - - DecrementPendingCallsOnPromiseWhenReturned(); - return promisedAnswer; - } - } - } - - protected override Call.WRITER SetupMessage(DynamicSerializerState args, ulong interfaceId, ushort methodId) - { - var call = base.SetupMessage(args, interfaceId, methodId); - - call.Target.which = MessageTarget.WHICH.PromisedAnswer; - call.Target.PromisedAnswer.QuestionId = _question.QuestionId; - _access.Serialize(call.Target.PromisedAnswer); - - return call; - } - - internal override void Freeze(out IRpcEndpoint? boundEndpoint) - { - lock (_question.ReentrancyBlocker) - { - if (_question.StateFlags.HasFlag(PendingQuestion.State.Returned) && - _pendingCallsOnPromise == 0) - { - if (ResolvedCap == null) - { - throw new RpcException("Answer did not resolve to expected capability"); - } - - ResolvedCap.Freeze(out boundEndpoint); - } - else - { - ++_pendingCallsOnPromise; - _question.DisallowFinish(); - boundEndpoint = _ep; - } - } - } - - internal override void Unfreeze() - { - lock (_question.ReentrancyBlocker) - { - if (_pendingCallsOnPromise > 0) - { - --_pendingCallsOnPromise; - _question.AllowFinish(); - } - else - { - ResolvedCap?.Unfreeze(); - } - } - } - - internal override void Export(IRpcEndpoint endpoint, CapDescriptor.WRITER writer) - { - lock (_question.ReentrancyBlocker) - { - if (_question.StateFlags.HasFlag(PendingQuestion.State.Disposed)) - throw new ObjectDisposedException(nameof(PendingQuestion)); - - if (_question.StateFlags.HasFlag(PendingQuestion.State.Returned)) - { - ResolvedCap?.Export(endpoint, writer); - } - else - { - if (_question.StateFlags.HasFlag(PendingQuestion.State.FinishRequested)) - throw new InvalidOperationException("Finish request was already sent"); - - if (endpoint == _ep) - { - writer.which = CapDescriptor.WHICH.ReceiverAnswer; - _access.Serialize(writer.ReceiverAnswer); - writer.ReceiverAnswer.QuestionId = _question.QuestionId; - } - else if (_question.IsTailCall) - { - // FIXME: Resource management! We should prevent finishing this - // cap as long as it is exported. Unfortunately, we cannot determine - // when it gets removed from the export table. - - var vine = Vine.Create(this); - uint id = endpoint.AllocateExport(vine, out bool first); - - writer.which = CapDescriptor.WHICH.SenderHosted; - writer.SenderHosted = id; - } - else - { - this.ExportAsSenderPromise(endpoint, writer); - } - } - } - } - - protected async override void ReleaseRemotely() - { - try { (await WhenResolved)?.Release(false); } - catch { } - } - } -#endif -} \ No newline at end of file diff --git a/Capnp.Net.Runtime/Rpc/ResolvingCapabilityExtensions.cs b/Capnp.Net.Runtime/Rpc/ResolvingCapabilityExtensions.cs index ade7ee8..0461d75 100644 --- a/Capnp.Net.Runtime/Rpc/ResolvingCapabilityExtensions.cs +++ b/Capnp.Net.Runtime/Rpc/ResolvingCapabilityExtensions.cs @@ -34,9 +34,23 @@ namespace Capnp.Rpc return null; } - public static async Task AsProxyTask(this Task task) + public static async Task AsProxyTask(this Task task) + where T: IDisposable? { - var obj = await task; + IDisposable? obj; + try + { + obj = await task; + } + catch (TaskCanceledException exception) + { + return new Proxy(LazyCapability.CreateCanceledCap(exception.CancellationToken)); + } + catch (System.Exception exception) + { + return new Proxy(LazyCapability.CreateBrokenCap(exception.Message)); + } + switch (obj) { case Proxy proxy: return proxy; diff --git a/Capnp.Net.Runtime/Rpc/RpcEngine.cs b/Capnp.Net.Runtime/Rpc/RpcEngine.cs index 74ffc4b..8b6d9ce 100644 --- a/Capnp.Net.Runtime/Rpc/RpcEngine.cs +++ b/Capnp.Net.Runtime/Rpc/RpcEngine.cs @@ -132,6 +132,9 @@ namespace Capnp.Rpc public long SendCount => Interlocked.Read(ref _sendCount); public long RecvCount => Interlocked.Read(ref _recvCount); + public int ImportedCapabilityCount => _importTable.Count; + public int ExportedCapabilityCount => _exportTable.Count; + void Tx(WireFrame frame) { try diff --git a/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs b/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs index 2012f74..a4d4dc0 100644 --- a/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs +++ b/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs @@ -263,6 +263,8 @@ namespace Capnp.Rpc SafeJoin(connection.PumpRunner); } + _rpcEngine.BootstrapCap = null; + GC.SuppressFinalize(this); } diff --git a/CapnpC.CSharp.Generator.Tests/Embedded Resources/test.cs b/CapnpC.CSharp.Generator.Tests/Embedded Resources/test.cs index 6053239..2f482e4 100644 --- a/CapnpC.CSharp.Generator.Tests/Embedded Resources/test.cs +++ b/CapnpC.CSharp.Generator.Tests/Embedded Resources/test.cs @@ -16717,21 +16717,21 @@ namespace Capnproto_test.Capnp.Test public static Capnproto_test.Capnp.Test.ITestInterface OutBox_Cap(this Task<(string, Capnproto_test.Capnp.Test.TestPipeline.Box)> task) { async Task AwaitProxy() => (await task).Item2?.Cap; - return (Capnproto_test.Capnp.Test.ITestInterface)CapabilityReflection.CreateProxy(Impatient.GetAnswer(task).Access(Path_capnproto_test_capnp_test_TestPipeline_getCap_OutBox_Cap, AwaitProxy())); + return (Capnproto_test.Capnp.Test.ITestInterface)CapabilityReflection.CreateProxy(Impatient.Access(task, Path_capnproto_test_capnp_test_TestPipeline_getCap_OutBox_Cap, AwaitProxy())); } static readonly MemberAccessPath Path_capnproto_test_capnp_test_TestPipeline_getAnyCap_OutBox_Cap = new MemberAccessPath(1U, 0U); public static BareProxy OutBox_Cap(this Task<(string, Capnproto_test.Capnp.Test.TestPipeline.AnyBox)> task) { async Task AwaitProxy() => (await task).Item2?.Cap; - return (BareProxy)CapabilityReflection.CreateProxy(Impatient.GetAnswer(task).Access(Path_capnproto_test_capnp_test_TestPipeline_getAnyCap_OutBox_Cap, AwaitProxy())); + return (BareProxy)CapabilityReflection.CreateProxy(Impatient.Access(task, Path_capnproto_test_capnp_test_TestPipeline_getAnyCap_OutBox_Cap, AwaitProxy())); } static readonly MemberAccessPath Path_capnproto_test_capnp_test_TestTailCallee_foo_C = new MemberAccessPath(1U); public static Capnproto_test.Capnp.Test.ITestCallOrder C(this Task task) { async Task AwaitProxy() => (await task).C; - return (Capnproto_test.Capnp.Test.ITestCallOrder)CapabilityReflection.CreateProxy(Impatient.GetAnswer(task).Access(Path_capnproto_test_capnp_test_TestTailCallee_foo_C, AwaitProxy())); + return (Capnproto_test.Capnp.Test.ITestCallOrder)CapabilityReflection.CreateProxy(Impatient.Access(task, Path_capnproto_test_capnp_test_TestTailCallee_foo_C, AwaitProxy())); } } } \ No newline at end of file diff --git a/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs index f1940aa..676a09d 100644 --- a/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs +++ b/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs @@ -931,21 +931,16 @@ namespace CapnpC.CSharp.Generator.CodeGen InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.Rpc.Impatient)), + IdentifierName(nameof(Capnp.Rpc.Impatient.Access)))) + .AddArgumentListArguments( + Argument( + _names.TaskParameter.IdentifierName), + Argument( + accessPath.IdentifierName), + Argument( InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nameof(Capnp.Rpc.Impatient)), - IdentifierName(nameof(Capnp.Rpc.Impatient.GetAnswer)))) - .AddArgumentListArguments( - Argument( - _names.TaskParameter.IdentifierName)), - IdentifierName(nameof(Capnp.Rpc.IPromisedAnswer.Access)))) - .AddArgumentListArguments( - Argument( - accessPath.IdentifierName), - Argument( - InvocationExpression( - _names.AwaitProxy.IdentifierName)))))))); + _names.AwaitProxy.IdentifierName)))))))); yield return pathDecl; yield return methodDecl;