Algorithm Challenge
Posted: Wed Aug 12, 2009 9:37 pm
I'm doing a performance sweep and working on a new heap allocator. The design goal with this implementation is to be non-blocking and multi-threaded.
This heap manager comes into play after paging is enabled but maybe adapted for a non-paged heap. The problem arises when multiple threads hit it, single threaded it works fine. There's a couple bugs (or a single bug that manifests itself in a couple ways). In one case a linked list item will refer to itself so gets stuck in a perpetual loop. In another case there seems to be an alignment issue where the magic values dont match up properly. Such a trivial block of code shouldn't take more than a couple hours to nail down but this one has got me perplexed. (When a process is created the _FreePartitions member is setup with {0xDeadBeef, NULL, 0xffffffff-sizeof(TPartitionInfo), 0xDeadBeef}) It then divides up memory as requested and merges adjacent partitions during a free. Any help or suggestions are appreciated.
Code: Select all
//partitionalloc.h
#pragma once
#if !defined(__PART_ALLOC_H_INCLUDED__)
#define __PART_ALLOC_H_INCLUDED__
class PartitionAllocator{
public:
#pragma pack(push, 1)
typedef struct TPartitionInfo{
ui32 Magic1;
TPartitionInfo * Next;
ui32 Size;
ui32 Magic2;
}TPartitionInfo;
#pragma pack(pop)
static const ui32 PartitionMagic = 0xDeadBeef;
typedef TPartitionInfo * PartitionInfo;
PartitionInfo _FreePartitions;
PartitionInfo _UsedPartitions;
PartitionAllocator() : _FreePartitions(NULL), _UsedPartitions(NULL){}
void * Alloc(size_t size){
if (!size) return NULL;
ui32 iActualSize = size + sizeof(TPartitionInfo);
Retry1:
for (PartitionInfo pFreePartition = _FreePartitions ; pFreePartition ; pFreePartition = pFreePartition->Next){
assert(pFreePartition->Magic1 == PartitionMagic && pFreePartition->Magic2 == PartitionMagic);
ui32 iFreePartitionSize = pFreePartition->Size;
if (iFreePartitionSize > iActualSize){
if ((ui32)iFreePartitionSize != InterlockedCompareExchange((ui32*)&pFreePartition->Size, (iFreePartitionSize-iActualSize), iFreePartitionSize)) continue;
PartitionInfo pRet = (PartitionInfo)((ui32)pFreePartition + sizeof(TPartitionInfo) + (iFreePartitionSize-iActualSize));
pRet->Magic1 = pRet->Magic2 = PartitionMagic;
pRet->Size = size;
for(;;){
pRet->Next = _UsedPartitions;
if ((ui32)pRet->Next != InterlockedCompareExchange((ui32*)&_UsedPartitions, (ui32)pRet, (ui32)pRet->Next)) goto Retry1;
return ++pRet;
}
}
}
return NULL;
}
void Free(void*addr){
if (!addr) return;
ui32 iSize;
PartitionInfo FreeMe = (PartitionInfo)addr;
FreeMe--;
assert(FreeMe->Magic1 == PartitionMagic && FreeMe->Magic2 == PartitionMagic);
//first remove this item from the _UsedPartitions list
Retry1:
if ((ui32)FreeMe != InterlockedCompareExchange((ui32*)&_UsedPartitions, (ui32)FreeMe->Next, (ui32)FreeMe)) { //if its not the 1st item then iterate
for (PartitionInfo pUsedPartition = _UsedPartitions ; pUsedPartition ; pUsedPartition = pUsedPartition->Next){
assert(pUsedPartition->Magic1 == PartitionMagic && pUsedPartition->Magic2 == PartitionMagic);
if (pUsedPartition->Next && pUsedPartition->Next==FreeMe){
if ((ui32)FreeMe->Next != InterlockedCompareExchange((ui32*)&pUsedPartition->Next, (ui32)FreeMe->Next, (ui32)FreeMe)) goto Retry1;
}
}
}
//now push FreeMe on the _FreePartitions merging blocks if needed
PartitionInfo PartitionAfterFreeMe = (PartitionInfo)((ui32)FreeMe + FreeMe->Size + sizeof(TPartitionInfo));
Retry2:
for (PartitionInfo pFreePartition = _FreePartitions ; pFreePartition ; pFreePartition = pFreePartition->Next){
assert(pFreePartition->Magic1 == PartitionMagic && pFreePartition->Magic2 == PartitionMagic);
if (pFreePartition->Next == PartitionAfterFreeMe){
assert(PartitionAfterFreeMe->Magic1 == PartitionMagic && PartitionAfterFreeMe->Magic2 == PartitionMagic);
//merge FreeMe and PartitionAfterFreeMe by modifying FreeMe to include PartitionAfterFreeMe and destroy PartitionAfterFreeMe
iSize = FreeMe->Size; //save this in case it must revert
FreeMe->Size += PartitionAfterFreeMe->Size + sizeof(TPartitionInfo);
FreeMe->Next = PartitionAfterFreeMe->Next;
if ((ui32)PartitionAfterFreeMe != InterlockedCompareExchange((ui32*)&pFreePartition->Next, (ui32)FreeMe, (ui32)PartitionAfterFreeMe)){
//pFreePartition was modified (PartitionAfterFreeMe was probably acquired) revert the size modification and retry
FreeMe->Size = iSize;
goto Retry2;
}
}
if ((ui32)pFreePartition + pFreePartition->Size + sizeof(TPartitionInfo) == (ui32)FreeMe){
//merge blocks where FreeMe is at the end of pFreePartition
//do so by expanding pFreePartition
iSize = FreeMe->Size + pFreePartition->Size + sizeof(TPartitionInfo);
ui32 iCurrentSize = pFreePartition->Size;
if (iCurrentSize != (ui32)InterlockedCompareExchange((ui32*)&pFreePartition->Size, iSize, iCurrentSize)) goto Retry2;
return;
}else if (!pFreePartition->Next){ //end of list, add here
if (NULL != InterlockedCompareExchange((ui32*)&pFreePartition->Next, (ui32)FreeMe, NULL)) goto Retry2;
break;
}
}
}
};
#endif//__PART_ALLOC_H_INCLUDED__