diff --git a/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs b/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs index e7403cb..a83f2ae 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs @@ -259,7 +259,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod, Timeout(10000)] + [TestMethod, Timeout(20000)] public void ReleaseOnCancelClient() { // Since we have a threaded model, there is no way to deterministically provoke the situation @@ -774,7 +774,9 @@ namespace Capnp.Net.Runtime.Tests var cap = new TestCallOrderImpl(); cap.CountToDispose = 6; +#if DEBUG_DISPOSE Skeleton.BeginAssertNotDisposed(cap); +#endif var earlyCall = main.GetCallSequence(0, default); var echo = main.Echo(cap, default); @@ -809,7 +811,9 @@ namespace Capnp.Net.Runtime.Tests Assert.AreEqual(4u, call4.Result); Assert.AreEqual(5u, call5.Result); +#if DEBUG_DISPOSE Skeleton.EndAssertNotDisposed(cap); +#endif } } } diff --git a/Capnp.Net.Runtime/Rpc/RpcEngine.cs b/Capnp.Net.Runtime/Rpc/RpcEngine.cs index cb20bf6..be864fe 100644 --- a/Capnp.Net.Runtime/Rpc/RpcEngine.cs +++ b/Capnp.Net.Runtime/Rpc/RpcEngine.cs @@ -234,11 +234,11 @@ namespace Capnp.Rpc PendingQuestion AllocateQuestion(ConsumedCapability target, SerializerState inParams) { - uint questionId = RandId(); - var question = new PendingQuestion(this, questionId, target, inParams); - lock (_reentrancyBlocker) { + uint questionId = RandId(); + var question = new PendingQuestion(this, questionId, target, inParams); + while (!_questionTable.ReplacementTryAdd(questionId, question)) { questionId = RandId(); @@ -246,9 +246,9 @@ namespace Capnp.Rpc question = new PendingQuestion(this, questionId, target, inParams); oldQuestion.Dispose(); } - } - return question; + return question; + } } void DeleteQuestion(uint id, PendingQuestion question) diff --git a/Capnp.Net.Runtime/Rpc/Skeleton.cs b/Capnp.Net.Runtime/Rpc/Skeleton.cs index 2a098f9..2be39c9 100644 --- a/Capnp.Net.Runtime/Rpc/Skeleton.cs +++ b/Capnp.Net.Runtime/Rpc/Skeleton.cs @@ -29,7 +29,9 @@ namespace Capnp.Rpc } } +#if DEBUG_DISPOSE const int NoDisposeFlag = 0x4000000; +#endif static readonly ConditionalWeakTable _implMap = new ConditionalWeakTable(); @@ -61,6 +63,7 @@ namespace Capnp.Rpc return new SkeletonRelinquisher(GetOrCreateSkeleton(impl, true)); } +#if DEBUG_DISPOSE /// /// This DEBUG-only diagnostic method states that the Skeleton corresponding to a given capability is not expected to /// be disposed until the next call to EndAssertNotDisposed(). @@ -81,6 +84,7 @@ namespace Capnp.Rpc { GetOrCreateSkeleton(impl, false).EndAssertNotDisposed(); } +#endif int _refCount = 0; @@ -99,6 +103,7 @@ namespace Capnp.Rpc Interlocked.Increment(ref _refCount); } +#if DEBUG_DISPOSE internal void BeginAssertNotDisposed() { if ((Interlocked.Add(ref _refCount, NoDisposeFlag) & NoDisposeFlag) == 0) @@ -113,6 +118,7 @@ namespace Capnp.Rpc throw new InvalidOperationException("Flag already cleared. State is now broken."); } } +#endif internal void Relinquish() { @@ -120,8 +126,10 @@ namespace Capnp.Rpc if (0 == count) { +#if DEBUG_DISPOSE if ((count & NoDisposeFlag) != 0) throw new InvalidOperationException("Unexpected Skeleton disposal"); +#endif Dispose(true); GC.SuppressFinalize(this);