diff --git a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj index 3e741a9..3f0b362 100644 --- a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj +++ b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj @@ -25,7 +25,11 @@ - TRACE + TRACE;DEBUG + + + + DebugCapabilityLifecycle diff --git a/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs b/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs index d628571..1035f0b 100644 --- a/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs +++ b/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs @@ -18,7 +18,10 @@ internal abstract void Unfreeze(); internal abstract void AddRef(); - internal abstract void Release(); + internal abstract void Release( + [System.Runtime.CompilerServices.CallerMemberName] string methodName = "", + [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0); #if DebugFinalizers public string CreatorMemberName { get; set; } diff --git a/Capnp.Net.Runtime/Rpc/LocalCapability.cs b/Capnp.Net.Runtime/Rpc/LocalCapability.cs index 4bff9e6..23b0ced 100644 --- a/Capnp.Net.Runtime/Rpc/LocalCapability.cs +++ b/Capnp.Net.Runtime/Rpc/LocalCapability.cs @@ -36,7 +36,10 @@ namespace Capnp.Rpc ProvidedCap.Claim(); } - internal override void Release() + internal override void Release( + [System.Runtime.CompilerServices.CallerMemberName] string methodName = "", + [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0) { ProvidedCap.Relinquish(); } diff --git a/Capnp.Net.Runtime/Rpc/RefCountingCapability.cs b/Capnp.Net.Runtime/Rpc/RefCountingCapability.cs index 290cca4..695a91c 100644 --- a/Capnp.Net.Runtime/Rpc/RefCountingCapability.cs +++ b/Capnp.Net.Runtime/Rpc/RefCountingCapability.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Logging; +using System; using System.Threading.Tasks; namespace Capnp.Rpc @@ -25,6 +26,14 @@ namespace Capnp.Rpc // Value 0 has the special meaning of being in state C. long _refCount = 1; +#if DebugCapabilityLifecycle + ILogger Logger { get; } = Logging.CreateLogger(); + + string _releasingMethodName; + string _releasingFilePath; + int _releasingLineNumber; +#endif + ~RefCountingCapability() { Dispose(false); @@ -71,12 +80,20 @@ namespace Capnp.Rpc { --_refCount; +#if DebugCapabilityLifecycle + Logger.LogError($"Attempted to add reference to capability which was already released. " + + $"Releasing entity: {_releasingFilePath}, line {_releasingLineNumber}, method {_releasingMethodName}" + + $"Current stack trace: {Environment.StackTrace}"); +#endif throw new ObjectDisposedException(ToString(), "Attempted to add reference to capability which was already released"); } } } - internal sealed override void Release() + internal sealed override void Release( + [System.Runtime.CompilerServices.CallerMemberName] string methodName = "", + [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", + [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0) { lock (_reentrancyBlocker) { @@ -85,6 +102,13 @@ namespace Capnp.Rpc case 1: // initial state, actually ref. count 0 case 2: // actually ref. count 1 _refCount = 0; + +#if DebugCapabilityLifecycle + _releasingMethodName = methodName; + _releasingFilePath = filePath; + _releasingLineNumber = lineNumber; +#endif + Dispose(true); GC.SuppressFinalize(this); break;