mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-18 07:39:08 +00:00
TINSEL: Changed heap manager to use malloc internally
svn-id: r45427
This commit is contained in:
parent
05508d8dcd
commit
e07a9b0524
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user