Skip to content

Commit

Permalink
Breaking change IMemoryChunkAllocator.AllocateChunk to TryAllocateChunk
Browse files Browse the repository at this point in the history
  • Loading branch information
xoofx committed Jul 5, 2024
1 parent 1cb40c6 commit 331649f
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 17 deletions.
6 changes: 3 additions & 3 deletions doc/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ public unsafe class BasicChunkAllocator : IMemoryChunkAllocator
private readonly Dictionary<int, MemoryChunk> _chunks = new Dictionary<int, MemoryChunk>();
private const int ChunkSize = 65536;

public MemoryChunk AllocateChunk(MemorySize minSize)
public bool TryAllocateChunk(MemorySize minSize, out MemoryChunk chunk)
{
var blockSize = (uint)Math.Max(ChunkSize, (int)minSize.Value);
var address = NativeMemory.AlignedAlloc(blockSize, 64);
var chunk = new MemoryChunk((ulong)_chunks.Count, (ulong)address, blockSize);
chunk = new MemoryChunk((ulong)_chunks.Count, (ulong)address, blockSize);
_chunks.Add(_chunks.Count, chunk);
return chunk;
return true;
}

public void FreeChunk(in MemoryChunk chunk)
Expand Down
6 changes: 3 additions & 3 deletions src/XenoAtom.Allocators.Bench/BenchAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ private unsafe class BasicChunkAllocator : IMemoryChunkAllocator
private readonly Dictionary<int, MemoryChunk> _chunks = new Dictionary<int, MemoryChunk>();
private const int ChunkSize = 65536;

public MemoryChunk AllocateChunk(MemorySize minSize)
public bool TryAllocateChunk(MemorySize minSize, out MemoryChunk chunk)
{
var blockSize = (uint)Math.Max(ChunkSize, (int)minSize.Value);
var address = NativeMemory.AlignedAlloc(blockSize, 64);
var chunk = new MemoryChunk((ulong)_chunks.Count, (ulong)address, blockSize);
chunk = new MemoryChunk((ulong)_chunks.Count, (ulong)address, blockSize);
_chunks.Add(_chunks.Count, chunk);
return chunk;
return true;
}

public void FreeChunk(in MemoryChunk chunk)
Expand Down
5 changes: 3 additions & 2 deletions src/XenoAtom.Allocators.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ public void Reset()

public List<ChunkAllocationRequest> RequestedChunkAllocations { get; }

