From c470a9a05a5f503b34b7daa61b18e317ade87075 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 Apr 2026 22:19:45 +1000 Subject: [PATCH] Revert "Merge pull request #3056 from SixLabors/js/accumulative-memory-limit" This reverts commit c5624b534e6e51b5bdeed1aae5b1d7c3a9c330ae, reversing changes made to b3f37932b6c79b8d7ad902f1eee09d240b7b3b4d. --- .../Memory/Allocators/MemoryAllocator.cs | 180 +----------------- .../Allocators/MemoryAllocatorOptions.cs | 30 +-- .../Allocators/SimpleGcMemoryAllocator.cs | 40 +--- ...iformUnmanagedMemoryPoolMemoryAllocator.cs | 58 +----- .../Allocators/UnmanagedMemoryAllocator.cs | 24 +-- .../DiscontiguousBuffers/IMemoryGroup{T}.cs | 8 +- .../MemoryGroup{T}.Consumed.cs | 16 +- .../MemoryGroup{T}.Owned.cs | 5 +- .../DiscontiguousBuffers/MemoryGroup{T}.cs | 38 +--- .../SimpleGcMemoryAllocatorTests.cs | 44 ----- ...niformUnmanagedPoolMemoryAllocatorTests.cs | 56 +----- .../MemoryGroupTests.Allocate.cs | 10 +- 12 files changed, 45 insertions(+), 464 deletions(-) diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs index d6badf9282..8eaf0b6d69 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs @@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.Memory; public abstract class MemoryAllocator { private const int OneGigabyte = 1 << 30; - private long accumulativeAllocatedBytes; - private int trackingSuppressionCount; /// /// Gets the default platform-specific global instance that @@ -25,44 +23,9 @@ public abstract class MemoryAllocator /// public static MemoryAllocator Default { get; } = Create(); - /// - /// Gets the maximum number of bytes that can be allocated by a memory group. - /// - /// - /// The allocation limit is determined by the process architecture: 4 GB for 64-bit processes and - /// 1 GB for 32-bit processes. - /// - internal long MemoryGroupAllocationLimitBytes { get; private protected set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte; - - /// - /// Gets the maximum allowed total allocation size, in bytes, for the current process. - /// - /// - /// Defaults to , effectively imposing no limit on total allocations. - /// This property can be set to enforce a cap on total memory usage across all allocations made through this allocator instance, providing - /// a safeguard against excessive memory consumption.
- /// When the cumulative size of active allocations exceeds this limit, an will be thrown to - /// prevent further allocations and signal that the limit has been breached. - ///
- internal long AccumulativeAllocationLimitBytes { get; private protected set; } = long.MaxValue; + internal long MemoryGroupAllocationLimitBytes { get; private set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte; - /// - /// Gets the maximum size, in bytes, that can be allocated for a single buffer. - /// - /// - /// The single buffer allocation limit is set to 1 GB by default. - /// - internal int SingleBufferAllocationLimitBytes { get; private protected set; } = OneGigabyte; - - /// - /// Gets a value indicating whether change tracking is currently suppressed for this instance. - /// - /// - /// When change tracking is suppressed, modifications to the object will not be recorded or - /// trigger change notifications. This property is used internally to temporarily disable tracking during - /// batch updates or initialization. - /// - private bool IsTrackingSuppressed => Volatile.Read(ref this.trackingSuppressionCount) > 0; + internal int SingleBufferAllocationLimitBytes { get; private set; } = OneGigabyte; /// /// 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) allocator.SingleBufferAllocationLimitBytes = (int)Math.Min(allocator.SingleBufferAllocationLimitBytes, allocator.MemoryGroupAllocationLimitBytes); } - if (options.AccumulativeAllocationLimitMegabytes.HasValue) - { - allocator.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L; - } - return allocator; } @@ -114,10 +72,6 @@ public abstract IMemoryOwner Allocate(int length, AllocationOptions option /// Releases all retained resources not being in use. /// Eg: by resetting array pools and letting GC to free the arrays. /// - /// - /// This does not dispose active allocations; callers are responsible for disposing all - /// instances to release memory. - /// public virtual void ReleaseRetainedResources() { } @@ -148,137 +102,11 @@ internal MemoryGroup AllocateGroup( InvalidMemoryOperationException.ThrowAllocationOverLimitException(totalLengthInBytes, this.MemoryGroupAllocationLimitBytes); } - long totalLengthInBytesLong = (long)totalLengthInBytes; - this.ReserveAllocation(totalLengthInBytesLong); - - using (this.SuppressTracking()) - { - try - { - MemoryGroup group = this.AllocateGroupCore(totalLength, totalLengthInBytesLong, bufferAlignment, options); - group.SetAllocationTracking(this, totalLengthInBytesLong); - return group; - } - catch - { - this.ReleaseAccumulatedBytes(totalLengthInBytesLong); - throw; - } - } + // Cast to long is safe because we already checked that the total length is within the limit. + return this.AllocateGroupCore(totalLength, (long)totalLengthInBytes, bufferAlignment, options); } internal virtual MemoryGroup AllocateGroupCore(long totalLengthInElements, long totalLengthInBytes, int bufferAlignment, AllocationOptions options) where T : struct => MemoryGroup.Allocate(this, totalLengthInElements, bufferAlignment, options); - - /// - /// Tracks the allocation of an instance after reserving bytes. - /// - /// Type of the data stored in the buffer. - /// The allocation to track. - /// The allocation size in bytes. - /// The tracked allocation. - protected IMemoryOwner TrackAllocation(IMemoryOwner owner, ulong lengthInBytes) - where T : struct - { - if (this.IsTrackingSuppressed || lengthInBytes == 0) - { - return owner; - } - - return new TrackingMemoryOwner(owner, this, (long)lengthInBytes); - } - - /// - /// Reserves accumulative allocation bytes before creating the underlying buffer. - /// - /// The number of bytes to reserve. - protected void ReserveAllocation(long lengthInBytes) - { - if (this.IsTrackingSuppressed || lengthInBytes <= 0) - { - return; - } - - long total = Interlocked.Add(ref this.accumulativeAllocatedBytes, lengthInBytes); - if (total > this.AccumulativeAllocationLimitBytes) - { - _ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes); - InvalidMemoryOperationException.ThrowAllocationOverLimitException((ulong)lengthInBytes, this.AccumulativeAllocationLimitBytes); - } - } - - /// - /// Releases accumulative allocation bytes previously tracked by this allocator. - /// - /// The number of bytes to release. - internal void ReleaseAccumulatedBytes(long lengthInBytes) - { - if (lengthInBytes <= 0) - { - return; - } - - _ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes); - } - - /// - /// Suppresses accumulative allocation tracking for the lifetime of the returned scope. - /// - /// An that restores tracking when disposed. - internal IDisposable SuppressTracking() => new TrackingSuppressionScope(this); - - /// - /// Temporarily suppresses accumulative allocation tracking within a scope. - /// - private sealed class TrackingSuppressionScope : IDisposable - { - private MemoryAllocator? allocator; - - public TrackingSuppressionScope(MemoryAllocator allocator) - { - this.allocator = allocator; - _ = Interlocked.Increment(ref allocator.trackingSuppressionCount); - } - - public void Dispose() - { - if (this.allocator != null) - { - _ = Interlocked.Decrement(ref this.allocator.trackingSuppressionCount); - this.allocator = null; - } - } - } - - /// - /// Wraps an to release accumulative tracking on dispose. - /// - private sealed class TrackingMemoryOwner : IMemoryOwner - where T : struct - { - private IMemoryOwner? owner; - private readonly MemoryAllocator allocator; - private readonly long lengthInBytes; - - public TrackingMemoryOwner(IMemoryOwner owner, MemoryAllocator allocator, long lengthInBytes) - { - this.owner = owner; - this.allocator = allocator; - this.lengthInBytes = lengthInBytes; - } - - public Memory Memory => this.owner?.Memory ?? Memory.Empty; - - public void Dispose() - { - // Ensure only one caller disposes the inner owner and releases the reservation. - IMemoryOwner? inner = Interlocked.Exchange(ref this.owner, null); - if (inner != null) - { - inner.Dispose(); - this.allocator.ReleaseAccumulatedBytes(this.lengthInBytes); - } - } - } } diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs index 3339203dac..d9ba62c1ef 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs @@ -10,7 +10,6 @@ public struct MemoryAllocatorOptions { private int? maximumPoolSizeMegabytes; private int? allocationLimitMegabytes; - private int? accumulativeAllocationLimitMegabytes; /// /// Gets or sets a value defining the maximum size of the 's internal memory pool @@ -18,7 +17,7 @@ public struct MemoryAllocatorOptions /// public int? MaximumPoolSizeMegabytes { - readonly get => this.maximumPoolSizeMegabytes; + get => this.maximumPoolSizeMegabytes; set { if (value.HasValue) @@ -36,7 +35,7 @@ public int? MaximumPoolSizeMegabytes /// public int? AllocationLimitMegabytes { - readonly get => this.allocationLimitMegabytes; + get => this.allocationLimitMegabytes; set { if (value.HasValue) @@ -47,29 +46,4 @@ public int? AllocationLimitMegabytes this.allocationLimitMegabytes = value; } } - - /// - /// Gets or sets a value defining the maximum total size that can be allocated by the allocator in Megabytes. - /// means platform default: 2GB on 32-bit processes, 8GB on 64-bit processes. - /// - public int? AccumulativeAllocationLimitMegabytes - { - readonly get => this.accumulativeAllocationLimitMegabytes; - set - { - if (value.HasValue) - { - Guard.MustBeGreaterThan(value.Value, 0, nameof(this.AccumulativeAllocationLimitMegabytes)); - if (this.AllocationLimitMegabytes.HasValue) - { - Guard.MustBeGreaterThanOrEqualTo( - value.Value, - this.AllocationLimitMegabytes.Value, - nameof(this.AccumulativeAllocationLimitMegabytes)); - } - } - - this.accumulativeAllocationLimitMegabytes = value; - } - } } diff --git a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs index 9cbb15713a..675afe8b9f 100644 --- a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs @@ -12,32 +12,6 @@ namespace SixLabors.ImageSharp.Memory; /// public sealed class SimpleGcMemoryAllocator : MemoryAllocator { - /// - /// Initializes a new instance of the class with default limits. - /// - public SimpleGcMemoryAllocator() - : this(default) - { - } - - /// - /// Initializes a new instance of the class with custom limits. - /// - /// The to apply. - public SimpleGcMemoryAllocator(MemoryAllocatorOptions options) - { - if (options.AllocationLimitMegabytes.HasValue) - { - this.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L; - this.SingleBufferAllocationLimitBytes = (int)Math.Min(this.SingleBufferAllocationLimitBytes, this.MemoryGroupAllocationLimitBytes); - } - - if (options.AccumulativeAllocationLimitMegabytes.HasValue) - { - this.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L; - } - } - /// protected internal override int GetBufferCapacityInBytes() => int.MaxValue; @@ -55,18 +29,6 @@ public override IMemoryOwner Allocate(int length, AllocationOptions option InvalidMemoryOperationException.ThrowAllocationOverLimitException(lengthInBytes, this.SingleBufferAllocationLimitBytes); } - long lengthInBytesLong = (long)lengthInBytes; - this.ReserveAllocation(lengthInBytesLong); - - try - { - IMemoryOwner buffer = new BasicArrayBuffer(new T[length]); - return this.TrackAllocation(buffer, lengthInBytes); - } - catch - { - this.ReleaseAccumulatedBytes(lengthInBytesLong); - throw; - } + return new BasicArrayBuffer(new T[length]); } } diff --git a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs index e34860b37b..d5cd329f1b 100644 --- a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs @@ -92,24 +92,13 @@ public override IMemoryOwner Allocate( if (lengthInBytes <= (ulong)this.sharedArrayPoolThresholdInBytes) { - long lengthInBytesLong = (long)lengthInBytes; - this.ReserveAllocation(lengthInBytesLong); - - try - { - SharedArrayPoolBuffer buffer = new(length); - if (options.Has(AllocationOptions.Clean)) - { - buffer.GetSpan().Clear(); - } - - return this.TrackAllocation(buffer, lengthInBytes); - } - catch + SharedArrayPoolBuffer buffer = new(length); + if (options.Has(AllocationOptions.Clean)) { - this.ReleaseAccumulatedBytes(lengthInBytesLong); - throw; + buffer.GetSpan().Clear(); } + + return buffer; } if (lengthInBytes <= (ulong)this.poolBufferSizeInBytes) @@ -117,38 +106,12 @@ public override IMemoryOwner Allocate( UnmanagedMemoryHandle mem = this.pool.Rent(); if (mem.IsValid) { - long lengthInBytesLong = (long)lengthInBytes; - this.ReserveAllocation(lengthInBytesLong); - - try - { - UnmanagedBuffer buffer = this.pool.CreateGuardedBuffer(mem, length, options.Has(AllocationOptions.Clean)); - return this.TrackAllocation(buffer, lengthInBytes); - } - catch - { - this.ReleaseAccumulatedBytes(lengthInBytesLong); - throw; - } + UnmanagedBuffer buffer = this.pool.CreateGuardedBuffer(mem, length, options.Has(AllocationOptions.Clean)); + return buffer; } } - long nonPooledLengthInBytesLong = (long)lengthInBytes; - this.ReserveAllocation(nonPooledLengthInBytesLong); - - try - { - using (this.nonPoolAllocator.SuppressTracking()) - { - IMemoryOwner nonPooled = this.nonPoolAllocator.Allocate(length, options); - return this.TrackAllocation(nonPooled, lengthInBytes); - } - } - catch - { - this.ReleaseAccumulatedBytes(nonPooledLengthInBytesLong); - throw; - } + return this.nonPoolAllocator.Allocate(length, options); } /// @@ -181,10 +144,7 @@ internal override MemoryGroup AllocateGroupCore( return poolGroup; } - using (this.nonPoolAllocator.SuppressTracking()) - { - return MemoryGroup.Allocate(this.nonPoolAllocator, totalLengthInElements, bufferAlignment, options); - } + return MemoryGroup.Allocate(this.nonPoolAllocator, totalLengthInElements, bufferAlignment, options); } public override void ReleaseRetainedResources() => this.pool.Release(); diff --git a/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs index 73cef8b5ee..daf1a79925 100644 --- a/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory.Internals; namespace SixLabors.ImageSharp.Memory; @@ -20,26 +19,13 @@ internal class UnmanagedMemoryAllocator : MemoryAllocator protected internal override int GetBufferCapacityInBytes() => this.bufferCapacityInBytes; public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) - where T : struct { - ulong lengthInBytes = (ulong)length * (ulong)Unsafe.SizeOf(); - long lengthInBytesLong = (long)lengthInBytes; - this.ReserveAllocation(lengthInBytesLong); - - try + UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(length); + if (options.Has(AllocationOptions.Clean)) { - UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(length); - if (options.Has(AllocationOptions.Clean)) - { - buffer.GetSpan().Clear(); - } - - return this.TrackAllocation(buffer, lengthInBytes); - } - catch - { - this.ReleaseAccumulatedBytes(lengthInBytesLong); - throw; + buffer.GetSpan().Clear(); } + + return buffer; } } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs index 03f26aab04..7e9719ea75 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs @@ -15,12 +15,12 @@ public interface IMemoryGroup : IReadOnlyList> /// Gets the number of elements per contiguous sub-buffer preceding the last buffer. /// The last buffer is allowed to be smaller. /// - public int BufferLength { get; } + int BufferLength { get; } /// /// Gets the aggregate number of elements in the group. /// - public long TotalLength { get; } + long TotalLength { get; } /// /// Gets a value indicating whether the group has been invalidated. @@ -29,7 +29,7 @@ public interface IMemoryGroup : IReadOnlyList> /// Invalidation usually occurs when an image processor capable to alter the image dimensions replaces /// the image buffers internally. /// - public bool IsValid { get; } + bool IsValid { get; } /// /// Returns a value-type implementing an allocation-free enumerator of the memory groups in the current @@ -39,5 +39,5 @@ public interface IMemoryGroup : IReadOnlyList> /// implementation, which is still available when casting to one of the underlying interfaces. /// /// A new instance mapping the current values in use. - public new MemoryGroupEnumerator GetEnumerator(); + new MemoryGroupEnumerator GetEnumerator(); } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs index 75e93ce7f8..950e2a019e 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs @@ -31,23 +31,23 @@ public override int Count /// [MethodImpl(InliningOptions.ShortMethod)] - public override MemoryGroupEnumerator GetEnumerator() => new(this); + public override MemoryGroupEnumerator GetEnumerator() + { + return new MemoryGroupEnumerator(this); + } /// IEnumerator> IEnumerable>.GetEnumerator() - + { /* The runtime sees the Array class as if it implemented the * type-generic collection interfaces explicitly, so here we * can just cast the source array to IList> (or to * an equivalent type), and invoke the generic GetEnumerator * method directly from that interface reference. This saves * having to create our own iterator block here. */ - => ((IList>)this.source).GetEnumerator(); - - public override void Dispose() - { - this.View.Invalidate(); - this.ReleaseAllocationTracking(); + return ((IList>)this.source).GetEnumerator(); } + + public override void Dispose() => this.View.Invalidate(); } } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs index be92272bbe..af896ee0e1 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs @@ -73,8 +73,8 @@ private static IMemoryOwner[] CreateBuffers( result[i] = currentBuffer; } - ObservedBuffer lastBuffer = ObservedBuffer.Create(pooledBuffers[^1], sizeOfLastBuffer, options); - result[^1] = lastBuffer; + ObservedBuffer lastBuffer = ObservedBuffer.Create(pooledBuffers[pooledBuffers.Length - 1], sizeOfLastBuffer, options); + result[result.Length - 1] = lastBuffer; return result; } @@ -155,7 +155,6 @@ public override void Dispose() } } - this.ReleaseAllocationTracking(); this.memoryOwners = null; this.IsValid = false; this.groupLifetimeGuard = null; diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs index aa0e188959..6dd99fcb02 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs @@ -22,9 +22,6 @@ internal abstract partial class MemoryGroup : IMemoryGroup, IDisposable private static readonly int ElementSize = Unsafe.SizeOf(); private MemoryGroupSpanCache memoryGroupSpanCache; - private MemoryAllocator? trackingAllocator; - private long trackingLengthInBytes; - private int trackingReleased; private MemoryGroup(int bufferLength, long totalLength) { @@ -55,45 +52,16 @@ private MemoryGroup(int bufferLength, long totalLength) /// public abstract MemoryGroupEnumerator GetEnumerator(); - /// - /// Configures allocation tracking by specifying the allocator and the length, in bytes, to be tracked. - /// - /// The memory allocator to use for tracking allocations. - /// The length, in bytes, of the memory region to track. Must be greater than or equal to zero. - /// - /// Intended for initialization; callers should avoid changing tracking state concurrently with disposal. - /// - internal void SetAllocationTracking(MemoryAllocator allocator, long lengthInBytes) - { - this.trackingAllocator = allocator; - this.trackingLengthInBytes = lengthInBytes; - } - - /// - /// Releases any resources or tracking information associated with allocation tracking for this instance. - /// - /// - /// 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. - /// - internal void ReleaseAllocationTracking() - { - if (Interlocked.Exchange(ref this.trackingReleased, 1) == 0 && this.trackingAllocator != null) - { - this.trackingAllocator.ReleaseAccumulatedBytes(this.trackingLengthInBytes); - this.trackingAllocator = null; - } - } - /// IEnumerator> IEnumerable>.GetEnumerator() - + { /* This method is implemented in each derived class. * Implementing the method here as non-abstract and throwing, * then reimplementing it explicitly in each derived class, is * a workaround for the lack of support for abstract explicit * interface method implementations in C#. */ - => throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()"); + throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()"); + } /// IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs index c33e6d1070..0e791c5d97 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs @@ -48,50 +48,6 @@ public unsafe void Allocate_MemoryIsPinnableMultipleTimes() } } - [Fact] - public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose() - { - SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions - { - AccumulativeAllocationLimitMegabytes = 1 - }); - const int oneMb = 1 << 20; - - // Reserve the full limit with a single owner. - IMemoryOwner b0 = allocator.Allocate(oneMb); - - // Additional allocation should exceed the limit while the owner is live. - Assert.Throws(() => allocator.Allocate(1)); - - // Disposing the owner releases the reservation. - b0.Dispose(); - - // Allocation should succeed after the reservation is released. - allocator.Allocate(oneMb).Dispose(); - } - - [Fact] - public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose() - { - SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions - { - AccumulativeAllocationLimitMegabytes = 1 - }); - const int oneMb = 1 << 20; - - // Reserve the full limit with a single group. - MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024); - - // Additional allocation should exceed the limit while the group is live. - Assert.Throws(() => allocator.AllocateGroup(1, 1024)); - - // Disposing the group releases the reservation. - g0.Dispose(); - - // Allocation should succeed after the reservation is released. - allocator.AllocateGroup(oneMb, 1024).Dispose(); - } - [StructLayout(LayoutKind.Explicit, Size = 512)] private struct BigStruct { diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index 06da3505e1..1d185a0de9 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -16,8 +16,8 @@ public class UniformUnmanagedPoolMemoryAllocatorTests { public class BufferTests1 : BufferTestSuite { - private static UniformUnmanagedMemoryPoolMemoryAllocator CreateMemoryAllocator() => - new( + private static MemoryAllocator CreateMemoryAllocator() => + new UniformUnmanagedMemoryPoolMemoryAllocator( sharedArrayPoolThresholdInBytes: 1024, poolBufferSizeInBytes: 2048, maxPoolSizeInBytes: 2048 * 4, @@ -31,8 +31,8 @@ public BufferTests1() public class BufferTests2 : BufferTestSuite { - private static UniformUnmanagedMemoryPoolMemoryAllocator CreateMemoryAllocator() => - new( + private static MemoryAllocator CreateMemoryAllocator() => + new UniformUnmanagedMemoryPoolMemoryAllocator( sharedArrayPoolThresholdInBytes: 512, poolBufferSizeInBytes: 1024, maxPoolSizeInBytes: 1024 * 4, @@ -179,8 +179,8 @@ static void RunTest() g1.Dispose(); // Do some unmanaged allocations to make sure new non-pooled unmanaged allocations will grab different memory: - IntPtr dummy1 = Marshal.AllocHGlobal(checked((IntPtr)B(8))); - IntPtr dummy2 = Marshal.AllocHGlobal(checked((IntPtr)B(8))); + IntPtr dummy1 = Marshal.AllocHGlobal((IntPtr)B(8)); + IntPtr dummy2 = Marshal.AllocHGlobal((IntPtr)B(8)); using MemoryGroup g2 = allocator.AllocateGroup(B(8), 1024); using MemoryGroup g3 = allocator.AllocateGroup(B(8), 1024); @@ -433,50 +433,6 @@ public void AllocateGroup_OverLimit_ThrowsInvalidMemoryOperationException() Assert.Throws(() => allocator.AllocateGroup(5 * oneMb, 1024)); } - [Fact] - public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose() - { - MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions - { - AccumulativeAllocationLimitMegabytes = 1 - }); - const int oneMb = 1 << 20; - - // Reserve the full limit with a single owner. - IMemoryOwner b0 = allocator.Allocate(oneMb); - - // Additional allocation should exceed the limit while the owner is live. - Assert.Throws(() => allocator.Allocate(1)); - - // Disposing the owner releases the reservation. - b0.Dispose(); - - // Allocation should succeed after the reservation is released. - allocator.Allocate(oneMb).Dispose(); - } - - [Fact] - public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose() - { - MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions - { - AccumulativeAllocationLimitMegabytes = 1 - }); - const int oneMb = 1 << 20; - - // Reserve the full limit with a single group. - MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024); - - // Additional allocation should exceed the limit while the group is live. - Assert.Throws(() => allocator.AllocateGroup(1, 1024)); - - // Disposing the group releases the reservation. - g0.Dispose(); - - // Allocation should succeed after the reservation is released. - allocator.AllocateGroup(oneMb, 1024).Dispose(); - } - [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] public void MemoryAllocator_Create_SetHighLimit() { diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs index cca2230e6f..678a089a85 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs @@ -98,15 +98,7 @@ public void Allocate_FromPool_BufferSizesAreCorrect( [InlineData(AllocationOptions.Clean)] public unsafe void Allocate_FromPool_AllocationOptionsAreApplied(AllocationOptions options) { - // Disable trimming to avoid buffers being freed between Return and TryAllocate by the - // trim timer or the Gen2 GC callback. - UniformUnmanagedMemoryPool pool = new( - 10, - 5, - new UniformUnmanagedMemoryPool.TrimSettings - { - Rate = 0 - }); + UniformUnmanagedMemoryPool pool = new(10, 5); UnmanagedMemoryHandle[] buffers = pool.Rent(5); foreach (UnmanagedMemoryHandle b in buffers) {