mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
9.4 KiB
292 lines
9.4 KiB
// Copyright (c) Six Labors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.DotNet.RemoteExecutor;
|
|
using SixLabors.ImageSharp.Memory.Internals;
|
|
using Xunit;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace SixLabors.ImageSharp.Tests.Memory.Allocators
|
|
{
|
|
public partial class UniformUnmanagedMemoryPoolTests
|
|
{
|
|
private readonly ITestOutputHelper output;
|
|
|
|
public UniformUnmanagedMemoryPoolTests(ITestOutputHelper output)
|
|
{
|
|
this.output = output;
|
|
}
|
|
|
|
private static unsafe Span<byte> GetSpan(UniformUnmanagedMemoryPool pool, UnmanagedMemoryHandle h) =>
|
|
new Span<byte>((void*)h.DangerousGetHandle(), pool.BufferLength);
|
|
|
|
[Theory]
|
|
[InlineData(3, 11)]
|
|
[InlineData(7, 4)]
|
|
public void Constructor_InitializesProperties(int arrayLength, int capacity)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(arrayLength, capacity);
|
|
Assert.Equal(arrayLength, pool.BufferLength);
|
|
Assert.Equal(capacity, pool.Capacity);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(1, 3)]
|
|
[InlineData(8, 10)]
|
|
public void Rent_SingleBuffer_ReturnsCorrectBuffer(int length, int capacity)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(length, capacity);
|
|
for (int i = 0; i < capacity; i++)
|
|
{
|
|
UnmanagedMemoryHandle h = pool.Rent();
|
|
CheckBuffer(length, pool, h);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Return_DoesNotDeallocateMemory()
|
|
{
|
|
RemoteExecutor.Invoke(RunTest).Dispose();
|
|
|
|
static void RunTest()
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(16, 16);
|
|
UnmanagedMemoryHandle a = pool.Rent();
|
|
UnmanagedMemoryHandle[] b = pool.Rent(2);
|
|
|
|
Assert.Equal(3, UnmanagedMemoryHandle.TotalOutstandingHandles);
|
|
pool.Return(a);
|
|
pool.Return(b);
|
|
Assert.Equal(3, UnmanagedMemoryHandle.TotalOutstandingHandles);
|
|
}
|
|
}
|
|
|
|
private static void CheckBuffer(int length, UniformUnmanagedMemoryPool pool, UnmanagedMemoryHandle h)
|
|
{
|
|
Assert.NotNull(h);
|
|
Assert.False(h.IsClosed);
|
|
Span<byte> span = GetSpan(pool, h);
|
|
span.Fill(123);
|
|
|
|
byte[] expected = new byte[length];
|
|
expected.AsSpan().Fill(123);
|
|
Assert.True(span.SequenceEqual(expected));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(1, 1)]
|
|
[InlineData(1, 5)]
|
|
[InlineData(42, 7)]
|
|
[InlineData(5, 10)]
|
|
public void Rent_MultiBuffer_ReturnsCorrectBuffers(int length, int bufferCount)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(length, 10);
|
|
UnmanagedMemoryHandle[] handles = pool.Rent(bufferCount);
|
|
Assert.NotNull(handles);
|
|
Assert.Equal(bufferCount, handles.Length);
|
|
|
|
foreach (UnmanagedMemoryHandle h in handles)
|
|
{
|
|
CheckBuffer(length, pool, h);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Rent_MultipleTimesWithoutReturn_ReturnsDifferentHandles()
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(128, 10);
|
|
UnmanagedMemoryHandle[] a = pool.Rent(2);
|
|
UnmanagedMemoryHandle b = pool.Rent();
|
|
|
|
Assert.NotEqual(a[0].DangerousGetHandle(), a[1].DangerousGetHandle());
|
|
Assert.NotEqual(a[0].DangerousGetHandle(), b.DangerousGetHandle());
|
|
Assert.NotEqual(a[1].DangerousGetHandle(), b.DangerousGetHandle());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(4, 2, 10)]
|
|
[InlineData(5, 1, 6)]
|
|
[InlineData(12, 4, 12)]
|
|
public void RentReturnRent_SameBuffers(int totalCount, int rentUnit, int capacity)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(128, capacity);
|
|
var allHandles = new HashSet<UnmanagedMemoryHandle>();
|
|
var handleUnits = new List<UnmanagedMemoryHandle[]>();
|
|
|
|
UnmanagedMemoryHandle[] handles;
|
|
for (int i = 0; i < totalCount; i += rentUnit)
|
|
{
|
|
handles = pool.Rent(rentUnit);
|
|
Assert.NotNull(handles);
|
|
handleUnits.Add(handles);
|
|
foreach (UnmanagedMemoryHandle array in handles)
|
|
{
|
|
allHandles.Add(array);
|
|
}
|
|
}
|
|
|
|
foreach (UnmanagedMemoryHandle[] arrayUnit in handleUnits)
|
|
{
|
|
if (arrayUnit.Length == 1)
|
|
{
|
|
// Test single-array return:
|
|
pool.Return(arrayUnit.Single());
|
|
}
|
|
else
|
|
{
|
|
pool.Return(arrayUnit);
|
|
}
|
|
}
|
|
|
|
handles = pool.Rent(totalCount);
|
|
|
|
Assert.NotNull(handles);
|
|
|
|
foreach (UnmanagedMemoryHandle array in handles)
|
|
{
|
|
Assert.Contains(array, allHandles);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Rent_SingleBuffer_OverCapacity_ReturnsNull()
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(7, 1000);
|
|
Assert.NotNull(pool.Rent(1000));
|
|
Assert.Null(pool.Rent());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0, 6, 5)]
|
|
[InlineData(5, 1, 5)]
|
|
[InlineData(4, 7, 10)]
|
|
public void Rent_MultiBuffer_OverCapacity_ReturnsNull(int initialRent, int attempt, int capacity)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(128, capacity);
|
|
Assert.NotNull(pool.Rent(initialRent));
|
|
Assert.Null(pool.Rent(attempt));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0, 5, 5)]
|
|
[InlineData(5, 1, 6)]
|
|
[InlineData(4, 7, 11)]
|
|
[InlineData(3, 3, 7)]
|
|
public void Rent_MultiBuff_BelowCapacity_Succeeds(int initialRent, int attempt, int capacity)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(128, capacity);
|
|
Assert.NotNull(pool.Rent(initialRent));
|
|
Assert.NotNull(pool.Rent(attempt));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void Release_SubsequentRentReturnsNull(bool multiple)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(16, 16);
|
|
pool.Rent(); // Dummy rent
|
|
pool.Release();
|
|
if (multiple)
|
|
{
|
|
UnmanagedMemoryHandle b = pool.Rent();
|
|
Assert.Null(b);
|
|
}
|
|
else
|
|
{
|
|
UnmanagedMemoryHandle[] b = pool.Rent(2);
|
|
Assert.Null(b);
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void Release_SubsequentReturnClosesHandle(bool multiple)
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(16, 16);
|
|
if (multiple)
|
|
{
|
|
UnmanagedMemoryHandle[] b = pool.Rent(2);
|
|
pool.Release();
|
|
|
|
Assert.False(b[0].IsClosed);
|
|
Assert.False(b[1].IsClosed);
|
|
|
|
pool.Return(b);
|
|
|
|
Assert.True(b[0].IsClosed);
|
|
Assert.True(b[1].IsClosed);
|
|
}
|
|
else
|
|
{
|
|
UnmanagedMemoryHandle b = pool.Rent();
|
|
pool.Release();
|
|
Assert.False(b.IsClosed);
|
|
pool.Return(b);
|
|
Assert.True(b.IsClosed);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Release_ShouldFreeRetainedMemory()
|
|
{
|
|
RemoteExecutor.Invoke(RunTest).Dispose();
|
|
|
|
static void RunTest()
|
|
{
|
|
var pool = new UniformUnmanagedMemoryPool(16, 16);
|
|
UnmanagedMemoryHandle a = pool.Rent();
|
|
UnmanagedMemoryHandle[] b = pool.Rent(2);
|
|
pool.Return(a);
|
|
pool.Return(b);
|
|
|
|
Assert.Equal(3, UnmanagedMemoryHandle.TotalOutstandingHandles);
|
|
pool.Release();
|
|
Assert.Equal(0, UnmanagedMemoryHandle.TotalOutstandingHandles);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void RentReturn_IsThreadSafe()
|
|
{
|
|
int count = Environment.ProcessorCount * 200;
|
|
var pool = new UniformUnmanagedMemoryPool(8, count);
|
|
var rnd = new Random(0);
|
|
|
|
Parallel.For(0, Environment.ProcessorCount, (int i) =>
|
|
{
|
|
var allArrays = new List<UnmanagedMemoryHandle>();
|
|
int pauseAt = rnd.Next(100);
|
|
for (int j = 0; j < 100; j++)
|
|
{
|
|
UnmanagedMemoryHandle[] data = pool.Rent(2);
|
|
|
|
GetSpan(pool, data[0]).Fill((byte)i);
|
|
GetSpan(pool, data[1]).Fill((byte)i);
|
|
allArrays.Add(data[0]);
|
|
allArrays.Add(data[1]);
|
|
|
|
if (j == pauseAt)
|
|
{
|
|
Thread.Sleep(15);
|
|
}
|
|
}
|
|
|
|
Span<byte> expected = new byte[8];
|
|
expected.Fill((byte)i);
|
|
|
|
foreach (UnmanagedMemoryHandle array in allArrays)
|
|
{
|
|
Assert.True(expected.SequenceEqual(GetSpan(pool, array)));
|
|
pool.Return(new[] { array });
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|