TINSEL: Changed heap manager to use malloc internally

svn-id: r45427
This commit is contained in:
Max Horn 2009-10-27 00:37:23 +00:00
parent 05508d8dcd
commit e07a9b0524

View File

@ -36,9 +36,9 @@ namespace Tinsel {
// internal allocation flags // internal allocation flags
#define DWM_USED 0x0001 ///< the objects memory block is in use #define DWM_USED 0x0001 ///< the objects memory block is in use
#define DWM_DISCARDED 0x0100 ///< the objects memory block has been discarded #define DWM_DISCARDED 0x0002 ///< the objects memory block has been discarded
#define DWM_LOCKED 0x0200 ///< the objects memory block is locked #define DWM_LOCKED 0x0004 ///< the objects memory block is locked
#define DWM_SENTINEL 0x0400 ///< the objects memory block is a sentinel #define DWM_SENTINEL 0x0008 ///< the objects memory block is a sentinel
struct MEM_NODE { struct MEM_NODE {
@ -83,8 +83,6 @@ static MEM_NODE *AllocMemNode(void);
* Initialises the memory manager. * Initialises the memory manager.
*/ */
void MemoryInit() { void MemoryInit() {
MEM_NODE *pNode;
#ifdef DEBUG #ifdef DEBUG
// clear number of nodes in use // clear number of nodes in use
numNodes = 0; numNodes = 0;
@ -94,6 +92,7 @@ void MemoryInit() {
pFreeMemNodes = mnodeList; pFreeMemNodes = mnodeList;
// link all other objects after first // link all other objects after first
memset(mnodeList, 0, sizeof(mnodeList));
for (int i = 1; i < NUM_MNODES; i++) { for (int i = 1; i < NUM_MNODES; i++) {
mnodeList[i - 1].pNext = mnodeList + i; mnodeList[i - 1].pNext = mnodeList + i;
} }
@ -104,53 +103,37 @@ void MemoryInit() {
// clear list of fixed memory nodes // clear list of fixed memory nodes
memset(s_fixedMnodesList, 0, sizeof(s_fixedMnodesList)); memset(s_fixedMnodesList, 0, sizeof(s_fixedMnodesList));
// allocates a big chunk of memory
uint32 size = MemoryPoolSize[0];
if (TinselVersion == TINSEL_V1) size = MemoryPoolSize[1];
else if (TinselVersion == TINSEL_V2) size = MemoryPoolSize[2];
uint8 *mem = (uint8 *)malloc(size);
assert(mem);
// allocate a mnode for this memory
pNode = AllocMemNode();
// make sure mnode was allocated
assert(pNode);
// convert segment to memory address
pNode->pBaseAddr = mem;
// set size of the memory heap
pNode->size = size;
// clear the memory
memset(pNode->pBaseAddr, 0, size);
// set cyclic links to the sentinel // set cyclic links to the sentinel
heapSentinel.pPrev = pNode; heapSentinel.pPrev = &heapSentinel;
heapSentinel.pNext = pNode; heapSentinel.pNext = &heapSentinel;
pNode->pPrev = &heapSentinel;
pNode->pNext = &heapSentinel;
// flag sentinel as locked // flag sentinel as locked
heapSentinel.flags = DWM_LOCKED | DWM_SENTINEL; heapSentinel.flags = DWM_LOCKED | DWM_SENTINEL;
// store the start of the master data block in the sentinel // store the current heap size in the sentinel
heapSentinel.pBaseAddr = mem; uint32 size = MemoryPoolSize[0];
if (TinselVersion == TINSEL_V1) size = MemoryPoolSize[1];
else if (TinselVersion == TINSEL_V2) size = MemoryPoolSize[2];
heapSentinel.size = size;
} }
/** /**
* Deinitialises the memory manager. * Deinitialises the memory manager.
*/ */
void MemoryDeinit() { void MemoryDeinit() {
MEM_NODE *pNode = s_fixedMnodesList; const MEM_NODE *pHeap = &heapSentinel;
for (int i = 0; i < ARRAYSIZE(s_fixedMnodesList); ++i, ++pNode) { MEM_NODE *pCur;
free(pNode->pBaseAddr);
pNode->pBaseAddr = 0; pCur = s_fixedMnodesList;
for (int i = 0; i < ARRAYSIZE(s_fixedMnodesList); ++i, ++pCur) {
free(pCur->pBaseAddr);
pCur->pBaseAddr = 0;
} }
// free memory used for the memory pool for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
free(heapSentinel.pBaseAddr); free(pCur->pBaseAddr);
pCur->pBaseAddr = 0;
}
} }
@ -209,71 +192,11 @@ void FreeMemNode(MEM_NODE *pMemNode) {
* @return true if any blocks were discarded, false otherwise * @return true if any blocks were discarded, false otherwise
*/ */
bool HeapCompact(long size, bool bDiscard) { bool HeapCompact(long size, bool bDiscard) {
MEM_NODE *pHeap = &heapSentinel; const MEM_NODE *pHeap = &heapSentinel;
MEM_NODE *pPrev, *pCur, *pOldest; MEM_NODE *pCur, *pOldest;
long largest; // size of largest free block
uint32 oldest; // time of the oldest discardable block uint32 oldest; // time of the oldest discardable block
while (true) { while (heapSentinel.size < size) {
bool bChanged;
do {
bChanged = false;
for (pPrev = pHeap->pNext, pCur = pPrev->pNext;
pCur != pHeap; pPrev = pCur, pCur = pCur->pNext) {
if (pPrev->flags == 0 && pCur->flags == 0) {
// both blocks are free - merge them
pPrev->size += pCur->size;
// unlink the current mnode
pPrev->pNext = pCur->pNext;
pCur->pNext->pPrev = pPrev;
// free the current mnode
FreeMemNode(pCur);
// set the changed flag
bChanged = true;
// leave the loop
break;
} else if (pPrev->flags == DWM_USED && pCur->flags == 0) {
// a free block after a moveable, non-discarded, not locked block - swap them
// set the changed flag
bChanged = true;
// move the unlocked blocks data up (can overlap)
memmove(pPrev->pBaseAddr + pCur->size, pPrev->pBaseAddr, pPrev->size);
// swap the order in the linked list
pPrev->pPrev->pNext = pCur;
pCur->pNext->pPrev = pPrev;
pCur->pPrev = pPrev->pPrev;
pPrev->pPrev = pCur;
pPrev->pNext = pCur->pNext;
pCur->pNext = pPrev;
pCur->pBaseAddr = pPrev->pBaseAddr;
pPrev->pBaseAddr += pCur->size;
// leave the loop
break;
}
}
} while (bChanged);
// find the largest free block
for (largest = 0, pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
if (pCur->flags == 0 && pCur->size > largest)
largest = pCur->size;
}
if (largest >= size)
// we have freed enough memory
return true;
if (!bDiscard) if (!bDiscard)
// we cannot free enough without discarding blocks // we cannot free enough without discarding blocks
@ -299,6 +222,9 @@ bool HeapCompact(long size, bool bDiscard) {
// cannot discard any blocks // cannot discard any blocks
return false; return false;
} }
// we have freed enough memory
return true;
} }
/** /**
@ -307,58 +233,46 @@ bool HeapCompact(long size, bool bDiscard) {
* @param size Number of bytes to allocate * @param size Number of bytes to allocate
*/ */
static MEM_NODE *MemoryAlloc(long size) { static MEM_NODE *MemoryAlloc(long size) {
const MEM_NODE *pHeap = &heapSentinel; MEM_NODE *pHeap = &heapSentinel;
MEM_NODE *pNode;
#ifdef SCUMM_NEED_ALIGNMENT #ifdef SCUMM_NEED_ALIGNMENT
const int alignPadding = sizeof(void*) - 1; const int alignPadding = sizeof(void*) - 1;
size = (size + alignPadding) & ~alignPadding; //round up to nearest multiple of sizeof(void*), this ensures the addresses that are returned are alignment-safe. size = (size + alignPadding) & ~alignPadding; //round up to nearest multiple of sizeof(void*), this ensures the addresses that are returned are alignment-safe.
#endif #endif
do { // compact the heap to make up room for 'size' bytes, if necessary
// search the heap for a free block if (!HeapCompact(size, true))
return 0;
for (pNode = pHeap->pNext; pNode != pHeap; pNode = pNode->pNext) { // success! we may allocate a new node of the right size
if (pNode->flags == 0 && pNode->size >= size) {
// a free block of the required size
pNode->flags = DWM_USED;
// update the LRU time // Allocate a node.
pNode->lruTime = DwGetCurrentTime() + 1; MEM_NODE *pNode = AllocMemNode();
if (pNode->size == size) { // Allocate memory for the node.
// an exact fit pNode->pBaseAddr = (byte *)malloc(size);
return pNode;
} else {
// allocate a node for the remainder of the free block
MEM_NODE *pTemp = AllocMemNode();
// calc size of the free block // Verify that we got the memory.
long freeSize = pNode->size - size; // TODO: If this fails, we should first try to compact the heap some further.
assert(pNode->pBaseAddr);
// set size of free block // Subtract size of new block from total
pTemp->size = freeSize; heapSentinel.size -= size;
// set size of node // Set flags, LRU time and size
pNode->size = size; pNode->flags = DWM_USED;
pNode->lruTime = DwGetCurrentTime();
pNode->size = size;
// place the free node before pNode // set mnode at the end of the list
pTemp->pBaseAddr = pNode->pBaseAddr; pNode->pPrev = pHeap->pPrev;
pNode->pBaseAddr += freeSize; pNode->pNext = pHeap;
pTemp->pNext = pNode;
pTemp->pPrev = pNode->pPrev;
pNode->pPrev->pNext = pTemp;
pNode->pPrev = pTemp;
return pNode; // fix links to this mnode
} pHeap->pPrev->pNext = pNode;
} pHeap->pPrev = pNode;
}
// compact the heap if we get to here
} while (HeapCompact(size, true));
// could not allocate a block return pNode;
return 0;
} }
/** /**
@ -371,6 +285,8 @@ MEM_NODE *MemoryNoAlloc() {
// chain a discarded node onto the end of the heap // chain a discarded node onto the end of the heap
MEM_NODE *pNode = AllocMemNode(); MEM_NODE *pNode = AllocMemNode();
pNode->flags = DWM_USED | DWM_DISCARDED; pNode->flags = DWM_USED | DWM_DISCARDED;
pNode->lruTime = DwGetCurrentTime();
pNode->size = 0;
// set mnode at the end of the list // set mnode at the end of the list
pNode->pPrev = pHeap->pPrev; pNode->pPrev = pHeap->pPrev;
@ -406,6 +322,10 @@ MEM_NODE *MemoryAllocFixed(long size) {
pNode->size = size; pNode->size = size;
pNode->lruTime = DwGetCurrentTime(); pNode->lruTime = DwGetCurrentTime();
pNode->flags = DWM_USED; pNode->flags = DWM_USED;
// Subtract size of new block from total
heapSentinel.size -= size;
return pNode; return pNode;
} }
} }
@ -427,36 +347,14 @@ void MemoryDiscard(MEM_NODE *pMemNode) {
// discard it if it isn't already // discard it if it isn't already
if ((pMemNode->flags & DWM_DISCARDED) == 0) { if ((pMemNode->flags & DWM_DISCARDED) == 0) {
// allocate a free node to replace this node // free memory
MEM_NODE *pTemp = AllocMemNode(); free(pMemNode->pBaseAddr);
heapSentinel.size += pMemNode->size;
// copy node data // mark the node as discarded
memcpy(pTemp, pMemNode, sizeof(MEM_NODE));
// flag as a free block
pTemp->flags = 0;
// link in the free node
pTemp->pPrev->pNext = pTemp;
pTemp->pNext->pPrev = pTemp;
// discard the node
pMemNode->flags |= DWM_DISCARDED; pMemNode->flags |= DWM_DISCARDED;
pMemNode->pBaseAddr = NULL; pMemNode->pBaseAddr = NULL;
pMemNode->size = 0; pMemNode->size = 0;
// and place it at the end of the heap
while ((pTemp->flags & DWM_SENTINEL) != DWM_SENTINEL)
pTemp = pTemp->pNext;
// pTemp now points to the heap sentinel
// set mnode at the end of the list
pMemNode->pPrev = pTemp->pPrev;
pMemNode->pNext = pTemp;
// fix links to this mnode
pTemp->pPrev->pNext = pMemNode;
pTemp->pPrev = pMemNode;
} }
} }