diff --git a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj
index 2d6ac0e..fec1cf6 100644
--- a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj
+++ b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj
@@ -24,7 +24,11 @@
- TRACE
+ TRACE;DEBUG;DebugCapabilityLifecycle
+
+
+
+ TRACE;DEBUG;DebugCapabilityLifecycle
diff --git a/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs b/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs
index 8152d44..da2919a 100644
--- a/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs
+++ b/Capnp.Net.Runtime/Rpc/ConsumedCapability.cs
@@ -22,7 +22,10 @@ namespace Capnp.Rpc
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 3328a5f..687a3f8 100644
--- a/Capnp.Net.Runtime/Rpc/LocalCapability.cs
+++ b/Capnp.Net.Runtime/Rpc/LocalCapability.cs
@@ -37,7 +37,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 e60e3dc..18b39d3 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;
using System.Threading.Tasks;
@@ -26,6 +27,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);
@@ -72,12 +81,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)
{
@@ -86,6 +103,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;