public Allocate ( ulong id, long size, long &outputStart ) : bool | ||
id | ulong | Unique id of the memory to allocate. |
size | long | Size of the memory to allocate. |
outputStart | long | Starting index of the allocated memory, if successful. |
return | bool |
public bool Allocate(ulong id, long size, out long outputStart)
{
Debug.Assert(!allocations.ContainsKey(id), "Id must not already be present.");
if (allocations.Count == 0)
{
//If it's the first allocation, then the next and previous pointers should circle around.
if (size <= memoryPoolSize)
{
outputStart = 0;
allocations.Add(id, new Allocation { Start = 0, End = size, Next = id, Previous = id });
searchStartIndex = 0;
return true;
}
outputStart = 0;
return false;
}
Debug.Assert(searchStartIndex >= 0 && searchStartIndex < allocations.Count, "Search start index must be within the allocation set!");
int allocationIndex = searchStartIndex;
var initialId = allocations.Keys[allocationIndex];
while (true)
{
var allocation = allocations.Values[allocationIndex];
int nextAllocationIndex = allocations.IndexOf(allocation.Next);
var nextAllocation = allocations.Values[nextAllocationIndex];
if (nextAllocation.Start < allocation.End)
{
//Wrapped around, so the gap goes from here to the end of the memory block, and from the beginning of the memory block to the next allocation.
//But we need contiguous space so the two areas have to be tested independently.
if (memoryPoolSize - allocation.End >= size)
{
AddAllocation(id, outputStart = allocation.End, allocation.End + size, ref allocations.Values[allocationIndex], ref allocations.Values[nextAllocationIndex]);
return true;
}
else
{
if (nextAllocation.Start >= size)
{
AddAllocation(id, outputStart = 0, size, ref allocations.Values[allocationIndex], ref allocations.Values[nextAllocationIndex]);
return true;
}
}
}
else
{
//The next allocation is in order.
if (nextAllocation.Start - allocation.End >= size)
{
AddAllocation(id, outputStart = allocation.End, allocation.End + size, ref allocations.Values[allocationIndex], ref allocations.Values[nextAllocationIndex]);
return true;
}
}
//If we get here, no open space was found.
//Move on to the next spot.
allocationIndex = nextAllocationIndex;
//Have we already wrapped around?
if (allocations.Keys[allocationIndex] == initialId)
{
//Wrapped around without finding any space.
outputStart = 0;
return false;
}
}
}
public static void TestChurnStability() { var allocator = new Allocator(2048); var random = new Random(5); ulong idCounter = 0; var allocatedIds = new QuickList<ulong>(BufferPools<ulong>.Locking); var unallocatedIds = new QuickList<ulong>(BufferPools<ulong>.Locking); for (int i = 0; i < 512; ++i) { long start; var id = idCounter++; //allocator.ValidatePointers(); if (allocator.Allocate(id, 1 + random.Next(5), out start)) { allocatedIds.Add(id); } else { unallocatedIds.Add(id); } //allocator.ValidatePointers(); } for (int timestepIndex = 0; timestepIndex < 100000; ++timestepIndex) { //First add and remove a bunch randomly. for (int i = random.Next(Math.Min(allocatedIds.Count, 15)); i >= 0; --i) { var indexToRemove = random.Next(allocatedIds.Count); //allocator.ValidatePointers(); Assert.IsTrue(allocator.Deallocate(allocatedIds.Elements[indexToRemove])); //allocator.ValidatePointers(); unallocatedIds.Add(allocatedIds.Elements[indexToRemove]); allocatedIds.FastRemoveAt(indexToRemove); } for (int i = random.Next(Math.Min(unallocatedIds.Count, 15)); i >= 0; --i) { var indexToAllocate = random.Next(unallocatedIds.Count); long start; //allocator.ValidatePointers(); if (allocator.Allocate(unallocatedIds.Elements[indexToAllocate], random.Next(3), out start)) { //allocator.ValidatePointers(); allocatedIds.Add(unallocatedIds.Elements[indexToAllocate]); unallocatedIds.FastRemoveAt(indexToAllocate); } //allocator.ValidatePointers(); } //Check to ensure that everything's still coherent. for (int i = 0; i < allocatedIds.Count; ++i) { Assert.IsTrue(allocator.Contains(allocatedIds.Elements[i])); } for (int i = 0; i < unallocatedIds.Count; ++i) { Assert.IsFalse(allocator.Contains(unallocatedIds.Elements[i])); } } //Wind it down. for (int i = 0; i < allocatedIds.Count; ++i) { Assert.IsTrue(allocator.Deallocate(allocatedIds.Elements[i])); } //Confirm cleanup. for (int i = 0; i < allocatedIds.Count; ++i) { Assert.IsFalse(allocator.Contains(allocatedIds.Elements[i])); } for (int i = 0; i < unallocatedIds.Count; ++i) { Assert.IsFalse(allocator.Contains(unallocatedIds.Elements[i])); } }