diff --git a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
index fa9a1b8c2f..419f7531f0 100644
--- a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
+++ b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
@@ -2,21 +2,22 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Threading;
namespace SixLabors.ImageSharp.Diagnostics
{
- public readonly struct MemoryInfo
- {
- internal MemoryInfo(long totalUndisposedAllocationCount)
- => this.TotalUndisposedAllocationCount = totalUndisposedAllocationCount;
-
- public long TotalUndisposedAllocationCount { get; }
- }
-
public static class MemoryDiagnostics
{
- public static MemoryInfo GetMemoryInfo() => throw new NotImplementedException();
+ private static int totalUndisposedAllocationCount;
+
+ public static MemoryInfo GetMemoryInfo() => new MemoryInfo(totalUndisposedAllocationCount);
public static bool EnableStrictDisposeWatcher { get; set; }
+
+ internal static void IncrementTotalUndisposedAllocationCount() =>
+ Interlocked.Increment(ref totalUndisposedAllocationCount);
+
+ internal static void DecrementTotalUndisposedAllocationCount() =>
+ Interlocked.Decrement(ref totalUndisposedAllocationCount);
}
}
diff --git a/src/ImageSharp/Diagnostics/MemoryInfo.cs b/src/ImageSharp/Diagnostics/MemoryInfo.cs
new file mode 100644
index 0000000000..54ae83bd0a
--- /dev/null
+++ b/src/ImageSharp/Diagnostics/MemoryInfo.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Diagnostics
+{
+ public readonly struct MemoryInfo
+ {
+ internal MemoryInfo(long totalUndisposedAllocationCount)
+ => this.TotalUndisposedAllocationCount = totalUndisposedAllocationCount;
+
+ public long TotalUndisposedAllocationCount { get; }
+ }
+}
diff --git a/src/ImageSharp/Memory/Allocators/Internals/RefCountedLifetimeGuard.cs b/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
similarity index 61%
rename from src/ImageSharp/Memory/Allocators/Internals/RefCountedLifetimeGuard.cs
rename to src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
index 61682aa567..534cec0f8f 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/RefCountedLifetimeGuard.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
@@ -4,42 +4,33 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
+using SixLabors.ImageSharp.Diagnostics;
namespace SixLabors.ImageSharp.Memory.Internals
{
///
- /// Implements reference counting lifetime guard mechanism similar to the one provided by ,
- /// but without the restriction of the guarded object being a handle.
+ /// Implements reference counting lifetime guard mechanism for memory resources
+ /// also maintaining the current value of .
///
- internal abstract class RefCountedLifetimeGuard : IDisposable
+ internal abstract class RefCountedMemoryLifetimeGuard : IDisposable
{
private int refCount = 1;
private int disposed;
private int released;
- ~RefCountedLifetimeGuard()
+ public RefCountedMemoryLifetimeGuard() => MemoryDiagnostics.IncrementTotalUndisposedAllocationCount();
+
+ ~RefCountedMemoryLifetimeGuard()
{
Interlocked.Exchange(ref this.disposed, 1);
- this.ReleaseRef();
+ this.ReleaseRef(true);
}
public bool IsDisposed => this.disposed == 1;
public void AddRef() => Interlocked.Increment(ref this.refCount);
- public void ReleaseRef()
- {
- Interlocked.Decrement(ref this.refCount);
- if (this.refCount == 0)
- {
- int wasReleased = Interlocked.Exchange(ref this.released, 1);
-
- if (wasReleased == 0)
- {
- this.Release();
- }
- }
- }
+ public void ReleaseRef() => this.ReleaseRef(false);
public void Dispose()
{
@@ -52,5 +43,24 @@ namespace SixLabors.ImageSharp.Memory.Internals
}
protected abstract void Release();
+
+ private void ReleaseRef(bool finalizing)
+ {
+ Interlocked.Decrement(ref this.refCount);
+ if (this.refCount == 0)
+ {
+ int wasReleased = Interlocked.Exchange(ref this.released, 1);
+
+ if (wasReleased == 0)
+ {
+ if (!finalizing)
+ {
+ MemoryDiagnostics.DecrementTotalUndisposedAllocationCount();
+ }
+
+ this.Release();
+ }
+ }
+ }
}
}
diff --git a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
index 9302c67e7d..2ea76da957 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
@@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
}
}
- private sealed class LifetimeGuard : RefCountedLifetimeGuard
+ private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard
{
private byte[] array;
diff --git a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs
index 666b248552..151fef69c4 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs
@@ -20,9 +20,9 @@ namespace SixLabors.ImageSharp.Memory.Internals
return buffer;
}
- public RefCountedLifetimeGuard CreateGroupLifetimeGuard(UnmanagedMemoryHandle[] handles) => new GroupLifetimeGuard(this, handles);
+ public RefCountedMemoryLifetimeGuard CreateGroupLifetimeGuard(UnmanagedMemoryHandle[] handles) => new GroupLifetimeGuard(this, handles);
- private sealed class GroupLifetimeGuard : RefCountedLifetimeGuard
+ private sealed class GroupLifetimeGuard : RefCountedMemoryLifetimeGuard
{
private readonly UniformUnmanagedMemoryPool pool;
private readonly UnmanagedMemoryHandle[] handles;
diff --git a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs
index 5f0759f203..8221120240 100644
--- a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs
+++ b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBufferLifetimeGuard.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
///
/// Defines a strategy for managing unmanaged memory ownership.
///
- internal abstract class UnmanagedBufferLifetimeGuard : RefCountedLifetimeGuard
+ internal abstract class UnmanagedBufferLifetimeGuard : RefCountedMemoryLifetimeGuard
{
private UnmanagedMemoryHandle handle;
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
index 21faf8e562..01aac3148e 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Memory
public sealed class Owned : MemoryGroup, IEnumerable>
{
private IMemoryOwner[] memoryOwners;
- private RefCountedLifetimeGuard groupLifetimeGuard;
+ private RefCountedMemoryLifetimeGuard groupLifetimeGuard;
public Owned(IMemoryOwner[] memoryOwners, int bufferLength, long totalLength, bool swappable)
: base(bufferLength, totalLength)
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs
index 11a70d1cb5..55f14fdfa7 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Microsoft.DotNet.RemoteExecutor;
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs
index 7fb3b7b7bb..4b808e8630 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs
@@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
}
}
- private class MockLifetimeGuard : RefCountedLifetimeGuard
+ private class MockLifetimeGuard : RefCountedMemoryLifetimeGuard
{
public int ReleaseInvocationCount { get; private set; }