From b16fd4c6859e0ebce36222c958c0d93631784702 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 26 Jan 2022 01:44:55 +0100 Subject: [PATCH] implemented TotalUndisposedAllocationCount --- .../Diagnostics/MemoryDiagnostics.cs | 19 ++++---- src/ImageSharp/Diagnostics/MemoryInfo.cs | 13 ++++++ ...rd.cs => RefCountedMemoryLifetimeGuard.cs} | 46 +++++++++++-------- .../Internals/SharedArrayPoolBuffer{T}.cs | 2 +- ...iformUnmanagedMemoryPool.LifetimeGuards.cs | 4 +- .../Internals/UnmanagedBufferLifetimeGuard.cs | 2 +- .../MemoryGroup{T}.Owned.cs | 2 +- .../Allocators/MemoryDiagnosticsTests.cs | 1 - .../RefCountedLifetimeGuardTests.cs | 2 +- 9 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 src/ImageSharp/Diagnostics/MemoryInfo.cs rename src/ImageSharp/Memory/Allocators/Internals/{RefCountedLifetimeGuard.cs => RefCountedMemoryLifetimeGuard.cs} (61%) 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; }