public MemoryChunk AllocateChunk(MemorySize size)
public bool TryAllocateChunk(MemorySize size, out MemoryChunk chunk)
{
while (_nextSize < size)
{
Expand All @@ -550,7 +550,8 @@ public MemoryChunk AllocateChunk(MemorySize size)
_baseAddress += _nextSize;
RequestedChunkAllocations.Add(new((ulong)chunkIndex, baseAddress, size, _nextSize));

return new((ulong)chunkIndex, baseAddress, _nextSize);
chunk = new((ulong)chunkIndex, baseAddress, _nextSize);
return true;
}

public void FreeChunk(in MemoryChunk chunk)
Expand Down
5 changes: 3 additions & 2 deletions src/XenoAtom.Allocators/IMemoryChunkAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ public interface IMemoryChunkAllocator
/// Allocates a new chunk of memory.
/// </summary>
/// <param name="minSize">The minimum size. Usually the size returned by an allocator can be much bigger.</param>
/// <returns>The allocated memory chunk.</returns>
MemoryChunk AllocateChunk(MemorySize minSize);
/// <param name="chunk">The allocated memory chunk.</param>
/// <returns><c>true</c> if the allocation was successful; otherwise <c>false</c>.</returns>
bool TryAllocateChunk(MemorySize minSize, out MemoryChunk chunk);

/// <summary>
/// Frees a chunk of memory.
Expand Down
38 changes: 31 additions & 7 deletions src/XenoAtom.Allocators/TlsfAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,30 @@ public TlsfAllocator(IMemoryChunkAllocator context, in TlsfAllocatorConfig confi
/// </summary>
/// <param name="size">The requested size</param>
/// <returns>An allocation with the requested size rounded up to the alignment of this allocator.</returns>
/// <exception cref="OutOfMemoryException">If the allocation failed.</exception>
public TlsfAllocation Allocate(uint size)
=> !TryAllocate(new(size), out var allocation) ? throw new OutOfMemoryException($"Failed to allocate a block of size {size}") : allocation;

/// <summary>
/// Tries to allocate a block of memory of the specified size.
/// </summary>
/// <param name="size">The requested size</param>
/// <param name="allocation">An allocation with the requested size rounded up to the alignment of this allocator if the result of this function is <c>true</c>.</param>
/// <returns><c>true</c> if the allocation was successful; otherwise <c>false</c>.</returns>
public bool TryAllocate(MemorySize size, out TlsfAllocation allocation)
{
// We align the size to the alignment (so free blocks are always aligned)
size = AlignHelper.AlignUp(size, _alignment);

var firstLevelIndex = Mapping(size, out int secondLevelIndex);

ref var freeBlock = ref FindSuitableBlock(size, ref firstLevelIndex, ref secondLevelIndex, out var freeBlockIndex);
// Tries to find a block. The allocation could fail if we don't have a block available and the chunk allocation failed.
ref var freeBlock = ref TryFindSuitableBlock(size, ref firstLevelIndex, ref secondLevelIndex, out var freeBlockIndex);
if (Unsafe.IsNullRef(ref freeBlock))
{
allocation = default;
return false;
}

ref var chunk = ref _chunks.UnsafeGetRefAt((int)freeBlock.ChunkIndex);
chunk.TotalAllocated += size;
Expand Down Expand Up @@ -154,7 +170,7 @@ public TlsfAllocation Allocate(uint size)
InsertBlockIntoFreeList(ref freeBlock, freeBlockIndex, newFirstLevelIndex, newSecondLevelIndex);
}

return new TlsfAllocation(new((uint)usedBlockIndex), (ulong)chunk.Info.BaseAddress + offsetIntoChunk, size);
allocation = new TlsfAllocation(new((uint)usedBlockIndex), (ulong)chunk.Info.BaseAddress + offsetIntoChunk, size);
}
else
{
Expand All @@ -167,9 +183,11 @@ public TlsfAllocation Allocate(uint size)
chunk.UsedBlockCount++;
chunk.FreeBlockCount--;
Debug.Assert(chunk.FreeBlockCount >= 0);
return new TlsfAllocation(new((uint)freeBlockIndex), (ulong)chunk.Info.BaseAddress + offsetIntoChunk, size);

allocation = new TlsfAllocation(new((uint)freeBlockIndex), (ulong)chunk.Info.BaseAddress + offsetIntoChunk, size);
}

return true;
}

/// <summary>
Expand Down Expand Up @@ -535,14 +553,20 @@ private void InsertBlockIntoFreeList(ref Block block, int blockIndex, int firstL
firstFreeIndex = blockIndex;
}

private ref Block FindSuitableBlock(uint size, ref int firstLevelIndex, ref int secondLevelIndex, out int blockIndex)
private ref Block TryFindSuitableBlock(uint size, ref int firstLevelIndex, ref int secondLevelIndex, out int blockIndex)
{
findFirstLevel:
firstLevelIndex = _bins.GetFirstLevelIndexAvailableAt(firstLevelIndex);

// If we don't have a block in higher level directory, we need to allocate a new chunk
if (firstLevelIndex < 0)
{
if (!_context.TryAllocateChunk(new(size), out var localChunk))
{
blockIndex = -1;
return ref Unsafe.NullRef<Block>();
}

int chunkIndex = _chunks.Count;
ref var chunkEntry = ref _chunks.UnsafeGetOrCreate(chunkIndex);
chunkEntry = default;
Expand All @@ -551,8 +575,8 @@ private void InsertBlockIntoFreeList(ref Block block, int blockIndex, int firstL
blockIndex = GetNextAvailableBlockIndex();
chunkEntry.FreeBlockCount++;
chunkEntry.FirstBlockInPhysicalOrder = blockIndex;

chunk = _context.AllocateChunk(new(size)); // We know that size is at minimum _alignment size
chunk = localChunk;

Debug.Assert(BitOperations.IsPow2(chunk.Size));
ref var block = ref GetOrCreateBlockAt(blockIndex);
block.ChunkIndex = (uint)chunkIndex;
Expand Down

0 comments on commit 331649f

Please sign in to comment.