completed porting forward the chunks and base layers of the heap management code
This commit is contained in:
parent
0f309bbbc0
commit
f1950fa933
|
@ -50,7 +50,8 @@ typedef struct tagRAWHEAPDATA
|
||||||
UINT32 opaque[32]; /* opaque data, do not modify */
|
UINT32 opaque[32]; /* opaque data, do not modify */
|
||||||
} RAWHEAPDATA, *PRAWHEAPDATA;
|
} RAWHEAPDATA, *PRAWHEAPDATA;
|
||||||
|
|
||||||
typedef void (*PFNRAWHEAPDATAFREE)(PRAWHEAPDATA prhd); /* function that optionally frees the heap data */
|
typedef void (*PFNRAWHEAPDATAFREE)(PRAWHEAPDATA); /* function that optionally frees the heap data */
|
||||||
|
typedef void (*PFNHEAPABORT)(PVOID); /* function called to abort on serious error */
|
||||||
|
|
||||||
/*--------------------
|
/*--------------------
|
||||||
* External functions
|
* External functions
|
||||||
|
@ -61,8 +62,9 @@ typedef void (*PFNRAWHEAPDATAFREE)(PRAWHEAPDATA prhd); /* function that optional
|
||||||
|
|
||||||
CDECL_BEGIN
|
CDECL_BEGIN
|
||||||
|
|
||||||
extern HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocator *pChunkAllocator,
|
extern HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, PFNHEAPABORT pfnAbort, PVOID pvAbortArg,
|
||||||
IMutexFactory *pMutexFactory, UINT32 nChunkBits, IMalloc **ppHeap);
|
IChunkAllocator *pChunkAllocator, IMutexFactory *pMutexFactory, UINT32 nChunkBits,
|
||||||
|
IMalloc **ppHeap);
|
||||||
|
|
||||||
CDECL_END
|
CDECL_END
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ typedef struct tagLDIV {
|
||||||
CDECL_BEGIN
|
CDECL_BEGIN
|
||||||
|
|
||||||
extern HRESULT IntLDiv(PLDIV pResult, INT64 num, INT64 denom);
|
extern HRESULT IntLDiv(PLDIV pResult, INT64 num, INT64 denom);
|
||||||
|
extern INT32 IntFirstSet(INT32 nMask);
|
||||||
|
|
||||||
CDECL_END
|
CDECL_END
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,10 @@
|
||||||
#define INT64_BITS 64
|
#define INT64_BITS 64
|
||||||
#define UINT64_BITS 64
|
#define UINT64_BITS 64
|
||||||
|
|
||||||
|
#define LOG_PTRSIZE 2 /* log2(sizeof(void *)) */
|
||||||
|
#define LOG_INTSIZE 2 /* log2(sizeof(int)) */
|
||||||
|
#define LOG_INT64SIZE 3 /* log2(sizeof(long long)) */
|
||||||
|
|
||||||
/* Boolean values */
|
/* Boolean values */
|
||||||
#define TRUE 1
|
#define TRUE 1
|
||||||
#define FALSE 0
|
#define FALSE 0
|
||||||
|
|
|
@ -32,8 +32,8 @@ MAKEFLAGS += -rR
|
||||||
CRBASEDIR := $(abspath ../..)
|
CRBASEDIR := $(abspath ../..)
|
||||||
include $(CRBASEDIR)/armcompile.mk
|
include $(CRBASEDIR)/armcompile.mk
|
||||||
|
|
||||||
LIB_OBJS = divide.o qdivrem.o heap_toplevel.o heap_base.o heap_chunks.o intlib.o objhelp.o objhelp_enumconn.o \
|
LIB_OBJS = divide.o qdivrem.o heap_toplevel.o heap_base.o heap_chunks.o heap_rtree.o heap_utils.o intlib.o objhelp.o \
|
||||||
objhelp_enumgeneric.o objhelp_fixedcp.o rbtree.o str.o strcopymem.o strcomparemem.o \
|
objhelp_enumconn.o objhelp_enumgeneric.o objhelp_fixedcp.o rbtree.o str.o strcopymem.o strcomparemem.o \
|
||||||
strsetmem.o lib_guids.o
|
strsetmem.o lib_guids.o
|
||||||
|
|
||||||
all: kernel-lib.o
|
all: kernel-lib.o
|
||||||
|
|
|
@ -41,23 +41,53 @@
|
||||||
#include <comrogue/internals/mmu.h>
|
#include <comrogue/internals/mmu.h>
|
||||||
#include "heap_internals.h"
|
#include "heap_internals.h"
|
||||||
|
|
||||||
|
/*-----------------------------------
|
||||||
|
* Base memory allocation functions.
|
||||||
|
*-----------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new chunk of memory for use by the base allocator, and point to it with the allocator pointers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - szMinimum = Minimum number of bytes we need to allocate.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - TRUE = Allocation failed.
|
||||||
|
* - FALSE = Allocation succeeded.
|
||||||
|
*/
|
||||||
static BOOL base_alloc_new_chunk(PHEAPDATA phd, SIZE_T szMinimum)
|
static BOOL base_alloc_new_chunk(PHEAPDATA phd, SIZE_T szMinimum)
|
||||||
{
|
{
|
||||||
BOOL fZero = FALSE; /* do we need this zeroed? (no) */
|
|
||||||
SIZE_T szAdjusted; /* adjusted size to multiple of chunk size */
|
SIZE_T szAdjusted; /* adjusted size to multiple of chunk size */
|
||||||
PVOID pvNewChunk; /* pointer to new chunk */
|
PVOID pvNewChunk; /* pointer to new chunk */
|
||||||
|
PVOID pvTemp; /* temporary pointer */
|
||||||
|
BOOL fZero = FALSE; /* do we need this zeroed? (no) */
|
||||||
|
|
||||||
szAdjusted = CHUNK_CEILING(phd, szMinimum);
|
szAdjusted = CHUNK_CEILING(phd, szMinimum);
|
||||||
pvNewChunk = _HeapChunkAlloc(phd, szAdjusted, phd->szChunk, TRUE, &fZero);
|
pvNewChunk = _HeapChunkAlloc(phd, szAdjusted, phd->szChunk, TRUE, &fZero);
|
||||||
if (!pvNewChunk)
|
if (!pvNewChunk)
|
||||||
return TRUE; /* allocation failed */
|
return TRUE; /* allocation failed */
|
||||||
*((PPVOID)pvNewChunk) = phd->pvBasePages;
|
*((PPVOID)pvNewChunk) = phd->pvBasePages;
|
||||||
|
pvTemp = (PVOID)(((UINT_PTR)pvNewChunk) + sizeof(PVOID));
|
||||||
|
*((SIZE_T *)pvTemp) = szAdjusted;
|
||||||
phd->pvBasePages = pvNewChunk;
|
phd->pvBasePages = pvNewChunk;
|
||||||
phd->pvBaseNext = (PVOID)(((UINT_PTR)pvNewChunk) + sizeof(PVOID));
|
phd->pvBaseNext = (PVOID)(((UINT_PTR)pvNewChunk) + sizeof(PVOID) + sizeof(SIZE_T));
|
||||||
phd->pvBasePast = (PVOID)(((UINT_PTR)pvNewChunk) + szAdjusted);
|
phd->pvBasePast = (PVOID)(((UINT_PTR)pvNewChunk) + szAdjusted);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a chunk of memory from the base allocator.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - sz = Number of bytes to allocate.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - NULL = Allocation failed.
|
||||||
|
* - Other = Pointer to the allocated block of memory.
|
||||||
|
*/
|
||||||
PVOID _HeapBaseAlloc(PHEAPDATA phd, SIZE_T sz)
|
PVOID _HeapBaseAlloc(PHEAPDATA phd, SIZE_T sz)
|
||||||
{
|
{
|
||||||
PVOID rc = NULL; /* return from this function */
|
PVOID rc = NULL; /* return from this function */
|
||||||
|
@ -68,8 +98,8 @@ PVOID _HeapBaseAlloc(PHEAPDATA phd, SIZE_T sz)
|
||||||
IMutex_Lock(phd->pmtxBase);
|
IMutex_Lock(phd->pmtxBase);
|
||||||
if (((UINT_PTR)(phd->pvBaseNext) + szAdjusted) > (UINT_PTR)(phd->pvBasePast))
|
if (((UINT_PTR)(phd->pvBaseNext) + szAdjusted) > (UINT_PTR)(phd->pvBasePast))
|
||||||
{ /* allocate new chunk if we don't have enough space for the allocation */
|
{ /* allocate new chunk if we don't have enough space for the allocation */
|
||||||
if (base_alloc_new_chunk(phd, szAdjusted + sizeof(PVOID)))
|
if (base_alloc_new_chunk(phd, szAdjusted + sizeof(PVOID) + sizeof(SIZE_T)))
|
||||||
goto error0;
|
goto error0; /* failed */
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = phd->pvBaseNext; /* perform the allocation */
|
rc = phd->pvBaseNext; /* perform the allocation */
|
||||||
|
@ -80,6 +110,16 @@ error0:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new EXTENT_NODE from the base allocator.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - NULL = Allocation failed.
|
||||||
|
* - Other = Pointer to the new EXTENT_NODE.
|
||||||
|
*/
|
||||||
PEXTENT_NODE _HeapBaseNodeAlloc(PHEAPDATA phd)
|
PEXTENT_NODE _HeapBaseNodeAlloc(PHEAPDATA phd)
|
||||||
{
|
{
|
||||||
PEXTENT_NODE rc = NULL; /* return from this function */
|
PEXTENT_NODE rc = NULL; /* return from this function */
|
||||||
|
@ -97,6 +137,16 @@ PEXTENT_NODE _HeapBaseNodeAlloc(PHEAPDATA phd)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns an EXTENT_NODE to the free list.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - pexn = Pointer to the EXTENT_NODE to be freed.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
void _HeapBaseNodeDeAlloc(PHEAPDATA phd, PEXTENT_NODE pexn)
|
void _HeapBaseNodeDeAlloc(PHEAPDATA phd, PEXTENT_NODE pexn)
|
||||||
{
|
{
|
||||||
/* add it to the free list */
|
/* add it to the free list */
|
||||||
|
@ -106,6 +156,15 @@ void _HeapBaseNodeDeAlloc(PHEAPDATA phd, PEXTENT_NODE pexn)
|
||||||
IMutex_Unlock(phd->pmtxBase);
|
IMutex_Unlock(phd->pmtxBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the base allocator.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Standard HRESULT success/failure indicator.
|
||||||
|
*/
|
||||||
HRESULT _HeapBaseSetup(PHEAPDATA phd)
|
HRESULT _HeapBaseSetup(PHEAPDATA phd)
|
||||||
{
|
{
|
||||||
HRESULT hr = IMutexFactory_CreateMutex(phd->pMutexFactory, &(phd->pmtxBase));
|
HRESULT hr = IMutexFactory_CreateMutex(phd->pMutexFactory, &(phd->pmtxBase));
|
||||||
|
@ -115,8 +174,32 @@ HRESULT _HeapBaseSetup(PHEAPDATA phd)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shut down the base allocator.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
void _HeapBaseShutdown(PHEAPDATA phd)
|
void _HeapBaseShutdown(PHEAPDATA phd)
|
||||||
{
|
{
|
||||||
/* TODO */
|
PVOID pChunk; /* the chunk we're working on */
|
||||||
|
PVOID pNextChunk; /* the next chunk to be dealt with */
|
||||||
|
PVOID pvTemp; /* temporary pointer */
|
||||||
|
SIZE_T szChunk; /* size of allocated chunk */
|
||||||
|
|
||||||
|
/* All allocated "base" chunks come straight from the IChunkAllocator, so it's safe to free them this way. */
|
||||||
|
pChunk = phd->pvBasePages;
|
||||||
|
while (pChunk)
|
||||||
|
{
|
||||||
|
pNextChunk = *((PPVOID)pChunk);
|
||||||
|
pvTemp = (PVOID)(((UINT_PTR)pChunk) + sizeof(PVOID));
|
||||||
|
szChunk = *((SIZE_T *)pvTemp);
|
||||||
|
IChunkAllocator_FreeChunk(phd->pChunkAllocator, pChunk, szChunk);
|
||||||
|
pChunk = pNextChunk;
|
||||||
|
}
|
||||||
|
|
||||||
IUnknown_Release(phd->pmtxBase);
|
IUnknown_Release(phd->pmtxBase);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,25 +34,42 @@
|
||||||
*/
|
*/
|
||||||
#include <comrogue/compiler_macros.h>
|
#include <comrogue/compiler_macros.h>
|
||||||
#include <comrogue/types.h>
|
#include <comrogue/types.h>
|
||||||
|
#include <comrogue/str.h>
|
||||||
#include <comrogue/objectbase.h>
|
#include <comrogue/objectbase.h>
|
||||||
#include <comrogue/scode.h>
|
#include <comrogue/scode.h>
|
||||||
#include <comrogue/stdobj.h>
|
#include <comrogue/stdobj.h>
|
||||||
#include <comrogue/internals/mmu.h>
|
#include <comrogue/internals/mmu.h>
|
||||||
#include "heap_internals.h"
|
#include "heap_internals.h"
|
||||||
|
|
||||||
|
#ifdef _H_THIS_FILE
|
||||||
|
#undef _H_THIS_FILE
|
||||||
|
_DECLARE_H_THIS_FILE
|
||||||
|
#endif
|
||||||
|
|
||||||
/*------------------------------------------------------------------------
|
/*------------------------------------------------------------------------
|
||||||
* Functions driving the red-black trees that contain EXTENT_NODE objects
|
* Functions driving the red-black trees that contain EXTENT_NODE objects
|
||||||
*------------------------------------------------------------------------
|
*------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two extent nodes, first by size, then by address.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - pexnA = First extent node to compare.
|
||||||
|
* - pexnB = Second extent node to compare.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A value less than, equal to, or greater than 0 as pexnA is less than, equal to, or greater than pexnB.
|
||||||
|
*/
|
||||||
static INT32 compare_sizeaddr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB)
|
static INT32 compare_sizeaddr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB)
|
||||||
{
|
{
|
||||||
|
/* compare sizes first */
|
||||||
SIZE_T szA = pexnA->sz;
|
SIZE_T szA = pexnA->sz;
|
||||||
SIZE_T szB = pexnB->sz;
|
SIZE_T szB = pexnB->sz;
|
||||||
INT32 rc = (szA > szB) - (szA < szB);
|
INT32 rc = (szA > szB) - (szA < szB);
|
||||||
|
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{ /* compare addresses */
|
||||||
UINT_PTR addrA = (UINT_PTR)(pexnA->pv);
|
UINT_PTR addrA = (UINT_PTR)(pexnA->pv);
|
||||||
UINT_PTR addrB = (UINT_PTR)(pexnB->pv);
|
UINT_PTR addrB = (UINT_PTR)(pexnB->pv);
|
||||||
rc = (addrA > addrB) - (addrA < addrB);
|
rc = (addrA > addrB) - (addrA < addrB);
|
||||||
|
@ -60,6 +77,16 @@ static INT32 compare_sizeaddr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two extent nodes by address.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - pexnA = First extent node to compare.
|
||||||
|
* - pexnB = Second extent node to compare.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A value less than, equal to, or greater than 0 as pexnA is less than, equal to, or greater than pexnB.
|
||||||
|
*/
|
||||||
static INT32 compare_addr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB)
|
static INT32 compare_addr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB)
|
||||||
{
|
{
|
||||||
UINT_PTR addrA = (UINT_PTR)(pexnA->pv);
|
UINT_PTR addrA = (UINT_PTR)(pexnA->pv);
|
||||||
|
@ -67,21 +94,57 @@ static INT32 compare_addr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB)
|
||||||
return (addrA > addrB) - (addrA < addrB);
|
return (addrA > addrB) - (addrA < addrB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a pointer to an extent node, returns a pointer to its RBTREENODE for the size-address tree.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - pexn = Pointer to the extent node.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Pointer to the extent node's "size-address" RBTREENODE.
|
||||||
|
*/
|
||||||
static PRBTREENODE get_sizeaddr_node(PEXTENT_NODE pexn)
|
static PRBTREENODE get_sizeaddr_node(PEXTENT_NODE pexn)
|
||||||
{
|
{
|
||||||
return &(pexn->rbtnSizeAddress);
|
return &(pexn->rbtnSizeAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a pointer to an extent node, returns a pointer to its RBTREENODE for the address tree.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - pexn = Pointer to the extent node.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Pointer to the extent node's "address" RBTREENODE.
|
||||||
|
*/
|
||||||
static PRBTREENODE get_addr_node(PEXTENT_NODE pexn)
|
static PRBTREENODE get_addr_node(PEXTENT_NODE pexn)
|
||||||
{
|
{
|
||||||
return &(pexn->rbtnAddress);
|
return &(pexn->rbtnAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a pointer to an extent node's "size-address" RBTREENODE, returns a pointer to the extent node.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - prbtn = Pointer to the "size-address" RBTREENODE.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Pointer to the base extent node.
|
||||||
|
*/
|
||||||
static PEXTENT_NODE get_from_sizeaddr_node(PRBTREENODE prbtn)
|
static PEXTENT_NODE get_from_sizeaddr_node(PRBTREENODE prbtn)
|
||||||
{
|
{
|
||||||
return (PEXTENT_NODE)(((UINT_PTR)prbtn) - OFFSETOF(EXTENT_NODE, rbtnSizeAddress));
|
return (PEXTENT_NODE)(((UINT_PTR)prbtn) - OFFSETOF(EXTENT_NODE, rbtnSizeAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a pointer to an extent node's "address" RBTREENODE, returns a pointer to the extent node.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - prbtn = Pointer to the "address" RBTREENODE.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Pointer to the base extent node.
|
||||||
|
*/
|
||||||
static PEXTENT_NODE get_from_addr_node(PRBTREENODE prbtn)
|
static PEXTENT_NODE get_from_addr_node(PRBTREENODE prbtn)
|
||||||
{
|
{
|
||||||
return (PEXTENT_NODE)(((UINT_PTR)prbtn) - OFFSETOF(EXTENT_NODE, rbtnAddress));
|
return (PEXTENT_NODE)(((UINT_PTR)prbtn) - OFFSETOF(EXTENT_NODE, rbtnAddress));
|
||||||
|
@ -92,31 +155,320 @@ static PEXTENT_NODE get_from_addr_node(PRBTREENODE prbtn)
|
||||||
*----------------------
|
*----------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Looks for a chunk in the "free extents" list that can be used to satisfy an allocation, and returns it
|
||||||
|
* if one is found.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - sz = Size of the chunk to be allocated.
|
||||||
|
* - szAlignment = Specifies the alignment that the allocated chunk should have.
|
||||||
|
* - pfZeroed = Points to a flag which indicates if the allocated chunk should be zeroed. If the actual
|
||||||
|
* chunk we find is zeroed, this gets toggled to TRUE.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - NULL = A recycled chunk could not be found.
|
||||||
|
* - Other = Pointer to the recycled chunk.
|
||||||
|
*/
|
||||||
|
static PVOID chunk_recycle(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL *pfZeroed)
|
||||||
|
{
|
||||||
|
PVOID rc; /* return from this function */
|
||||||
|
SIZE_T szAlloc; /* allocation size to look for */
|
||||||
|
SIZE_T szLeading; /* leading size of the chunk that can be returned */
|
||||||
|
SIZE_T szTrailing; /* trailing size of the chunk that can be returned */
|
||||||
|
BOOL fZeroed; /* is this block zeroed? */
|
||||||
|
PEXTENT_NODE pexn; /* pointer to extent node we find */
|
||||||
|
EXTENT_NODE exnKey; /* key for tree search */
|
||||||
|
|
||||||
|
/* Look for a big enough block in the tree. */
|
||||||
|
szAlloc = sz + szAlignment - phd->szChunk;
|
||||||
|
if (szAlloc < sz)
|
||||||
|
return NULL; /* the allocator wrapped around */
|
||||||
|
exnKey.pv = NULL;
|
||||||
|
exnKey.sz = szAlloc;
|
||||||
|
IMutex_Lock(phd->pmtxChunks);
|
||||||
|
pexn = (PEXTENT_NODE)RbtFindSuccessor(&(phd->rbtExtSizeAddr), (TREEKEY)(&exnKey));
|
||||||
|
if (!pexn)
|
||||||
|
{ /* no big enough block found, bug out */
|
||||||
|
IMutex_Unlock(phd->pmtxChunks);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute leading and trailing sizes in this block and point to return data. */
|
||||||
|
szLeading = ALIGNMENT_CEILING((UINT_PTR)(pexn->pv), szAlignment - 1) - (UINT_PTR)(pexn->pv);
|
||||||
|
_H_ASSERT(phd, pexn->sz >= szLeading + sz);
|
||||||
|
szTrailing = pexn->sz - szLeading - sz;
|
||||||
|
rc = (PVOID)((UINT_PTR)(pexn->pv) + szLeading);
|
||||||
|
fZeroed = pexn->fZeroed;
|
||||||
|
if (fZeroed)
|
||||||
|
*pfZeroed = TRUE;
|
||||||
|
|
||||||
|
/* Remove existing node from the trees. */
|
||||||
|
RbtDelete(&(phd->rbtExtSizeAddr), (TREEKEY)pexn);
|
||||||
|
RbtDelete(&(phd->rbtExtAddr), (TREEKEY)pexn);
|
||||||
|
|
||||||
|
if (szLeading > 0)
|
||||||
|
{ /* insert leading space as smaller chunk */
|
||||||
|
pexn->sz = szLeading;
|
||||||
|
rbtNewNode(&(pexn->rbtnSizeAddress));
|
||||||
|
rbtNewNode(&(pexn->rbtnAddress));
|
||||||
|
RbtInsert(&(phd->rbtExtSizeAddr), pexn);
|
||||||
|
RbtInsert(&(phd->rbtExtAddr), pexn);
|
||||||
|
pexn = NULL; /* reused the node, no longer there */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (szTrailing > 0)
|
||||||
|
{ /* insert trailing space as smaller chunk */
|
||||||
|
if (!pexn)
|
||||||
|
{ /* allocate another node, dropping mutex first to avoid deadlock */
|
||||||
|
IMutex_Unlock(phd->pmtxChunks);
|
||||||
|
pexn = _HeapBaseNodeAlloc(phd);
|
||||||
|
if (!pexn)
|
||||||
|
{ /* node allocation failed, reverse allocation and bug out */
|
||||||
|
_HeapChunkDeAlloc(phd, rc, sz, TRUE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
IMutex_Lock(phd->pmtxChunks);
|
||||||
|
}
|
||||||
|
pexn->pv = (PVOID)(((UINT_PTR)rc) + sz);
|
||||||
|
pexn->sz = szTrailing;
|
||||||
|
pexn->fZeroed = fZeroed;
|
||||||
|
rbtNewNode(&(pexn->rbtnSizeAddress));
|
||||||
|
rbtNewNode(&(pexn->rbtnAddress));
|
||||||
|
RbtInsert(&(phd->rbtExtSizeAddr), pexn);
|
||||||
|
RbtInsert(&(phd->rbtExtAddr), pexn);
|
||||||
|
pexn = NULL; /* used the node */
|
||||||
|
}
|
||||||
|
|
||||||
|
IMutex_Unlock(phd->pmtxChunks);
|
||||||
|
if (pexn)
|
||||||
|
_HeapBaseNodeDeAlloc(phd, pexn); /* deallocate unused node, if any */
|
||||||
|
if (*pfZeroed && !fZeroed)
|
||||||
|
StrSetMem(rc, 0, sz); /* zero memory if we wanted it and don't have it */
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new chunk for heap use, either from the current "free" chunks or from the underlying
|
||||||
|
* IChunkAllocator interface.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - sz = Size of the chunk to be allocated.
|
||||||
|
* - szAlignment = Specifies the alignment that the allocated chunk should have.
|
||||||
|
* - fBase = TRUE if this is an allocation from the heap base code, FALSE if not.
|
||||||
|
* - pfZeroed = Points to a flag which indicates if the allocated chunk should be zeroed. If the actual
|
||||||
|
* chunk we find is zeroed, this gets toggled to TRUE.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - NULL = A chunk could not be found (out of memory).
|
||||||
|
* - Other = Pointer to the new chunk.
|
||||||
|
*/
|
||||||
PVOID _HeapChunkAlloc(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL fBase, BOOL *pfZeroed)
|
PVOID _HeapChunkAlloc(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL fBase, BOOL *pfZeroed)
|
||||||
{
|
{
|
||||||
return NULL; /* TODO */
|
PVOID rc = NULL; /* return from this function */
|
||||||
|
HRESULT hr; /* return from chunk allocator */
|
||||||
|
|
||||||
|
_H_ASSERT(phd, sz != 0);
|
||||||
|
_H_ASSERT(phd, (sz & phd->uiChunkSizeMask) == 0);
|
||||||
|
_H_ASSERT(phd, szAlignment != 0);
|
||||||
|
_H_ASSERT(phd, (szAlignment & phd->uiChunkSizeMask) == 0);
|
||||||
|
|
||||||
|
/* try getting a recycled chunk first */
|
||||||
|
if (!fBase) /* don't try to recycle if we're allocating on behalf of the base */
|
||||||
|
rc = chunk_recycle(phd, sz, szAlignment, pfZeroed);
|
||||||
|
if (rc)
|
||||||
|
goto returning;
|
||||||
|
|
||||||
|
/* call the chunk allocator for a new chunk */
|
||||||
|
hr = IChunkAllocator_AllocChunk(phd->pChunkAllocator, sz, szAlignment, &rc);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{ /* got it! */
|
||||||
|
if (hr != MEMMGR_S_NONZEROED)
|
||||||
|
*pfZeroed = TRUE; /* we got zeroed memory even though we didn't ask for it */
|
||||||
|
goto returning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocation completely failed */
|
||||||
|
rc = NULL;
|
||||||
|
returning:
|
||||||
|
if (rc && !fBase)
|
||||||
|
{ /* log the chunk we return in our radix tree */
|
||||||
|
if (_HeapRTreeSet(phd, phd->prtChunks, (UINT_PTR)rc, rc))
|
||||||
|
{ /* bogus! deallocate the chunk */
|
||||||
|
_HeapChunkDeAlloc(phd, rc, sz, TRUE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_H_ASSERT(phd, CHUNK_ADDR2BASE(phd, rc) == rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Record a chunk in the "free extents" list.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - pvChunk = The chunk to be recorded in the free list.
|
||||||
|
* - sz = The size of the chunk to be recorded in the free list.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
|
static void chunk_record(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz)
|
||||||
|
{
|
||||||
|
HRESULT hr; /* allocator return value */
|
||||||
|
BOOL fUnzeroed; /* TRUE if region is not zeroed */
|
||||||
|
PEXTENT_NODE pexnNew; /* new extent node */
|
||||||
|
PEXTENT_NODE pexn; /* pointer to found extent node */
|
||||||
|
PEXTENT_NODE pexnPrev; /* pointer to previous node */
|
||||||
|
EXTENT_NODE exnKey; /* key value to search for */
|
||||||
|
|
||||||
|
/* tell the allocator to purge the region */
|
||||||
|
hr = IChunkAllocator_PurgeUnusedRegion(phd->pChunkAllocator, pvChunk, sz);
|
||||||
|
fUnzeroed = MAKEBOOL(hr == MEMMGR_S_NONZEROED);
|
||||||
|
|
||||||
|
/* allocate new node which we may need */
|
||||||
|
pexnNew = _HeapBaseNodeAlloc(phd);
|
||||||
|
IMutex_Lock(phd->pmtxChunks);
|
||||||
|
|
||||||
|
/* try looking to coalesce with next node */
|
||||||
|
exnKey.pv = (PVOID)(((UINT_PTR)pvChunk) + sz);
|
||||||
|
pexn = (PEXTENT_NODE)RbtFindSuccessor(&(phd->rbtExtAddr), (TREEKEY)(&exnKey));
|
||||||
|
if (pexn && (pexn->pv == exnKey.pv))
|
||||||
|
{ /* coalesce node forward with address range */
|
||||||
|
RbtDelete(&(phd->rbtExtSizeAddr), (TREEKEY)pexn);
|
||||||
|
pexn->pv = pvChunk;
|
||||||
|
pexn->sz += sz;
|
||||||
|
pexn->fZeroed = MAKEBOOL(pexn->fZeroed && !fUnzeroed);
|
||||||
|
rbtNewNode(&(pexn->rbtnSizeAddress));
|
||||||
|
RbtInsert(&(phd->rbtExtSizeAddr), pexn);
|
||||||
|
if (pexnNew) /* didn't need new node after all */
|
||||||
|
_HeapBaseNodeDeAlloc(phd, pexnNew);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* could not coalesce forward, insert new node */
|
||||||
|
if (!pexnNew)
|
||||||
|
{ /* this is not likely but in kernel context it could be significant */
|
||||||
|
_H_ASSERT(phd, FALSE); /* record an error here */
|
||||||
|
IMutex_Unlock(phd->pmtxChunks);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pexn = pexnNew;
|
||||||
|
pexn->pv = pvChunk;
|
||||||
|
pexn->sz = sz;
|
||||||
|
pexn->fZeroed = MAKEBOOL(!fUnzeroed);
|
||||||
|
rbtNewNode(&(pexn->rbtnSizeAddress));
|
||||||
|
rbtNewNode(&(pexn->rbtnAddress));
|
||||||
|
RbtInsert(&(phd->rbtExtSizeAddr), pexn);
|
||||||
|
RbtInsert(&(phd->rbtExtAddr), pexn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to coalesce backwards now. */
|
||||||
|
pexnPrev = (PEXTENT_NODE)RbtFindPredecessor(&(phd->rbtExtAddr), (TREEKEY)pexn);
|
||||||
|
if (pexnPrev && ((PVOID)(((UINT_PTR)(pexnPrev->pv)) + pexnPrev->sz)) == pvChunk)
|
||||||
|
{ /* Coalesce chunk with previous address range. */
|
||||||
|
RbtDelete(&(phd->rbtExtSizeAddr), (TREEKEY)pexnPrev);
|
||||||
|
RbtDelete(&(phd->rbtExtAddr), (TREEKEY)pexnPrev);
|
||||||
|
|
||||||
|
RbtDelete(&(phd->rbtExtSizeAddr), (TREEKEY)pexn);
|
||||||
|
pexn->pv = pexnPrev->pv;
|
||||||
|
pexn->sz += pexnPrev->sz;
|
||||||
|
pexn->fZeroed = MAKEBOOL(pexn->fZeroed && pexnPrev->fZeroed);
|
||||||
|
rbtNewNode(&(pexn->rbtnSizeAddress));
|
||||||
|
RbtInsert(&(phd->rbtExtSizeAddr), pexn);
|
||||||
|
_HeapBaseNodeDeAlloc(phd, pexnPrev);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMutex_Unlock(phd->pmtxChunks); /* done */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "Unmap" a chunk and record it as "free."
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - pvChunk = The chunk to be unmapped.
|
||||||
|
* - sz = The size of the chunk to be unmapped.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
void _HeapChunkUnmap(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz)
|
void _HeapChunkUnmap(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz)
|
||||||
{
|
{
|
||||||
/* TODO */
|
_H_ASSERT(phd, pvChunk);
|
||||||
|
_H_ASSERT(phd, CHUNK_ADDR2BASE(phd, pvChunk) == pvChunk);
|
||||||
|
_H_ASSERT(phd, sz);
|
||||||
|
_H_ASSERT(phd, (sz && phd->uiChunkSizeMask) == 0);
|
||||||
|
chunk_record(phd, pvChunk, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deallocate a chunk of memory.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - pvChunk = The chunk to be deallocated.
|
||||||
|
* - sz = The size of the chunk to be deallocated.
|
||||||
|
* - fUnmap = If TRUE, this chunk will be unmapped.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
void _HeapChunkDeAlloc(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz, BOOL fUnmap)
|
void _HeapChunkDeAlloc(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz, BOOL fUnmap)
|
||||||
{
|
{
|
||||||
/* TODO */
|
_H_ASSERT(phd, pvChunk);
|
||||||
|
_H_ASSERT(phd, CHUNK_ADDR2BASE(phd, pvChunk) == pvChunk);
|
||||||
|
_H_ASSERT(phd, sz);
|
||||||
|
_H_ASSERT(phd, (sz && phd->uiChunkSizeMask) == 0);
|
||||||
|
|
||||||
|
_HeapRTreeSet(phd, phd->prtChunks, (UINT_PTR)pvChunk, NULL);
|
||||||
|
if (fUnmap)
|
||||||
|
_HeapChunkUnmap(phd, pvChunk, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the chunk allocation code in the heap.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Standard HRESULT success/failure indicator.
|
||||||
|
*/
|
||||||
HRESULT _HeapChunkSetup(PHEAPDATA phd)
|
HRESULT _HeapChunkSetup(PHEAPDATA phd)
|
||||||
{
|
{
|
||||||
|
HRESULT hr; /* intermediate result */
|
||||||
|
|
||||||
|
hr = IMutexFactory_CreateMutex(phd->pMutexFactory, &(phd->pmtxChunks));
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
rbtInitTree(&(phd->rbtExtSizeAddr), (PFNTREECOMPARE)compare_sizeaddr, (PFNGETTREEKEY)get_from_sizeaddr_node,
|
rbtInitTree(&(phd->rbtExtSizeAddr), (PFNTREECOMPARE)compare_sizeaddr, (PFNGETTREEKEY)get_from_sizeaddr_node,
|
||||||
(PFNGETTREENODEPTR)get_sizeaddr_node, (PFNGETFROMTREENODEPTR)get_from_sizeaddr_node);
|
(PFNGETTREENODEPTR)get_sizeaddr_node, (PFNGETFROMTREENODEPTR)get_from_sizeaddr_node);
|
||||||
rbtInitTree(&(phd->rbtExtAddr), (PFNTREECOMPARE)compare_addr, (PFNGETTREEKEY)get_from_addr_node,
|
rbtInitTree(&(phd->rbtExtAddr), (PFNTREECOMPARE)compare_addr, (PFNGETTREEKEY)get_from_addr_node,
|
||||||
(PFNGETTREENODEPTR)get_addr_node, (PFNGETFROMTREENODEPTR)get_from_addr_node);
|
(PFNGETTREENODEPTR)get_addr_node, (PFNGETFROMTREENODEPTR)get_from_addr_node);
|
||||||
|
phd->prtChunks = _HeapRTreeNew(phd, (1U << (LOG_PTRSIZE + 3)) - phd->nChunkBits);
|
||||||
|
if (!(phd->prtChunks))
|
||||||
|
{
|
||||||
|
IUnknown_Release(phd->pmtxChunks);
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shut down the chunk allocation code in the heap.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
extern void _HeapChunkShutdown(PHEAPDATA phd)
|
extern void _HeapChunkShutdown(PHEAPDATA phd)
|
||||||
{
|
{
|
||||||
/* TODO */
|
/* TODO: find chunks owned by either the extent tree or the radix tree and attempt to return them */
|
||||||
|
_HeapRTreeDestroy(phd->prtChunks);
|
||||||
|
IUnknown_Release(phd->pmtxChunks);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,33 @@
|
||||||
|
|
||||||
#ifndef __ASM__
|
#ifndef __ASM__
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
#include <comrogue/compiler_macros.h>
|
#include <comrogue/compiler_macros.h>
|
||||||
#include <comrogue/types.h>
|
#include <comrogue/types.h>
|
||||||
#include <comrogue/objectbase.h>
|
#include <comrogue/objectbase.h>
|
||||||
#include <comrogue/connpoint.h>
|
#include <comrogue/connpoint.h>
|
||||||
#include <comrogue/allocator.h>
|
#include <comrogue/allocator.h>
|
||||||
|
#include <comrogue/stream.h>
|
||||||
#include <comrogue/mutex.h>
|
#include <comrogue/mutex.h>
|
||||||
#include <comrogue/heap.h>
|
#include <comrogue/heap.h>
|
||||||
#include <comrogue/objhelp.h>
|
#include <comrogue/objhelp.h>
|
||||||
#include <comrogue/internals/rbtree.h>
|
#include <comrogue/internals/rbtree.h>
|
||||||
|
#include <comrogue/internals/seg.h>
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
* Radix tree implementation for keeping track of memory chunks
|
||||||
|
*--------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define RTREE_NODESIZE (1U << 14) /* node size for tree */
|
||||||
|
|
||||||
|
typedef struct tagMEMRTREE
|
||||||
|
{
|
||||||
|
IMutex *pmtx; /* mutex for tree */
|
||||||
|
PPVOID ppRoot; /* tree root */
|
||||||
|
UINT32 uiHeight; /* tree height */
|
||||||
|
UINT32 auiLevel2Bits[0]; /* bits at level 2 - dynamically sized */
|
||||||
|
} MEMRTREE, *PMEMRTREE;
|
||||||
|
|
||||||
/*-------------------
|
/*-------------------
|
||||||
* Extent management
|
* Extent management
|
||||||
|
@ -77,6 +95,8 @@ typedef struct tagHEAPDATA {
|
||||||
UINT32 uiRefCount; /* reference count */
|
UINT32 uiRefCount; /* reference count */
|
||||||
UINT32 uiFlags; /* flags word */
|
UINT32 uiFlags; /* flags word */
|
||||||
PFNRAWHEAPDATAFREE pfnFreeRawHeapData; /* pointer to function that frees the raw heap data, if any */
|
PFNRAWHEAPDATAFREE pfnFreeRawHeapData; /* pointer to function that frees the raw heap data, if any */
|
||||||
|
PFNHEAPABORT pfnAbort; /* pointer to abort function */
|
||||||
|
PVOID pvAbortArg; /* argument to abort function */
|
||||||
IChunkAllocator *pChunkAllocator; /* chunk allocator pointer */
|
IChunkAllocator *pChunkAllocator; /* chunk allocator pointer */
|
||||||
IMutexFactory *pMutexFactory; /* mutex factory pointer */
|
IMutexFactory *pMutexFactory; /* mutex factory pointer */
|
||||||
FIXEDCPDATA fcpMallocSpy; /* connection point for IMallocSpy */
|
FIXEDCPDATA fcpMallocSpy; /* connection point for IMallocSpy */
|
||||||
|
@ -87,8 +107,10 @@ typedef struct tagHEAPDATA {
|
||||||
UINT32 szChunk; /* size of a chunk */
|
UINT32 szChunk; /* size of a chunk */
|
||||||
UINT32 uiChunkSizeMask; /* bitmask for a chunk */
|
UINT32 uiChunkSizeMask; /* bitmask for a chunk */
|
||||||
UINT32 cpgChunk; /* number of pages in a chunk */
|
UINT32 cpgChunk; /* number of pages in a chunk */
|
||||||
|
IMutex *pmtxChunks; /* chunks mutex */
|
||||||
RBTREE rbtExtSizeAddr; /* tree ordering extents by size and address */
|
RBTREE rbtExtSizeAddr; /* tree ordering extents by size and address */
|
||||||
RBTREE rbtExtAddr; /* tree ordering extents by address */
|
RBTREE rbtExtAddr; /* tree ordering extents by address */
|
||||||
|
PMEMRTREE prtChunks; /* radix tree containing all chunk values */
|
||||||
IMutex *pmtxBase; /* base mutex */
|
IMutex *pmtxBase; /* base mutex */
|
||||||
PVOID pvBasePages; /* pages being used for internal memory allocation */
|
PVOID pvBasePages; /* pages being used for internal memory allocation */
|
||||||
PVOID pvBaseNext; /* next allocation location */
|
PVOID pvBaseNext; /* next allocation location */
|
||||||
|
@ -96,19 +118,64 @@ typedef struct tagHEAPDATA {
|
||||||
PEXTENT_NODE pexnBaseNodes; /* pointer to base nodes */
|
PEXTENT_NODE pexnBaseNodes; /* pointer to base nodes */
|
||||||
} HEAPDATA, *PHEAPDATA;
|
} HEAPDATA, *PHEAPDATA;
|
||||||
|
|
||||||
|
/*---------------------------------
|
||||||
|
* Utility and debugging functions
|
||||||
|
*---------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get nearest aligned address at or below a. */
|
||||||
|
#define ALIGNMENT_ADDR2BASE(a, alignment) ((PVOID)(((UINT_PTR)(a)) & ~(alignment)))
|
||||||
|
|
||||||
|
/* Get offset between a and ALIGNMENT_ADDR2BASE(a, alignment). */
|
||||||
|
#define ALIGNMENT_ADDR2OFFSET(a, alignment) ((SIZE_T)(((UINT_PTR)(a)) & (alignment)))
|
||||||
|
|
||||||
|
/* Returns the smallest alignment multiple greater than sz. */
|
||||||
|
#define ALIGNMENT_CEILING(sz, alignment) (((sz) + (alignment)) & ~(alignment))
|
||||||
|
|
||||||
|
CDECL_BEGIN
|
||||||
|
|
||||||
|
extern void _HeapDbgWrite(PHEAPDATA phd, PCSTR sz);
|
||||||
|
extern void _HeapPrintf(PHEAPDATA phd, PCSTR szFormat, ...);
|
||||||
|
extern void _HeapAssertFailed(PHEAPDATA phd, PCSTR szFile, INT32 nLine);
|
||||||
|
extern SIZE_T _HeapPow2Ceiling(SIZE_T x);
|
||||||
|
|
||||||
|
CDECL_END
|
||||||
|
|
||||||
|
#define _H_THIS_FILE __FILE__
|
||||||
|
#define _DECLARE_H_THIS_FILE static const char SEG_RODATA _H_THIS_FILE[] = __FILE__;
|
||||||
|
|
||||||
|
#define _H_ASSERT(phd, expr) ((expr) ? (void)0 : _HeapAssertFailed(phd, _H_THIS_FILE, __LINE__))
|
||||||
|
|
||||||
|
/*---------------------------------
|
||||||
|
* Radix tree management functions
|
||||||
|
*---------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
CDECL_BEGIN
|
||||||
|
|
||||||
|
extern PMEMRTREE _HeapRTreeNew(PHEAPDATA phd, UINT32 cBits);
|
||||||
|
extern void _HeapRTreeDestroy(PMEMRTREE prt);
|
||||||
|
extern PVOID _HeapRTreeGetLocked(PHEAPDATA phd, PMEMRTREE prt, UINT_PTR uiKey);
|
||||||
|
extern PVOID _HeapRTreeGet(PHEAPDATA phd, PMEMRTREE prt, UINT_PTR uiKey);
|
||||||
|
extern BOOL _HeapRTreeSet(PHEAPDATA phd, PMEMRTREE prt, UINT_PTR uiKey, PVOID pv);
|
||||||
|
|
||||||
|
CDECL_END
|
||||||
|
|
||||||
/*-------------------------------------
|
/*-------------------------------------
|
||||||
* Internal chunk management functions
|
* Internal chunk management functions
|
||||||
*-------------------------------------
|
*-------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Get chunk address for allocated address a. */
|
/* Get chunk address for allocated address a. */
|
||||||
#define CHUNK_ADDR2BASE(phd, a) ((PVOID)(((UINT_PTR)(a)) & ~((phd)->uiChunkSizeMask)))
|
#define CHUNK_ADDR2BASE(phd, a) ALIGNMENT_ADDR2BASE((a), (phd)->uiChunkSizeMask)
|
||||||
|
|
||||||
/* Get chunk offset of allocated address a. */
|
/* Get chunk offset of allocated address a. */
|
||||||
#define CHUNK_ADDR2OFFSET(phd, a) ((SIZE_T)(((UINT_PTR)(a)) & (phd)->uiChunkSizeMask))
|
#define CHUNK_ADDR2OFFSET(phd, a) ALIGNMENT_ADDR2OFFSET((a), (phd)->uiChunkSizeMask)
|
||||||
|
|
||||||
/* Return the smallest chunk size multiple that can contain a certain size. */
|
/* Return the smallest chunk size multiple that can contain a certain size. */
|
||||||
#define CHUNK_CEILING(phd, sz) (((sz) + (phd)->uiChunkSizeMask) & ~((phd)->uiChunkSizeMask))
|
#define CHUNK_CEILING(phd, sz) ALIGNMENT_CEILING((sz), (phd)->uiChunkSizeMask)
|
||||||
|
|
||||||
|
CDECL_BEGIN
|
||||||
|
|
||||||
extern PVOID _HeapChunkAlloc(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL fBase, BOOL *pfZeroed);
|
extern PVOID _HeapChunkAlloc(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL fBase, BOOL *pfZeroed);
|
||||||
extern void _HeapChunkUnmap(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz);
|
extern void _HeapChunkUnmap(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz);
|
||||||
|
@ -116,6 +183,8 @@ extern void _HeapChunkDeAlloc(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz, BOOL fUnm
|
||||||
extern HRESULT _HeapChunkSetup(PHEAPDATA phd);
|
extern HRESULT _HeapChunkSetup(PHEAPDATA phd);
|
||||||
extern void _HeapChunkShutdown(PHEAPDATA phd);
|
extern void _HeapChunkShutdown(PHEAPDATA phd);
|
||||||
|
|
||||||
|
CDECL_END
|
||||||
|
|
||||||
/*------------------------------------
|
/*------------------------------------
|
||||||
* Internal base management functions
|
* Internal base management functions
|
||||||
*------------------------------------
|
*------------------------------------
|
||||||
|
|
246
kernel/lib/heap_rtree.c
Normal file
246
kernel/lib/heap_rtree.c
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the COMROGUE Operating System for Raspberry Pi
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, Eric J. Bowersox / Erbosoft Enterprises
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free for commercial and non-commercial use as long as the following conditions are
|
||||||
|
* adhered to.
|
||||||
|
*
|
||||||
|
* Copyright in this file remains Eric J. Bowersox and/or Erbosoft, and as such any copyright notices
|
||||||
|
* in the code are not to be removed.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||||
|
* provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this list of conditions and
|
||||||
|
* the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
||||||
|
* the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* "Raspberry Pi" is a trademark of the Raspberry Pi Foundation.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* This code is based on/inspired by jemalloc-3.3.1. Please see LICENSE.jemalloc for further details.
|
||||||
|
*/
|
||||||
|
#include <comrogue/compiler_macros.h>
|
||||||
|
#include <comrogue/types.h>
|
||||||
|
#include <comrogue/str.h>
|
||||||
|
#include <comrogue/intlib.h>
|
||||||
|
#include <comrogue/objectbase.h>
|
||||||
|
#include <comrogue/scode.h>
|
||||||
|
#include <comrogue/stdobj.h>
|
||||||
|
#include <comrogue/mutex.h>
|
||||||
|
#include "heap_internals.h"
|
||||||
|
|
||||||
|
#ifdef _H_THIS_FILE
|
||||||
|
#undef _H_THIS_FILE
|
||||||
|
_DECLARE_H_THIS_FILE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*---------------------------------
|
||||||
|
* Radix tree management functions
|
||||||
|
*---------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new radix tree structure and its root node.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to HEAPDATA block.
|
||||||
|
* - cBits = Number of bits of key information to store in the tree.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - NULL = Allocation failed.
|
||||||
|
* - Other = Pointer to the new tree structure.
|
||||||
|
*/
|
||||||
|
PMEMRTREE _HeapRTreeNew(PHEAPDATA phd, UINT32 cBits)
|
||||||
|
{
|
||||||
|
PMEMRTREE rc; /* return from this function */
|
||||||
|
UINT32 cBitsPerLevel; /* number of bits per level of the tree */
|
||||||
|
UINT32 uiHeight; /* tree height */
|
||||||
|
SIZE_T cb; /* number of bytes for allocation */
|
||||||
|
register UINT32 i; /* loop counter */
|
||||||
|
|
||||||
|
/* Compute the number of bits per level and the height of the tree. */
|
||||||
|
cBitsPerLevel = IntFirstSet(_HeapPow2Ceiling(RTREE_NODESIZE / sizeof(PVOID))) - 1;
|
||||||
|
uiHeight = cBits / cBitsPerLevel;
|
||||||
|
if ((uiHeight * cBitsPerLevel) != cBits)
|
||||||
|
uiHeight++;
|
||||||
|
_H_ASSERT(phd, (uiHeight * cBitsPerLevel) >= cBits);
|
||||||
|
|
||||||
|
/* Allocate the tree node. */
|
||||||
|
cb = sizeof(MEMRTREE) + (sizeof(UINT32) * uiHeight);
|
||||||
|
rc = (PMEMRTREE)_HeapBaseAlloc(phd, cb);
|
||||||
|
if (!rc)
|
||||||
|
return NULL;
|
||||||
|
StrSetMem(rc, 0, cb);
|
||||||
|
|
||||||
|
/* Allocate the mutex and fill the tree data. */
|
||||||
|
if (FAILED(IMutexFactory_CreateMutex(phd->pMutexFactory, &(rc->pmtx))))
|
||||||
|
return NULL; /* just leak the allocation */
|
||||||
|
rc->uiHeight = uiHeight;
|
||||||
|
if ((uiHeight * cBitsPerLevel) > cBits)
|
||||||
|
rc->auiLevel2Bits[0] = cBits % cBitsPerLevel;
|
||||||
|
else
|
||||||
|
rc->auiLevel2Bits[0] = cBitsPerLevel;
|
||||||
|
for (i = 1; i < uiHeight; i++)
|
||||||
|
rc->auiLevel2Bits[i] = cBitsPerLevel;
|
||||||
|
|
||||||
|
/* Allocate the tree root data. */
|
||||||
|
cb = sizeof(PVOID) << rc->auiLevel2Bits[0];
|
||||||
|
rc->ppRoot = (PPVOID)_HeapBaseAlloc(phd, cb);
|
||||||
|
if (!(rc->ppRoot))
|
||||||
|
{ /* failed allocation, just leak the base node */
|
||||||
|
IUnknown_Release(rc->pmtx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
StrSetMem(rc->ppRoot, 0, cb);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destroy a radix tree data structure.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - prt = Pointer to the radix tree structure to be destroyed.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
|
void _HeapRTreeDestroy(PMEMRTREE prt)
|
||||||
|
{
|
||||||
|
IUnknown_Release(prt->pmtx); /* destroy the mutex */
|
||||||
|
/* don't need to destroy anything else, it will get killed with the base allocations */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve a value from the radix tree. (Can either do it with the tree locked or not.)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to HEAPDATA block.
|
||||||
|
* - prt = Pointer to the radix tree structure to search.
|
||||||
|
* - uiKey = Key value to look for in the tree.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - NULL = Key value not found.
|
||||||
|
* - Other = Pointer value stashed in the tree under that key value.
|
||||||
|
*/
|
||||||
|
#define GENERATE_GET_FUNC(name) \
|
||||||
|
PVOID name (PHEAPDATA phd, PMEMRTREE prt, UINT_PTR uiKey) \
|
||||||
|
{ \
|
||||||
|
PVOID rc; /* return from this function */ \
|
||||||
|
UINT_PTR uiSubkey; /* subkey value */ \
|
||||||
|
UINT32 i; /* loop counter */ \
|
||||||
|
UINT32 nLeftShift; /* number of bits to shift key left */ \
|
||||||
|
UINT32 uiHeight; /* tree height value */ \
|
||||||
|
UINT32 cBits; /* number of bits at this level */ \
|
||||||
|
PPVOID ppNode; /* pointer to current node */ \
|
||||||
|
PPVOID ppChild; /* pointer to child node */ \
|
||||||
|
\
|
||||||
|
DO_LOCK(prt->pmtx); \
|
||||||
|
/* Walk the tree to find the right leaf node */ \
|
||||||
|
for (i = nLeftShift = 0, uiHeight = prt->uiHeight, ppNode = prt->ppRoot; \
|
||||||
|
i < (uiHeight - 1); \
|
||||||
|
i++, nLeftShift += cBits, ppNode = ppChild) \
|
||||||
|
{ \
|
||||||
|
cBits = prt->auiLevel2Bits[i]; \
|
||||||
|
uiSubkey = (uiKey << nLeftShift) >> ((1U << (LOG_PTRSIZE + 3)) - cBits); \
|
||||||
|
ppChild = (PPVOID)(ppNode[uiSubkey]); \
|
||||||
|
if (!ppChild) \
|
||||||
|
{ \
|
||||||
|
DO_UNLOCK(prt->pmtx); \
|
||||||
|
return NULL; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* this is a leaf node, it contains values, not pointers */ \
|
||||||
|
cBits = prt->auiLevel2Bits[i]; \
|
||||||
|
uiSubkey = (uiKey << nLeftShift) >> ((1U << (LOG_PTRSIZE + 3)) - cBits); \
|
||||||
|
rc = ppNode[uiSubkey]; \
|
||||||
|
DO_UNLOCK(prt->pmtx); \
|
||||||
|
DO_GET_VALIDATE \
|
||||||
|
return rc; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate locked version of function */
|
||||||
|
#define DO_LOCK(mtx) IMutex_Lock(mtx)
|
||||||
|
#define DO_UNLOCK(mtx) IMutex_Unlock(mtx)
|
||||||
|
#define DO_GET_VALIDATE
|
||||||
|
GENERATE_GET_FUNC(_HeapRTreeGetLocked)
|
||||||
|
#undef DO_LOCK
|
||||||
|
#undef DO_UNLOCK
|
||||||
|
#undef DO_GET_VALIDATE
|
||||||
|
|
||||||
|
/* Generate unlocked version of function */
|
||||||
|
#define DO_LOCK(mtx)
|
||||||
|
#define DO_UNLOCK(mtx)
|
||||||
|
#define DO_GET_VALIDATE _H_ASSERT(phd, _HeapRTreeGetLocked(phd, prt, uiKey) == rc);
|
||||||
|
GENERATE_GET_FUNC(_HeapRTreeGet)
|
||||||
|
#undef DO_LOCK
|
||||||
|
#undef DO_UNLOCK
|
||||||
|
#undef DO_GET_VALIDATE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets a pointer value into the radix tree under a specified key.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to HEAPDATA block.
|
||||||
|
* - prt = Pointer to the radix tree structure to set.
|
||||||
|
* - uiKey = Key value to set in the tree.
|
||||||
|
* - pv = Pointer value to set in the tree.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - TRUE = Allocation failed, tree value not set.
|
||||||
|
* - FALSE = Tree value set.
|
||||||
|
*/
|
||||||
|
BOOL _HeapRTreeSet(PHEAPDATA phd, PMEMRTREE prt, UINT_PTR uiKey, PVOID pv)
|
||||||
|
{
|
||||||
|
UINT_PTR uiSubkey; /* subkey value */
|
||||||
|
UINT32 i; /* loop counter */
|
||||||
|
UINT32 nLeftShift; /* number of bits to shift key left */
|
||||||
|
UINT32 uiHeight; /* tree height value */
|
||||||
|
UINT32 cBits; /* number of bits at this level */
|
||||||
|
PPVOID ppNode; /* pointer to current node */
|
||||||
|
PPVOID ppChild; /* pointer to child node */
|
||||||
|
SIZE_T cb; /* number of bytes to allocate */
|
||||||
|
|
||||||
|
IMutex_Lock(prt->pmtx);
|
||||||
|
/* Walk the tree to find the right leaf node (creating where applicable) */
|
||||||
|
for (i = nLeftShift = 0, uiHeight = prt->uiHeight, ppNode = prt->ppRoot;
|
||||||
|
i < (uiHeight - 1);
|
||||||
|
i++, nLeftShift += cBits, ppNode = ppChild)
|
||||||
|
{
|
||||||
|
cBits = prt->auiLevel2Bits[i];
|
||||||
|
uiSubkey = (uiKey << nLeftShift) >> ((1U << (LOG_PTRSIZE + 3)) - cBits);
|
||||||
|
ppChild = (PPVOID)(ppNode[uiSubkey]);
|
||||||
|
if (!ppChild)
|
||||||
|
{ /* allocate new node for next level */
|
||||||
|
cb = sizeof(PVOID) << prt->auiLevel2Bits[i + 1];
|
||||||
|
ppChild = (PPVOID)_HeapBaseAlloc(phd, cb);
|
||||||
|
if (!ppChild)
|
||||||
|
{ /* allocation failed, bug out */
|
||||||
|
IMutex_Unlock(prt->pmtx);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
StrSetMem(ppChild, 0, cb);
|
||||||
|
ppNode[uiSubkey] = (PVOID)ppChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is a leaf node, it contains values, not pointers */
|
||||||
|
cBits = prt->auiLevel2Bits[i];
|
||||||
|
uiSubkey = (uiKey << nLeftShift) >> ((1U << (LOG_PTRSIZE + 3)) - cBits);
|
||||||
|
ppNode[uiSubkey] = pv;
|
||||||
|
IMutex_Unlock(prt->pmtx);
|
||||||
|
return FALSE;
|
||||||
|
}
|
|
@ -131,10 +131,12 @@ static UINT32 malloc_Release(IUnknown *pThis)
|
||||||
if ((rc == 0) && !(phd->uiFlags & PHDFLAGS_DELETING))
|
if ((rc == 0) && !(phd->uiFlags & PHDFLAGS_DELETING))
|
||||||
{
|
{
|
||||||
phd->uiFlags |= PHDFLAGS_DELETING;
|
phd->uiFlags |= PHDFLAGS_DELETING;
|
||||||
|
/* Do subsystem shutdown. */
|
||||||
|
_HeapChunkShutdown(phd);
|
||||||
_HeapBaseShutdown(phd);
|
_HeapBaseShutdown(phd);
|
||||||
toplevel_shutdown(phd);
|
toplevel_shutdown(phd);
|
||||||
if (phd->pfnFreeRawHeapData)
|
if (phd->pfnFreeRawHeapData)
|
||||||
{
|
{ /* free the raw heap data */
|
||||||
pfnFree = phd->pfnFreeRawHeapData;
|
pfnFree = phd->pfnFreeRawHeapData;
|
||||||
(*pfnFree)((PRAWHEAPDATA)phd);
|
(*pfnFree)((PRAWHEAPDATA)phd);
|
||||||
}
|
}
|
||||||
|
@ -453,6 +455,8 @@ static const SEG_RODATA struct IConnectionPointContainerVTable vtblConnectionPoi
|
||||||
* for the heap.
|
* for the heap.
|
||||||
* - pfnFree = Pointer to a function called as the last stage of releasing the heap, which frees the
|
* - pfnFree = Pointer to a function called as the last stage of releasing the heap, which frees the
|
||||||
* "prhd" block. May be NULL.
|
* "prhd" block. May be NULL.
|
||||||
|
* - pfnAbort = Pointer to a function called when there's a serious error in the heap. May be NULL.
|
||||||
|
* - pvAbortArg = Argument passed to the pfnAbort function when it's called. May be NULL.
|
||||||
* - pChunkAllocator = Pointer to the IChunkAllocator interface used by the heap to allocate chunks of memory
|
* - pChunkAllocator = Pointer to the IChunkAllocator interface used by the heap to allocate chunks of memory
|
||||||
* for carving up by the heap.
|
* for carving up by the heap.
|
||||||
* - pMutexFactory = Pointer to the IMutexFactory interface used to allocate IMutex objects.
|
* - pMutexFactory = Pointer to the IMutexFactory interface used to allocate IMutex objects.
|
||||||
|
@ -462,8 +466,9 @@ static const SEG_RODATA struct IConnectionPointContainerVTable vtblConnectionPoi
|
||||||
* Returns:
|
* Returns:
|
||||||
* Standard HRESULT success/failure.
|
* Standard HRESULT success/failure.
|
||||||
*/
|
*/
|
||||||
HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocator *pChunkAllocator,
|
HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, PFNHEAPABORT pfnAbort, PVOID pvAbortArg,
|
||||||
IMutexFactory *pMutexFactory, UINT32 nChunkBits, IMalloc **ppHeap)
|
IChunkAllocator *pChunkAllocator, IMutexFactory *pMutexFactory, UINT32 nChunkBits,
|
||||||
|
IMalloc **ppHeap)
|
||||||
{
|
{
|
||||||
PHEAPDATA phd; /* pointer to actual heap data */
|
PHEAPDATA phd; /* pointer to actual heap data */
|
||||||
HRESULT hr; /* HRESULT of intermediate operations */
|
HRESULT hr; /* HRESULT of intermediate operations */
|
||||||
|
@ -481,6 +486,8 @@ HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocato
|
||||||
phd->uiRefCount = 1;
|
phd->uiRefCount = 1;
|
||||||
phd->uiFlags = 0;
|
phd->uiFlags = 0;
|
||||||
phd->pfnFreeRawHeapData = pfnFree;
|
phd->pfnFreeRawHeapData = pfnFree;
|
||||||
|
phd->pfnAbort = pfnAbort;
|
||||||
|
phd->pvAbortArg = pvAbortArg;
|
||||||
phd->nChunkBits = nChunkBits;
|
phd->nChunkBits = nChunkBits;
|
||||||
phd->szChunk = 1 << nChunkBits;
|
phd->szChunk = 1 << nChunkBits;
|
||||||
if (phd->szChunk < SYS_PAGE_SIZE)
|
if (phd->szChunk < SYS_PAGE_SIZE)
|
||||||
|
@ -497,10 +504,13 @@ HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocato
|
||||||
ObjHlpFixedCpSetup(&(phd->fcpSequentialStream), (PUNKNOWN)phd, &IID_ISequentialStream,
|
ObjHlpFixedCpSetup(&(phd->fcpSequentialStream), (PUNKNOWN)phd, &IID_ISequentialStream,
|
||||||
(IUnknown **)(&(phd->pDebugStream)), 1, NULL);
|
(IUnknown **)(&(phd->pDebugStream)), 1, NULL);
|
||||||
|
|
||||||
/* Setup base pointers. */
|
/* Setup all the various "subsystems." */
|
||||||
hr = _HeapBaseSetup(phd);
|
hr = _HeapBaseSetup(phd);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
goto error0;
|
goto error0;
|
||||||
|
hr = _HeapChunkSetup(phd);
|
||||||
|
if (FAILED(hr))
|
||||||
|
goto error1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -508,7 +518,9 @@ HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocato
|
||||||
*ppHeap = (IMalloc *)phd;
|
*ppHeap = (IMalloc *)phd;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
/*error1:*/
|
/*error2:*/
|
||||||
|
_HeapChunkShutdown(phd);
|
||||||
|
error1:
|
||||||
_HeapBaseShutdown(phd);
|
_HeapBaseShutdown(phd);
|
||||||
error0:
|
error0:
|
||||||
toplevel_shutdown(phd);
|
toplevel_shutdown(phd);
|
||||||
|
|
148
kernel/lib/heap_utils.c
Normal file
148
kernel/lib/heap_utils.c
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the COMROGUE Operating System for Raspberry Pi
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, Eric J. Bowersox / Erbosoft Enterprises
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free for commercial and non-commercial use as long as the following conditions are
|
||||||
|
* adhered to.
|
||||||
|
*
|
||||||
|
* Copyright in this file remains Eric J. Bowersox and/or Erbosoft, and as such any copyright notices
|
||||||
|
* in the code are not to be removed.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||||
|
* provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this list of conditions and
|
||||||
|
* the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
||||||
|
* the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* "Raspberry Pi" is a trademark of the Raspberry Pi Foundation.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* This code is based on/inspired by jemalloc-3.3.1. Please see LICENSE.jemalloc for further details.
|
||||||
|
*/
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <comrogue/compiler_macros.h>
|
||||||
|
#include <comrogue/types.h>
|
||||||
|
#include <comrogue/str.h>
|
||||||
|
#include <comrogue/objectbase.h>
|
||||||
|
#include <comrogue/stdobj.h>
|
||||||
|
#include <comrogue/stream.h>
|
||||||
|
#include <comrogue/internals/seg.h>
|
||||||
|
#include "heap_internals.h"
|
||||||
|
|
||||||
|
#ifdef _H_THIS_FILE
|
||||||
|
#undef _H_THIS_FILE
|
||||||
|
_DECLARE_H_THIS_FILE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*---------------------------------
|
||||||
|
* Utility and debugging functions
|
||||||
|
*---------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a string to debugging output (if it's been configured).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - sz = String to be written.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
|
void _HeapDbgWrite(PHEAPDATA phd, PCSTR sz)
|
||||||
|
{
|
||||||
|
if (phd->pDebugStream)
|
||||||
|
ISequentialStream_Write(phd->pDebugStream, sz, StrLength8(sz), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal function called to write formatted output to the debugging output stream.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - ppvArg = Pointer argument to StrFormatV8 (actually the ISequentialStream pointer).
|
||||||
|
* - pchData = Pointer to data to be written.
|
||||||
|
* - cbData = Number of characters to be written.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Standard HRESULT success/failure indicator.
|
||||||
|
*/
|
||||||
|
static HRESULT heap_printf_func(PPVOID ppvArg, PCCHAR pchData, UINT32 cbData)
|
||||||
|
{
|
||||||
|
return ISequentialStream_Write(((PSEQUENTIALSTREAM)ppvArg), pchData, cbData, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Formats data to the debugging output (if it's been configured).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - szFormat = Printf-style format string.
|
||||||
|
* - <var> = Arguments to be substituted into the printf-style format string.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
|
void _HeapPrintf(PHEAPDATA phd, PCSTR szFormat, ...)
|
||||||
|
{
|
||||||
|
va_list pargs;
|
||||||
|
|
||||||
|
if (phd->pDebugStream)
|
||||||
|
{
|
||||||
|
va_start(pargs, szFormat);
|
||||||
|
StrFormatV8(heap_printf_func, (PPVOID)(phd->pDebugStream), szFormat, pargs);
|
||||||
|
va_end(pargs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when an assertion in the code fails; it prints the assertion failure to the debugging output (if set)
|
||||||
|
* and calls the heap's abort function (if set).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - phd = Pointer to the HEAPDATA block.
|
||||||
|
* - szFile = File name where the assertion failed.
|
||||||
|
* - szLine = Line number where the assertion failed.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Nothing.
|
||||||
|
*/
|
||||||
|
void _HeapAssertFailed(PHEAPDATA phd, PCSTR szFile, INT32 nLine)
|
||||||
|
{
|
||||||
|
static const char SEG_RODATA szMessage[] = "_HeapAssertFailed at %s:%d\n";
|
||||||
|
_HeapPrintf(phd, szMessage, szFile, nLine);
|
||||||
|
if (phd->pfnAbort)
|
||||||
|
(*(phd->pfnAbort))(phd->pvAbortArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the smallest power of 2 greater than or equal to its argument.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - x = The value to compute.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The value that is the smallest power of 2 greater than or equal to x.
|
||||||
|
*/
|
||||||
|
SIZE_T _HeapPow2Ceiling(SIZE_T x)
|
||||||
|
{
|
||||||
|
x--;
|
||||||
|
x |= (x >> 1); /* this sequence sets all bits at or below the topmost one to 1 */
|
||||||
|
x |= (x >> 2);
|
||||||
|
x |= (x >> 4);
|
||||||
|
x |= (x >> 8);
|
||||||
|
x |= (x >> 16);
|
||||||
|
return ++x; /* which ensures that this is a power of 2 */
|
||||||
|
}
|
|
@ -78,3 +78,27 @@ HRESULT IntLDiv(PLDIV pResult, INT64 num, INT64 denom)
|
||||||
pResult->rem = -(pResult->rem);
|
pResult->rem = -(pResult->rem);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the index of the first bit set in the specified mask, where the least-significant bit is considered
|
||||||
|
* bit #1.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* - nMask = The mask to be tested.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - 0 = If nMask is 0.
|
||||||
|
* - Other = Index of the first set bit in nMask.
|
||||||
|
*/
|
||||||
|
INT32 IntFirstSet(INT32 nMask)
|
||||||
|
{
|
||||||
|
register INT32 nBit; /* bit index to return */
|
||||||
|
if (nMask)
|
||||||
|
{
|
||||||
|
for (nBit = 1; !(nMask & 1); nBit++)
|
||||||
|
nMask = ((UINT32)nMask) >> 1;
|
||||||
|
return nBit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0; /* corner case */
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user