Skip to content

Commit 10f749d

Browse files
Merge pull request #3123 from SixLabors/js/revert-3120
Revert "Merge pull request #3056 from SixLabors/js/accumulative-memor…
2 parents 1bc4207 + c470a9a commit 10f749d

12 files changed

Lines changed: 45 additions & 464 deletions

src/ImageSharp/Memory/Allocators/MemoryAllocator.cs

Lines changed: 4 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.Memory;
1212
public abstract class MemoryAllocator
1313
{
1414
private const int OneGigabyte = 1 << 30;
15-
private long accumulativeAllocatedBytes;
16-
private int trackingSuppressionCount;
1715

1816
/// <summary>
1917
/// Gets the default platform-specific global <see cref="MemoryAllocator"/> instance that
@@ -25,44 +23,9 @@ public abstract class MemoryAllocator
2523
/// </summary>
2624
public static MemoryAllocator Default { get; } = Create();
2725

28-
/// <summary>
29-
/// Gets the maximum number of bytes that can be allocated by a memory group.
30-
/// </summary>
31-
/// <remarks>
32-
/// The allocation limit is determined by the process architecture: 4 GB for 64-bit processes and
33-
/// 1 GB for 32-bit processes.
34-
/// </remarks>
35-
internal long MemoryGroupAllocationLimitBytes { get; private protected set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte;
36-
37-
/// <summary>
38-
/// Gets the maximum allowed total allocation size, in bytes, for the current process.
39-
/// </summary>
40-
/// <remarks>
41-
/// Defaults to <see cref="long.MaxValue"/>, effectively imposing no limit on total allocations.
42-
/// This property can be set to enforce a cap on total memory usage across all allocations made through this allocator instance, providing
43-
/// a safeguard against excessive memory consumption.<br/>
44-
/// When the cumulative size of active allocations exceeds this limit, an <see cref="InvalidMemoryOperationException"/> will be thrown to
45-
/// prevent further allocations and signal that the limit has been breached.
46-
/// </remarks>
47-
internal long AccumulativeAllocationLimitBytes { get; private protected set; } = long.MaxValue;
26+
internal long MemoryGroupAllocationLimitBytes { get; private set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte;
4827

49-
/// <summary>
50-
/// Gets the maximum size, in bytes, that can be allocated for a single buffer.
51-
/// </summary>
52-
/// <remarks>
53-
/// The single buffer allocation limit is set to 1 GB by default.
54-
/// </remarks>
55-
internal int SingleBufferAllocationLimitBytes { get; private protected set; } = OneGigabyte;
56-
57-
/// <summary>
58-
/// Gets a value indicating whether change tracking is currently suppressed for this instance.
59-
/// </summary>
60-
/// <remarks>
61-
/// When change tracking is suppressed, modifications to the object will not be recorded or
62-
/// trigger change notifications. This property is used internally to temporarily disable tracking during
63-
/// batch updates or initialization.
64-
/// </remarks>
65-
private bool IsTrackingSuppressed => Volatile.Read(ref this.trackingSuppressionCount) > 0;
28+
internal int SingleBufferAllocationLimitBytes { get; private set; } = OneGigabyte;
6629

6730
/// <summary>
6831
/// Gets the length of the largest contiguous buffer that can be handled by this allocator instance in bytes.
@@ -90,11 +53,6 @@ public static MemoryAllocator Create(MemoryAllocatorOptions options)
9053
allocator.SingleBufferAllocationLimitBytes = (int)Math.Min(allocator.SingleBufferAllocationLimitBytes, allocator.MemoryGroupAllocationLimitBytes);
9154
}
9255

93-
if (options.AccumulativeAllocationLimitMegabytes.HasValue)
94-
{
95-
allocator.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L;
96-
}
97-
9856
return allocator;
9957
}
10058

@@ -114,10 +72,6 @@ public abstract IMemoryOwner<T> Allocate<T>(int length, AllocationOptions option
11472
/// Releases all retained resources not being in use.
11573
/// Eg: by resetting array pools and letting GC to free the arrays.
11674
/// </summary>
117-
/// <remarks>
118-
/// This does not dispose active allocations; callers are responsible for disposing all
119-
/// <see cref="IMemoryOwner{T}"/> instances to release memory.
120-
/// </remarks>
12175
public virtual void ReleaseRetainedResources()
12276
{
12377
}
@@ -148,137 +102,11 @@ internal MemoryGroup<T> AllocateGroup<T>(
148102
InvalidMemoryOperationException.ThrowAllocationOverLimitException(totalLengthInBytes, this.MemoryGroupAllocationLimitBytes);
149103
}
150104

151-
long totalLengthInBytesLong = (long)totalLengthInBytes;
152-
this.ReserveAllocation(totalLengthInBytesLong);
153-
154-
using (this.SuppressTracking())
155-
{
156-
try
157-
{
158-
MemoryGroup<T> group = this.AllocateGroupCore<T>(totalLength, totalLengthInBytesLong, bufferAlignment, options);
159-
group.SetAllocationTracking(this, totalLengthInBytesLong);
160-
return group;
161-
}
162-
catch
163-
{
164-
this.ReleaseAccumulatedBytes(totalLengthInBytesLong);
165-
throw;
166-
}
167-
}
105+
// Cast to long is safe because we already checked that the total length is within the limit.
106+
return this.AllocateGroupCore<T>(totalLength, (long)totalLengthInBytes, bufferAlignment, options);
168107
}
169108

170109
internal virtual MemoryGroup<T> AllocateGroupCore<T>(long totalLengthInElements, long totalLengthInBytes, int bufferAlignment, AllocationOptions options)
171110
where T : struct
172111
=> MemoryGroup<T>.Allocate(this, totalLengthInElements, bufferAlignment, options);
173-
174-
/// <summary>
175-
/// Tracks the allocation of an <see cref="IMemoryOwner{T}" /> instance after reserving bytes.
176-
/// </summary>
177-
/// <typeparam name="T">Type of the data stored in the buffer.</typeparam>
178-
/// <param name="owner">The allocation to track.</param>
179-
/// <param name="lengthInBytes">The allocation size in bytes.</param>
180-
/// <returns>The tracked allocation.</returns>
181-
protected IMemoryOwner<T> TrackAllocation<T>(IMemoryOwner<T> owner, ulong lengthInBytes)
182-
where T : struct
183-
{
184-
if (this.IsTrackingSuppressed || lengthInBytes == 0)
185-
{
186-
return owner;
187-
}
188-
189-
return new TrackingMemoryOwner<T>(owner, this, (long)lengthInBytes);
190-
}
191-
192-
/// <summary>
193-
/// Reserves accumulative allocation bytes before creating the underlying buffer.
194-
/// </summary>
195-
/// <param name="lengthInBytes">The number of bytes to reserve.</param>
196-
protected void ReserveAllocation(long lengthInBytes)
197-
{
198-
if (this.IsTrackingSuppressed || lengthInBytes <= 0)
199-
{
200-
return;
201-
}
202-
203-
long total = Interlocked.Add(ref this.accumulativeAllocatedBytes, lengthInBytes);
204-
if (total > this.AccumulativeAllocationLimitBytes)
205-
{
206-
_ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes);
207-
InvalidMemoryOperationException.ThrowAllocationOverLimitException((ulong)lengthInBytes, this.AccumulativeAllocationLimitBytes);
208-
}
209-
}
210-
211-
/// <summary>
212-
/// Releases accumulative allocation bytes previously tracked by this allocator.
213-
/// </summary>
214-
/// <param name="lengthInBytes">The number of bytes to release.</param>
215-
internal void ReleaseAccumulatedBytes(long lengthInBytes)
216-
{
217-
if (lengthInBytes <= 0)
218-
{
219-
return;
220-
}
221-
222-
_ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes);
223-
}
224-
225-
/// <summary>
226-
/// Suppresses accumulative allocation tracking for the lifetime of the returned scope.
227-
/// </summary>
228-
/// <returns>An <see cref="IDisposable"/> that restores tracking when disposed.</returns>
229-
internal IDisposable SuppressTracking() => new TrackingSuppressionScope(this);
230-
231-
/// <summary>
232-
/// Temporarily suppresses accumulative allocation tracking within a scope.
233-
/// </summary>
234-
private sealed class TrackingSuppressionScope : IDisposable
235-
{
236-
private MemoryAllocator? allocator;
237-
238-
public TrackingSuppressionScope(MemoryAllocator allocator)
239-
{
240-
this.allocator = allocator;
241-
_ = Interlocked.Increment(ref allocator.trackingSuppressionCount);
242-
}
243-
244-
public void Dispose()
245-
{
246-
if (this.allocator != null)
247-
{
248-
_ = Interlocked.Decrement(ref this.allocator.trackingSuppressionCount);
249-
this.allocator = null;
250-
}
251-
}
252-
}
253-
254-
/// <summary>
255-
/// Wraps an <see cref="IMemoryOwner{T}"/> to release accumulative tracking on dispose.
256-
/// </summary>
257-
private sealed class TrackingMemoryOwner<T> : IMemoryOwner<T>
258-
where T : struct
259-
{
260-
private IMemoryOwner<T>? owner;
261-
private readonly MemoryAllocator allocator;
262-
private readonly long lengthInBytes;
263-
264-
public TrackingMemoryOwner(IMemoryOwner<T> owner, MemoryAllocator allocator, long lengthInBytes)
265-
{
266-
this.owner = owner;
267-
this.allocator = allocator;
268-
this.lengthInBytes = lengthInBytes;
269-
}
270-
271-
public Memory<T> Memory => this.owner?.Memory ?? Memory<T>.Empty;
272-
273-
public void Dispose()
274-
{
275-
// Ensure only one caller disposes the inner owner and releases the reservation.
276-
IMemoryOwner<T>? inner = Interlocked.Exchange(ref this.owner, null);
277-
if (inner != null)
278-
{
279-
inner.Dispose();
280-
this.allocator.ReleaseAccumulatedBytes(this.lengthInBytes);
281-
}
282-
}
283-
}
284112
}

