Skip to content

Commit 7f122be

Browse files
Add AllocationTrackingState and refactor tracking
1 parent 35e14e7 commit 7f122be

3 files changed

Lines changed: 48 additions & 31 deletions

File tree

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

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Memory;
1717
public abstract class AllocationTrackedMemoryManager<T> : MemoryManager<T>
1818
where T : struct
1919
{
20-
private MemoryAllocator? trackingAllocator;
21-
private long trackingLengthInBytes;
22-
private int trackingReleased;
20+
private AllocationTrackingState allocationTracking;
2321

2422
/// <summary>
2523
/// Releases resources held by the concrete tracked owner.
@@ -51,23 +49,13 @@ protected sealed override void Dispose(bool disposing)
5149
/// Derived allocators should not call it themselves; they only construct the concrete owner.
5250
/// </remarks>
5351
internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes)
54-
{
55-
this.trackingAllocator = allocator;
56-
this.trackingLengthInBytes = lengthInBytes;
57-
}
52+
=> this.allocationTracking.Attach(allocator, lengthInBytes);
5853

5954
/// <summary>
6055
/// Releases any tracked allocation bytes associated with this instance.
6156
/// </summary>
6257
/// <remarks>
6358
/// Calling this more than once is safe; only the first call after tracking has been attached releases bytes.
6459
/// </remarks>
65-
private void ReleaseAllocationTracking()
66-
{
67-
if (Interlocked.Exchange(ref this.trackingReleased, 1) == 0 && this.trackingAllocator != null)
68-
{
69-
this.trackingAllocator.ReleaseAccumulatedBytes(this.trackingLengthInBytes);
70-
this.trackingAllocator = null;
71-
}
72-
}
60+
private void ReleaseAllocationTracking() => this.allocationTracking.Release();
7361
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Memory;
5+
6+
/// <summary>
7+
/// Tracks a single allocator reservation and releases it exactly once.
8+
/// </summary>
9+
/// <remarks>
10+
/// This type is intended to live as a mutable field on the owning object. It should not be copied
11+
/// after tracking has been attached, because the owner relies on a single shared release state.
12+
/// </remarks>
13+
internal struct AllocationTrackingState
14+
{
15+
private MemoryAllocator? allocator;
16+
private long lengthInBytes;
17+
private int released;
18+
19+
/// <summary>
20+
/// Attaches allocator reservation tracking to the current owner.
21+
/// </summary>
22+
/// <param name="allocator">The allocator that owns the reservation.</param>
23+
/// <param name="lengthInBytes">The reserved allocation size, in bytes.</param>
24+
internal void Attach(MemoryAllocator allocator, long lengthInBytes)
25+
{
26+
this.allocator = allocator;
27+
this.lengthInBytes = lengthInBytes;
28+
}
29+
30+
/// <summary>
31+
/// Releases the attached allocator reservation once.
32+
/// </summary>
33+
internal void Release()
34+
{
35+
if (Interlocked.Exchange(ref this.released, 1) == 0 && this.allocator != null)
36+
{
37+
this.allocator.ReleaseAccumulatedBytes(this.lengthInBytes);
38+
this.allocator = null;
39+
}
40+
}
41+
}

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
2121
{
2222
private static readonly int ElementSize = Unsafe.SizeOf<T>();
2323

24+
private AllocationTrackingState allocationTracking;
2425
private MemoryGroupSpanCache memoryGroupSpanCache;
25-
private MemoryAllocator? trackingAllocator;
26-
private long trackingLengthInBytes;
27-
private int trackingReleased;
2826

2927
private MemoryGroup(int bufferLength, long totalLength)
3028
{
@@ -64,11 +62,8 @@ private MemoryGroup(int bufferLength, long totalLength)
6462
/// Intended for one-time initialization after the group has been created; callers should avoid changing
6563
/// tracking state concurrently with disposal.
6664
/// </remarks>
67-
internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes)
68-
{
69-
this.trackingAllocator = allocator;
70-
this.trackingLengthInBytes = lengthInBytes;
71-
}
65+
internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) =>
66+
this.allocationTracking.Attach(allocator, lengthInBytes);
7267

7368
/// <summary>
7469
/// Releases any resources or tracking information associated with allocation tracking for this instance.
@@ -77,14 +72,7 @@ internal void AttachAllocationTracking(MemoryAllocator allocator, long lengthInB
7772
/// This method is intended to be called when allocation tracking is no longer needed. It is safe
7873
/// to call multiple times; subsequent calls after the first have no effect, even when called concurrently.
7974
/// </remarks>
80-
internal void ReleaseAllocationTracking()
81-
{
82-
if (Interlocked.Exchange(ref this.trackingReleased, 1) == 0 && this.trackingAllocator != null)
83-
{
84-
this.trackingAllocator.ReleaseAccumulatedBytes(this.trackingLengthInBytes);
85-
this.trackingAllocator = null;
86-
}
87-
}
75+
internal void ReleaseAllocationTracking() => this.allocationTracking.Release();
8876

8977
/// <inheritdoc />
9078
IEnumerator<Memory<T>> IEnumerable<Memory<T>>.GetEnumerator()

0 commit comments

Comments
 (0)