mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 14:51:41 +01:00
attempting to fix race cond. with StrictlyOrderedAwaitTask
This commit is contained in:
parent
e496a19e3d
commit
16c9f8871a
@ -48,7 +48,12 @@ namespace Capnp.Net.Runtime.Tests
|
||||
{
|
||||
readonly TaskCompletionSource<DeserializerState> _tcs = new TaskCompletionSource<DeserializerState>();
|
||||
|
||||
public Task<DeserializerState> WhenReturned => _tcs.Task;
|
||||
public PromisedAnswerMock()
|
||||
{
|
||||
WhenReturned = _tcs.Task.EnforceAwaitOrder();
|
||||
}
|
||||
|
||||
public StrictlyOrderedAwaitTask<DeserializerState> WhenReturned { get; }
|
||||
|
||||
public void Return()
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Capnp.Net.Runtime.Tests.GenImpls;
|
||||
using Capnp.Rpc;
|
||||
using Capnp.Util;
|
||||
using Capnproto_test.Capnp.Test;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
@ -140,7 +141,12 @@ namespace Capnp.Net.Runtime.Tests
|
||||
class PromisedAnswerMock : IPromisedAnswer
|
||||
{
|
||||
readonly TaskCompletionSource<DeserializerState> _tcs = new TaskCompletionSource<DeserializerState>();
|
||||
public Task<DeserializerState> WhenReturned => _tcs.Task;
|
||||
public StrictlyOrderedAwaitTask<DeserializerState> WhenReturned { get; }
|
||||
|
||||
public PromisedAnswerMock()
|
||||
{
|
||||
WhenReturned = _tcs.Task.EnforceAwaitOrder();
|
||||
}
|
||||
|
||||
public bool IsTailCall => false;
|
||||
|
||||
@ -164,7 +170,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var answer = new PromisedAnswerMock();
|
||||
Assert.ThrowsException<ArgumentException>(() => Impatient.GetAnswer(answer.WhenReturned));
|
||||
Assert.ThrowsException<ArgumentException>(() => Impatient.GetAnswer(Task.FromResult(new object())));
|
||||
var t = Impatient.MakePipelineAware(answer, _ => _);
|
||||
Assert.AreEqual(answer, Impatient.GetAnswer(t));
|
||||
#pragma warning restore CS0618
|
||||
@ -174,7 +180,8 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public async Task Access()
|
||||
{
|
||||
var answer = new PromisedAnswerMock();
|
||||
var cap = Impatient.Access(answer.WhenReturned, new MemberAccessPath(), Task.FromResult<IDisposable>(new TestInterfaceImpl2()));
|
||||
async Task AwaitReturn() => await answer.WhenReturned;
|
||||
var cap = Impatient.Access(AwaitReturn(), new MemberAccessPath(), Task.FromResult<IDisposable>(new TestInterfaceImpl2()));
|
||||
using (var proxy = new BareProxy(cap))
|
||||
{
|
||||
await proxy.WhenResolved;
|
||||
|
@ -608,6 +608,7 @@ namespace Capnp.Net.Runtime.Tests.GenImpls
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Assert.AreEqual(expected, _counter);
|
||||
return Task.FromResult(_counter++);
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
result.WriteData(0, 654321);
|
||||
mock.Return.SetResult(result);
|
||||
|
||||
Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
var outresult = answer.WhenReturned.Result;
|
||||
Assert.AreEqual(ObjectKind.Struct, outresult.Kind);
|
||||
Assert.AreEqual(654321, outresult.ReadDataInt(0));
|
||||
@ -170,7 +170,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
mock.Return.SetCanceled();
|
||||
|
||||
Assert.IsTrue(Assert.ThrowsExceptionAsync<TaskCanceledException>(() => answer.WhenReturned).Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(Assert.ThrowsExceptionAsync<TaskCanceledException>(async () => await answer.WhenReturned).Wait(MediumNonDbgTimeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,7 +266,8 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
// Even after the client cancelled the call, the server must still send
|
||||
// a response.
|
||||
Assert.IsTrue(answer.WhenReturned.ContinueWith(t => { }).Wait(MediumNonDbgTimeout));
|
||||
async Task AwaitWhenReturned() => await answer.WhenReturned;
|
||||
Assert.IsTrue(AwaitWhenReturned().ContinueWith(t => { }).Wait(MediumNonDbgTimeout));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -312,7 +313,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
mock.Return.SetException(new MyTestException());
|
||||
|
||||
var exTask = Assert.ThrowsExceptionAsync<RpcException>(() => answer.WhenReturned);
|
||||
var exTask = Assert.ThrowsExceptionAsync<RpcException>(async () => await answer.WhenReturned);
|
||||
Assert.IsTrue(exTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(exTask.Result.Message.Contains(new MyTestException().Message));
|
||||
}
|
||||
@ -367,7 +368,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
mock.Return.SetResult(result);
|
||||
|
||||
Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsFalse(ct.IsCancellationRequested);
|
||||
|
||||
Assert.IsTrue(mock2.WhenCalled.Wait(MediumNonDbgTimeout));
|
||||
@ -383,7 +384,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
result2.WriteData(0, 222222);
|
||||
mock2.Return.SetResult(result2);
|
||||
|
||||
Assert.IsTrue(answer2.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer2.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
var outresult2 = answer2.WhenReturned.Result;
|
||||
Assert.AreEqual(ObjectKind.Struct, outresult2.Kind);
|
||||
Assert.AreEqual(222222, outresult2.ReadDataInt(0));
|
||||
@ -443,7 +444,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
using (var answer2 = pipelined.Call(0x8765432187654321, 0x4444, args2))
|
||||
{
|
||||
Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(mock2.WhenCalled.Wait(MediumNonDbgTimeout));
|
||||
|
||||
(var interfaceId2, var methodId2, var inargs2, var ct2) = mock2.WhenCalled.Result;
|
||||
@ -457,7 +458,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
result2.WriteData(0, 222222);
|
||||
mock2.Return.SetResult(result2);
|
||||
|
||||
Assert.IsTrue(answer2.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer2.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
var outresult2 = answer2.WhenReturned.Result;
|
||||
Assert.AreEqual(ObjectKind.Struct, outresult2.Kind);
|
||||
Assert.AreEqual(222222, outresult2.ReadDataInt(0));
|
||||
@ -521,7 +522,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
mock.Return.SetResult(result);
|
||||
|
||||
Assert.IsTrue(answer.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsFalse(ct.IsCancellationRequested);
|
||||
|
||||
var args4 = DynamicSerializerState.CreateForRpc();
|
||||
@ -570,10 +571,10 @@ namespace Capnp.Net.Runtime.Tests
|
||||
ret5.WriteData(0, -4);
|
||||
call5.Result.Result.SetResult(ret5);
|
||||
|
||||
Assert.IsTrue(answer2.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer3.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer4.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer5.WhenReturned.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer2.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer3.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer4.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(answer5.WhenReturned.WrappedTask.Wait(MediumNonDbgTimeout));
|
||||
|
||||
Assert.AreEqual(-1, answer2.WhenReturned.Result.ReadDataInt(0));
|
||||
Assert.AreEqual(-2, answer3.WhenReturned.Result.ReadDataInt(0));
|
||||
@ -686,7 +687,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
mock.Return.SetResult(result);
|
||||
|
||||
Assert.IsTrue(Assert.ThrowsExceptionAsync<TaskCanceledException>(
|
||||
() => answer2.WhenReturned).Wait(MediumNonDbgTimeout));
|
||||
async () => await answer2.WhenReturned).Wait(MediumNonDbgTimeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
{
|
||||
var fooTask2 = main2.Foo(123, null);
|
||||
Assert.IsTrue(fooTask2.Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(fooTask2.C().GetCallSequence(1).Wait(MediumNonDbgTimeout));
|
||||
Assert.IsTrue(fooTask2.C().GetCallSequence(0).Wait(MediumNonDbgTimeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -423,8 +423,8 @@ namespace Capnp.Net.Runtime.Tests
|
||||
Assert.AreEqual(456u, promise.Result.I);
|
||||
Assert.AreEqual("from TestTailCaller", promise.Result.T);
|
||||
|
||||
var dependentCall1 = c.GetCallSequence(0, default);
|
||||
var dependentCall2 = c.GetCallSequence(0, default);
|
||||
var dependentCall1 = c.GetCallSequence(1, default);
|
||||
var dependentCall2 = c.GetCallSequence(2, default);
|
||||
|
||||
AssertOutput(stdout, "foo");
|
||||
Assert.IsTrue(dependentCall0.Wait(MediumNonDbgTimeout));
|
||||
|
@ -276,7 +276,7 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public void RunTest(Action<ITestbed> action)
|
||||
{
|
||||
(_server, _client) = SetupClientServerPair(_options);
|
||||
Assert.IsTrue(SpinWait.SpinUntil(() => _server.ConnectionCount > 0, 2 * MediumNonDbgTimeout));
|
||||
Assert.IsTrue(SpinWait.SpinUntil(() => _server.ConnectionCount > 0, LargeNonDbgTimeout));
|
||||
var conn = _server.Connections[0];
|
||||
|
||||
using (_server)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capnp.Rpc
|
||||
@ -15,7 +16,7 @@ namespace Capnp.Rpc
|
||||
/// <summary>
|
||||
/// Task which will complete when the RPC returns, delivering its result struct.
|
||||
/// </summary>
|
||||
Task<DeserializerState> WhenReturned { get; }
|
||||
StrictlyOrderedAwaitTask<DeserializerState> WhenReturned { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a low-level capability for promise pipelining.
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -20,14 +21,15 @@ namespace Capnp.Rpc.Interception
|
||||
public PromisedAnswer(CallContext callContext)
|
||||
{
|
||||
_callContext = callContext;
|
||||
WhenReturned = _futureResult.Task.EnforceAwaitOrder();
|
||||
}
|
||||
|
||||
public Task<DeserializerState> WhenReturned => _futureResult.Task;
|
||||
public StrictlyOrderedAwaitTask<DeserializerState> WhenReturned { get; }
|
||||
public CancellationToken CancelFromAlice => _cancelFromAlice.Token;
|
||||
|
||||
public ConsumedCapability Access(MemberAccessPath access)
|
||||
{
|
||||
return _callContext._censorCapability.Policy.Attach<ConsumedCapability>(new LocalAnswerCapability(_futureResult.Task, access));
|
||||
return _callContext._censorCapability.Policy.Attach<ConsumedCapability>(new LocalAnswerCapability(WhenReturned, access));
|
||||
}
|
||||
|
||||
public ConsumedCapability Access(MemberAccessPath _, Task<IDisposable?> task)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -11,7 +12,7 @@ namespace Capnp.Rpc
|
||||
public LocalAnswer(CancellationTokenSource cts, Task<DeserializerState> call)
|
||||
{
|
||||
_cts = cts ?? throw new ArgumentNullException(nameof(cts));
|
||||
WhenReturned = call ?? throw new ArgumentNullException(nameof(call));
|
||||
WhenReturned = call?.EnforceAwaitOrder() ?? throw new ArgumentNullException(nameof(call));
|
||||
|
||||
CleanupAfterReturn();
|
||||
}
|
||||
@ -23,7 +24,7 @@ namespace Capnp.Rpc
|
||||
finally { _cts.Dispose(); }
|
||||
}
|
||||
|
||||
public Task<DeserializerState> WhenReturned { get; }
|
||||
public StrictlyOrderedAwaitTask<DeserializerState> WhenReturned { get; }
|
||||
|
||||
public bool IsTailCall => false;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -7,7 +8,7 @@ namespace Capnp.Rpc
|
||||
|
||||
class LocalAnswerCapability : RefCountingCapability, IResolvingCapability
|
||||
{
|
||||
static async Task<Proxy> TransferOwnershipToDummyProxy(Task<DeserializerState> answer, MemberAccessPath access)
|
||||
static async Task<Proxy> TransferOwnershipToDummyProxy(StrictlyOrderedAwaitTask<DeserializerState> answer, MemberAccessPath access)
|
||||
{
|
||||
var result = await answer;
|
||||
var cap = access.Eval(result);
|
||||
@ -23,7 +24,7 @@ namespace Capnp.Rpc
|
||||
_whenResolvedProxy = proxyTask;
|
||||
}
|
||||
|
||||
public LocalAnswerCapability(Task<DeserializerState> answer, MemberAccessPath access):
|
||||
public LocalAnswerCapability(StrictlyOrderedAwaitTask<DeserializerState> answer, MemberAccessPath access):
|
||||
this(TransferOwnershipToDummyProxy(answer, access))
|
||||
{
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
@ -53,6 +54,7 @@ namespace Capnp.Rpc
|
||||
}
|
||||
|
||||
readonly TaskCompletionSource<DeserializerState> _tcs = new TaskCompletionSource<DeserializerState>();
|
||||
readonly StrictlyOrderedAwaitTask<DeserializerState> _whenReturned;
|
||||
readonly uint _questionId;
|
||||
ConsumedCapability? _target;
|
||||
SerializerState? _inParams;
|
||||
@ -64,6 +66,8 @@ namespace Capnp.Rpc
|
||||
_questionId = id;
|
||||
_target = target;
|
||||
_inParams = inParams;
|
||||
_whenReturned = _tcs.Task.EnforceAwaitOrder();
|
||||
|
||||
StateFlags = inParams == null ? State.Sent : State.None;
|
||||
|
||||
if (target != null)
|
||||
@ -81,7 +85,7 @@ namespace Capnp.Rpc
|
||||
/// <summary>
|
||||
/// Eventually returns the server answer
|
||||
/// </summary>
|
||||
public Task<DeserializerState> WhenReturned => _tcs.Task;
|
||||
public StrictlyOrderedAwaitTask<DeserializerState> WhenReturned => _whenReturned;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this question represents a tail call
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -72,7 +73,7 @@ namespace Capnp.Rpc
|
||||
return null;
|
||||
}
|
||||
|
||||
async void TrackCall(Task call)
|
||||
async void TrackCall(StrictlyOrderedAwaitTask call)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Capnp.Util;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capnp.Rpc
|
||||
@ -38,7 +39,7 @@ namespace Capnp.Rpc
|
||||
{
|
||||
}
|
||||
|
||||
async void ReAllowFinishWhenDone(Task task)
|
||||
async void ReAllowFinishWhenDone(StrictlyOrderedAwaitTask task)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Capnp.Util
|
||||
{
|
||||
internal class StrictlyOrderedAwaitTask: INotifyCompletion
|
||||
public class StrictlyOrderedAwaitTask: INotifyCompletion
|
||||
{
|
||||
class Cover { }
|
||||
class Seal { }
|
||||
@ -99,7 +99,7 @@ namespace Capnp.Util
|
||||
public Task WrappedTask => _awaitedTask;
|
||||
}
|
||||
|
||||
internal class StrictlyOrderedAwaitTask<T> : StrictlyOrderedAwaitTask
|
||||
public class StrictlyOrderedAwaitTask<T> : StrictlyOrderedAwaitTask
|
||||
{
|
||||
public StrictlyOrderedAwaitTask(Task<T> awaitedTask): base(awaitedTask)
|
||||
{
|
||||
@ -115,7 +115,7 @@ namespace Capnp.Util
|
||||
}
|
||||
|
||||
|
||||
internal static class StrictlyOrderedTaskExtensions
|
||||
public static class StrictlyOrderedTaskExtensions
|
||||
{
|
||||
public static StrictlyOrderedAwaitTask<T> EnforceAwaitOrder<T>(this Task<T> task)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user