src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@ public struct MemoryAllocatorOptions
1010
{
1111
private int? maximumPoolSizeMegabytes;
1212
private int? allocationLimitMegabytes;
13-
private int? accumulativeAllocationLimitMegabytes;
1413

1514
/// <summary>
1615
/// Gets or sets a value defining the maximum size of the <see cref="MemoryAllocator"/>'s internal memory pool
1716
/// in Megabytes. <see langword="null"/> means platform default.
1817
/// </summary>
1918
public int? MaximumPoolSizeMegabytes
2019
{
21-
readonly get => this.maximumPoolSizeMegabytes;
20+
get => this.maximumPoolSizeMegabytes;
2221
set
2322
{
2423
if (value.HasValue)
@@ -36,7 +35,7 @@ public int? MaximumPoolSizeMegabytes
3635
/// </summary>
3736
public int? AllocationLimitMegabytes
3837
{
39-
readonly get => this.allocationLimitMegabytes;
38+
get => this.allocationLimitMegabytes;
4039
set
4140
{
4241
if (value.HasValue)
@@ -47,29 +46,4 @@ public int? AllocationLimitMegabytes
4746
this.allocationLimitMegabytes = value;
4847
}
4948
}
50-
51-
/// <summary>
52-
/// Gets or sets a value defining the maximum total size that can be allocated by the allocator in Megabytes.
53-
/// <see langword="null"/> means platform default: 2GB on 32-bit processes, 8GB on 64-bit processes.
54-
/// </summary>
55-
public int? AccumulativeAllocationLimitMegabytes
56-
{
57-
readonly get => this.accumulativeAllocationLimitMegabytes;
58-
set
59-
{
60-
if (value.HasValue)
61-
{
62-
Guard.MustBeGreaterThan(value.Value, 0, nameof(this.AccumulativeAllocationLimitMegabytes));
63-
if (this.AllocationLimitMegabytes.HasValue)
64-
{
65-
Guard.MustBeGreaterThanOrEqualTo(
66-
value.Value,
67-
this.AllocationLimitMegabytes.Value,
68-
nameof(this.AccumulativeAllocationLimitMegabytes));
69-
}
70-
}
71-
72-
this.accumulativeAllocationLimitMegabytes = value;
73-
}
74-
}
7549
}

src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,6 @@ namespace SixLabors.ImageSharp.Memory;
1212
/// </summary>
1313
public sealed class SimpleGcMemoryAllocator : MemoryAllocator
1414
{
15-
/// <summary>
16-
/// Initializes a new instance of the <see cref="SimpleGcMemoryAllocator"/> class with default limits.
17-
/// </summary>
18-
public SimpleGcMemoryAllocator()
19-
: this(default)
20-
{
21-
}
22-
23-
/// <summary>
24-
/// Initializes a new instance of the <see cref="SimpleGcMemoryAllocator"/> class with custom limits.
25-
/// </summary>
26-
/// <param name="options">The <see cref="MemoryAllocatorOptions"/> to apply.</param>
27-
public SimpleGcMemoryAllocator(MemoryAllocatorOptions options)
28-
{
29-
if (options.AllocationLimitMegabytes.HasValue)
30-
{
31-
this.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L;
32-
this.SingleBufferAllocationLimitBytes = (int)Math.Min(this.SingleBufferAllocationLimitBytes, this.MemoryGroupAllocationLimitBytes);
33-
}
34-
35-
if (options.AccumulativeAllocationLimitMegabytes.HasValue)
36-
{
37-
this.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L;
38-
}
39-
}
40-
4115
/// <inheritdoc />
4216
protected internal override int GetBufferCapacityInBytes() => int.MaxValue;
4317

@@ -55,18 +29,6 @@ public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions option
5529
InvalidMemoryOperationException.ThrowAllocationOverLimitException(lengthInBytes, this.SingleBufferAllocationLimitBytes);
5630
}
5731

58-
long lengthInBytesLong = (long)lengthInBytes;
59-
this.ReserveAllocation(lengthInBytesLong);
60-
61-
try
62-
{
63-
IMemoryOwner<T> buffer = new BasicArrayBuffer<T>(new T[length]);
64-
return this.TrackAllocation(buffer, lengthInBytes);
65-
}
66-
catch
67-
{
68-
this.ReleaseAccumulatedBytes(lengthInBytesLong);
69-
throw;
70-
}
32+
return new BasicArrayBuffer<T>(new T[length]);
7133
}
7234
}

0 commit comments

Comments
 (0)