Browse Source

Add AllocationTrackingState and refactor tracking

pull/3120/head
James Jackson-South 3 months ago
parent
commit
631b64ea7d
  1. 18
      src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs
  2. 41
      src/ImageSharp/Memory/AllocationTrackingState.cs
  3. 20
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

18
src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs

@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Memory;
public abstract class AllocationTrackedMemoryManager<T> : MemoryManager<T>
where T : struct
{
private MemoryAllocator? trackingAllocator;
private long trackingLengthInBytes;
private int trackingReleased;
private AllocationTrackingState allocationTracking;
/// <summary>
/// Releases resources held by the concrete tracked owner.
@ -51,10 +49,7 @@ public abstract class AllocationTrackedMemoryManager<T> : MemoryManager<T>
/// Derived allocators should not call it themselves; they only construct the concrete owner.
/// </remarks>
internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes)
{
this.trackingAllocator = allocator;
this.trackingLengthInBytes = lengthInBytes;
}
=> this.allocationTracking.Attach(allocator, lengthInBytes);
/// <summary>
/// Releases any tracked allocation bytes associated with this instance.
@ -62,12 +57,5 @@ public abstract class AllocationTrackedMemoryManager<T> : MemoryManager<T>
/// <remarks>
/// Calling this more than once is safe; only the first call after tracking has been attached releases bytes.
/// </remarks>
private void ReleaseAllocationTracking()
{
if (Interlocked.Exchange(ref this.trackingReleased, 1) == 0 && this.trackingAllocator != null)
{
this.trackingAllocator.ReleaseAccumulatedBytes(this.trackingLengthInBytes);
this.trackingAllocator = null;
}
}
private void ReleaseAllocationTracking() => this.allocationTracking.Release();
}

41
src/ImageSharp/Memory/AllocationTrackingState.cs

@ -0,0 +1,41 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Memory;
/// <summary>
/// Tracks a single allocator reservation and releases it exactly once.
/// </summary>
/// <remarks>
/// This type is intended to live as a mutable field on the owning object. It should not be copied
/// after tracking has been attached, because the owner relies on a single shared release state.
/// </remarks>
internal struct AllocationTrackingState
{
private MemoryAllocator? allocator;
private long lengthInBytes;
private int released;
/// <summary>
/// Attaches allocator reservation tracking to the current owner.
/// </summary>
/// <param name="allocator">The allocator that owns the reservation.</param>
/// <param name="lengthInBytes">The reserved allocation size, in bytes.</param>
internal void Attach(MemoryAllocator allocator, long lengthInBytes)
{
this.allocator = allocator;
this.lengthInBytes = lengthInBytes;
}
/// <summary>
/// Releases the attached allocator reservation once.
/// </summary>
internal void Release()
{
if (Interlocked.Exchange(ref this.released, 1) == 0 && this.allocator != null)
{
this.allocator.ReleaseAccumulatedBytes(this.lengthInBytes);
this.allocator = null;
}
}
}

20
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

@ -21,10 +21,8 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
{
private static readonly int ElementSize = Unsafe.SizeOf<T>();
private AllocationTrackingState allocationTracking;
private MemoryGroupSpanCache memoryGroupSpanCache;
private MemoryAllocator? trackingAllocator;
private long trackingLengthInBytes;
private int trackingReleased;
private MemoryGroup(int bufferLength, long totalLength)
{
@ -64,11 +62,8 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
/// Intended for one-time initialization after the group has been created; callers should avoid changing
/// tracking state concurrently with disposal.
/// </remarks>
internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes)
{
this.trackingAllocator = allocator;
this.trackingLengthInBytes = lengthInBytes;
}
internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) =>
this.allocationTracking.Attach(allocator, lengthInBytes);
/// <summary>
/// Releases any resources or tracking information associated with allocation tracking for this instance.
@ -77,14 +72,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
/// This method is intended to be called when allocation tracking is no longer needed. It is safe
/// to call multiple times; subsequent calls after the first have no effect, even when called concurrently.
/// </remarks>
internal void ReleaseAllocationTracking()
{
if (Interlocked.Exchange(ref this.trackingReleased, 1) == 0 && this.trackingAllocator != null)
{
this.trackingAllocator.ReleaseAccumulatedBytes(this.trackingLengthInBytes);
this.trackingAllocator = null;
}
}
internal void ReleaseAllocationTracking() => this.allocationTracking.Release();
/// <inheritdoc />
IEnumerator<Memory<T>> IEnumerable<Memory<T>>.GetEnumerator()

Loading…
Cancel
Save