kernel/util: Merge the bootloader and runtime_loader heap implementations.
They were mostly copies of one another, save for the glue code and a few other things. Now they're mostly unified, and this allows the test to be greatly simplified, too, since it can avoid including any bootloader code at all. The heap implementation itself should have no behavioral changes from before. Those will come in future commits.
This commit is contained in:
@@ -0,0 +1,379 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2013, Axel Dörfler, [email protected].
|
||||||
|
* Copyright 2005-2013, Ingo Weinhold, [email protected].
|
||||||
|
* Copyright 2025, Haiku, Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _SIMPLE_ALLOCATOR_H
|
||||||
|
#define _SIMPLE_ALLOCATOR_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
#include <util/SplayTree.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*! This is a very simple malloc()/free() implementation - it only
|
||||||
|
manages a free list using a splay tree.
|
||||||
|
|
||||||
|
After heap_init() is called, all free memory is contained in one
|
||||||
|
big chunk, the only entry in the free chunk tree.
|
||||||
|
|
||||||
|
When memory is allocated, the smallest free chunk that contains
|
||||||
|
the requested size is split (or taken as a whole if it can't be
|
||||||
|
splitted anymore), and its lower half will be removed from the
|
||||||
|
free list.
|
||||||
|
|
||||||
|
The free list is ordered by size, starting with the smallest
|
||||||
|
free chunk available. When a chunk is freed, it will be joined
|
||||||
|
with its predecessor or successor, if possible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<uint32 Alignment = 8>
|
||||||
|
class SimpleAllocator {
|
||||||
|
class Chunk {
|
||||||
|
public:
|
||||||
|
size_t CompleteSize() const
|
||||||
|
{
|
||||||
|
return fSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
union {
|
||||||
|
uint32 fSize;
|
||||||
|
char fAlignment[Alignment];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FreeChunk;
|
||||||
|
|
||||||
|
struct FreeChunkData : SplayTreeLink<FreeChunk> {
|
||||||
|
|
||||||
|
FreeChunk* Next() const
|
||||||
|
{
|
||||||
|
return fNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeChunk** NextLink()
|
||||||
|
{
|
||||||
|
return &fNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FreeChunk* fNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FreeChunk : public Chunk, public FreeChunkData {
|
||||||
|
public:
|
||||||
|
void SetTo(size_t size)
|
||||||
|
{
|
||||||
|
Chunk::fSize = size;
|
||||||
|
FreeChunkData::fNext = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the amount of bytes that can be allocated
|
||||||
|
in this chunk.
|
||||||
|
*/
|
||||||
|
size_t Size() const
|
||||||
|
{
|
||||||
|
return (addr_t)this + Chunk::fSize - (addr_t)AllocatedAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Splits the upper half at the requested location and returns it. This chunk
|
||||||
|
will no longer be a valid FreeChunk object; only its fSize will be valid.
|
||||||
|
*/
|
||||||
|
FreeChunk* Split(size_t splitSize)
|
||||||
|
{
|
||||||
|
splitSize = Align(splitSize);
|
||||||
|
|
||||||
|
FreeChunk* chunk = (FreeChunk*)((addr_t)AllocatedAddress() + splitSize);
|
||||||
|
size_t newSize = (addr_t)chunk - (addr_t)this;
|
||||||
|
chunk->fSize = Chunk::fSize - newSize;
|
||||||
|
chunk->fNext = NULL;
|
||||||
|
|
||||||
|
Chunk::fSize = newSize;
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Checks if the specified chunk touches this chunk, so
|
||||||
|
that they could be joined.
|
||||||
|
*/
|
||||||
|
bool IsTouching(FreeChunk* chunk)
|
||||||
|
{
|
||||||
|
return chunk
|
||||||
|
&& (((uint8*)this + Chunk::fSize == (uint8*)chunk)
|
||||||
|
|| (uint8*)chunk + chunk->fSize == (uint8*)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Joins the chunk to this chunk and returns the pointer
|
||||||
|
to the new chunk - which will either be one of the
|
||||||
|
two chunks.
|
||||||
|
Note, the chunks must be joinable, or else this method
|
||||||
|
doesn't work correctly. Use FreeChunk::IsTouching()
|
||||||
|
to check if this method can be applied.
|
||||||
|
*/
|
||||||
|
FreeChunk* Join(FreeChunk* chunk)
|
||||||
|
{
|
||||||
|
if (chunk < this) {
|
||||||
|
chunk->fSize += Chunk::fSize;
|
||||||
|
chunk->fNext = FreeChunkData::fNext;
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk::fSize += chunk->fSize;
|
||||||
|
FreeChunkData::fNext = chunk->fNext;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* AllocatedAddress() const
|
||||||
|
{
|
||||||
|
return (void*)static_cast<const FreeChunkData*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FreeChunk* SetToAllocated(void* allocated)
|
||||||
|
{
|
||||||
|
return static_cast<FreeChunk*>((FreeChunkData*)allocated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FreeChunkKey {
|
||||||
|
FreeChunkKey(size_t size)
|
||||||
|
:
|
||||||
|
fSize(size),
|
||||||
|
fChunk(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeChunkKey(const FreeChunk* chunk)
|
||||||
|
:
|
||||||
|
fSize(chunk->Size()),
|
||||||
|
fChunk(chunk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int Compare(const FreeChunk* chunk) const
|
||||||
|
{
|
||||||
|
size_t chunkSize = chunk->Size();
|
||||||
|
if (chunkSize != fSize)
|
||||||
|
return fSize < chunkSize ? -1 : 1;
|
||||||
|
|
||||||
|
if (fChunk == chunk)
|
||||||
|
return 0;
|
||||||
|
return fChunk < chunk ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t fSize;
|
||||||
|
const FreeChunk* fChunk;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FreeChunkTreeDefinition {
|
||||||
|
typedef FreeChunkKey KeyType;
|
||||||
|
typedef FreeChunk NodeType;
|
||||||
|
|
||||||
|
static FreeChunkKey GetKey(const FreeChunk* node)
|
||||||
|
{
|
||||||
|
return FreeChunkKey(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SplayTreeLink<FreeChunk>* GetLink(FreeChunk* node)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Compare(const FreeChunkKey& key, const FreeChunk* node)
|
||||||
|
{
|
||||||
|
return key.Compare(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FreeChunk** GetListLink(FreeChunk* node)
|
||||||
|
{
|
||||||
|
return node->NextLink();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef IteratableSplayTree<FreeChunkTreeDefinition> FreeChunkTree;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static inline size_t Align(size_t size, size_t alignment = Alignment)
|
||||||
|
{
|
||||||
|
return (size + alignment - 1) & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
SimpleAllocator()
|
||||||
|
:
|
||||||
|
fAvailable(0)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
fMaxHeapSize = fMaxHeapUsage = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~SimpleAllocator()
|
||||||
|
{
|
||||||
|
// Releasing memory is the caller's responsibility.
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddChunk(void* base, uint32 size)
|
||||||
|
{
|
||||||
|
FreeChunk* chunk = (FreeChunk*)base;
|
||||||
|
chunk->SetTo(size);
|
||||||
|
fFreeChunkTree.Insert(chunk);
|
||||||
|
|
||||||
|
fAvailable += chunk->Size();
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
fMaxHeapSize += chunk->Size();
|
||||||
|
fMaxHeapUsage = fMaxHeapSize - fAvailable;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 Available() const { return fAvailable; }
|
||||||
|
|
||||||
|
void* Allocate(uint32 size)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// align the size requirement to an Alignment bytes boundary
|
||||||
|
if (size < sizeof(FreeChunkData))
|
||||||
|
size = sizeof(FreeChunkData);
|
||||||
|
size = Align(size);
|
||||||
|
|
||||||
|
if (size > fAvailable)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FreeChunk* chunk = fFreeChunkTree.FindClosest(FreeChunkKey(size), true, true);
|
||||||
|
if (chunk == NULL) {
|
||||||
|
// could not find a free chunk as large as needed
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fFreeChunkTree.Remove(chunk);
|
||||||
|
fAvailable -= chunk->Size();
|
||||||
|
|
||||||
|
void* allocated = chunk->AllocatedAddress();
|
||||||
|
|
||||||
|
// If this chunk is bigger than the requested size and there's enough space
|
||||||
|
// left over for a new chunk, we split it.
|
||||||
|
if (chunk->Size() >= (size + Align(sizeof(FreeChunk)))) {
|
||||||
|
FreeChunk* freeChunk = chunk->Split(size);
|
||||||
|
fFreeChunkTree.Insert(freeChunk);
|
||||||
|
fAvailable += freeChunk->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
fMaxHeapUsage = std::max(fMaxHeapUsage, fMaxHeapSize - fAvailable);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return allocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 UsableSize(void* allocated)
|
||||||
|
{
|
||||||
|
FreeChunk* chunk = FreeChunk::SetToAllocated(allocated);
|
||||||
|
return chunk->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Reallocate(void* oldBuffer, uint32 newSize)
|
||||||
|
{
|
||||||
|
size_t oldSize = 0;
|
||||||
|
if (oldBuffer != NULL) {
|
||||||
|
oldSize = UsableSize(oldBuffer);
|
||||||
|
|
||||||
|
// Check if the old buffer still fits, and if it makes sense to keep it.
|
||||||
|
if (oldSize >= newSize && (oldSize < 128 || newSize > (oldSize / 3)))
|
||||||
|
return oldBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* newBuffer = Allocate(newSize);
|
||||||
|
if (newBuffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (oldBuffer != NULL) {
|
||||||
|
memcpy(newBuffer, oldBuffer, (oldSize < newSize) ? oldSize : newSize);
|
||||||
|
Free(oldBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Free(void* allocated)
|
||||||
|
{
|
||||||
|
if (allocated == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FreeChunk* freedChunk = FreeChunk::SetToAllocated(allocated);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ALLOCATIONS
|
||||||
|
if (freedChunk->Size() > (fMaxHeapSize - fAvailable)) {
|
||||||
|
panic("freed chunk %p clobbered (%#zx)!\n", freedChunk,
|
||||||
|
freedChunk->Size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
||||||
|
while (chunk) {
|
||||||
|
if (chunk->Size() > fAvailable || freedChunk == chunk)
|
||||||
|
panic("invalid chunk in free list (%p (%zu)), or double free\n",
|
||||||
|
chunk, chunk->Size());
|
||||||
|
chunk = chunk->Next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// try to join the new free chunk with an existing one
|
||||||
|
// it may be joined with up to two chunks
|
||||||
|
|
||||||
|
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
||||||
|
int32 joinCount = 0;
|
||||||
|
|
||||||
|
while (chunk) {
|
||||||
|
FreeChunk* nextChunk = chunk->Next();
|
||||||
|
|
||||||
|
if (chunk->IsTouching(freedChunk)) {
|
||||||
|
fFreeChunkTree.Remove(chunk);
|
||||||
|
fAvailable -= chunk->Size();
|
||||||
|
|
||||||
|
freedChunk = chunk->Join(freedChunk);
|
||||||
|
|
||||||
|
if (++joinCount == 2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = nextChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
fFreeChunkTree.Insert(freedChunk);
|
||||||
|
fAvailable += freedChunk->Size();
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
fMaxHeapUsage = std::max(fMaxHeapUsage, fMaxHeapSize - fAvailable);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
uint32 MaxHeapSize() const { return fMaxHeapSize; }
|
||||||
|
uint32 MaxHeapUsage() const { return fMaxHeapUsage; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void DumpChunks()
|
||||||
|
{
|
||||||
|
FreeChunk* chunk = fFreeChunkTree.FindMin();
|
||||||
|
while (chunk != NULL) {
|
||||||
|
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
|
||||||
|
chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
|
||||||
|
chunk->Next());
|
||||||
|
chunk = chunk->Next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FreeChunkTree fFreeChunkTree;
|
||||||
|
uint32 fAvailable;
|
||||||
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
|
uint32 fMaxHeapSize, fMaxHeapUsage;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _SIMPLE_ALLOCATOR_H */
|
||||||
+29
-353
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2013, Axel Dörfler, [email protected].
|
* Copyright 2003-2013, Axel Dörfler, [email protected].
|
||||||
|
* Copyright 2005-2013, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,17 +14,15 @@
|
|||||||
|
|
||||||
#include <boot/platform.h>
|
#include <boot/platform.h>
|
||||||
#include <util/OpenHashTable.h>
|
#include <util/OpenHashTable.h>
|
||||||
#include <util/SplayTree.h>
|
|
||||||
|
|
||||||
#ifdef HEAP_TEST
|
|
||||||
#include <stdio.h>
|
#define DEBUG_ALLOCATIONS
|
||||||
#define dprintf printf
|
// if defined, freed memory is filled with 0xcc
|
||||||
#define malloc heap_malloc
|
#define DEBUG_MAX_HEAP_USAGE
|
||||||
#define free heap_free
|
// if defined, the maximum heap usage is determined and printed before
|
||||||
#define realloc heap_realloc
|
// entering the kernel
|
||||||
void panic(const char* format, ...);
|
|
||||||
void free(void*);
|
#include <util/SimpleAllocator.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_HEAP
|
//#define TRACE_HEAP
|
||||||
@@ -34,29 +33,6 @@ void free(void*);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*! This is a very simple malloc()/free() implementation - it only
|
|
||||||
manages a free list.
|
|
||||||
After heap_init() is called, all free memory is contained in one
|
|
||||||
big chunk, the only entry in the free link list (which is a single
|
|
||||||
linked list).
|
|
||||||
When memory is allocated, the smallest free chunk that contains
|
|
||||||
the requested size is split (or taken as a whole if it can't be
|
|
||||||
splitted anymore), and it's lower half will be removed from the
|
|
||||||
free list.
|
|
||||||
The free list is ordered by size, starting with the smallest
|
|
||||||
free chunk available. When a chunk is freed, it will be joint
|
|
||||||
with its predecessor or successor, if possible.
|
|
||||||
To ease list handling, the list anchor itself is a free chunk with
|
|
||||||
size 0 that can't be allocated.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define DEBUG_ALLOCATIONS
|
|
||||||
// if defined, freed memory is filled with 0xcc
|
|
||||||
#define DEBUG_MAX_HEAP_USAGE
|
|
||||||
// if defined, the maximum heap usage is determined and printed before
|
|
||||||
// entering the kernel
|
|
||||||
|
|
||||||
|
|
||||||
const static size_t kAlignment = 8;
|
const static size_t kAlignment = 8;
|
||||||
// all memory chunks will be a multiple of this
|
// all memory chunks will be a multiple of this
|
||||||
|
|
||||||
@@ -66,117 +42,6 @@ const static size_t kLargeAllocationThreshold = 128 * 1024;
|
|||||||
// allocations of this size or larger are allocated separately
|
// allocations of this size or larger are allocated separately
|
||||||
|
|
||||||
|
|
||||||
class Chunk {
|
|
||||||
public:
|
|
||||||
size_t CompleteSize() const
|
|
||||||
{
|
|
||||||
return fSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
union {
|
|
||||||
size_t fSize;
|
|
||||||
char fAlignment[kAlignment];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class FreeChunk;
|
|
||||||
|
|
||||||
|
|
||||||
struct FreeChunkData : SplayTreeLink<FreeChunk> {
|
|
||||||
|
|
||||||
FreeChunk* Next() const
|
|
||||||
{
|
|
||||||
return fNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeChunk** NextLink()
|
|
||||||
{
|
|
||||||
return &fNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
FreeChunk* fNext;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class FreeChunk : public Chunk, public FreeChunkData {
|
|
||||||
public:
|
|
||||||
void SetTo(size_t size);
|
|
||||||
|
|
||||||
size_t Size() const;
|
|
||||||
|
|
||||||
FreeChunk* Split(size_t splitSize);
|
|
||||||
bool IsTouching(FreeChunk* link);
|
|
||||||
FreeChunk* Join(FreeChunk* link);
|
|
||||||
|
|
||||||
void* AllocatedAddress() const;
|
|
||||||
static FreeChunk* SetToAllocated(void* allocated);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FreeChunkKey {
|
|
||||||
FreeChunkKey(size_t size)
|
|
||||||
:
|
|
||||||
fSize(size),
|
|
||||||
fChunk(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeChunkKey(const FreeChunk* chunk)
|
|
||||||
:
|
|
||||||
fSize(chunk->Size()),
|
|
||||||
fChunk(chunk)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int Compare(const FreeChunk* chunk) const
|
|
||||||
{
|
|
||||||
size_t chunkSize = chunk->Size();
|
|
||||||
if (chunkSize != fSize)
|
|
||||||
return fSize < chunkSize ? -1 : 1;
|
|
||||||
|
|
||||||
if (fChunk == chunk)
|
|
||||||
return 0;
|
|
||||||
return fChunk < chunk ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t fSize;
|
|
||||||
const FreeChunk* fChunk;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FreeChunkTreeDefinition {
|
|
||||||
typedef FreeChunkKey KeyType;
|
|
||||||
typedef FreeChunk NodeType;
|
|
||||||
|
|
||||||
static FreeChunkKey GetKey(const FreeChunk* node)
|
|
||||||
{
|
|
||||||
return FreeChunkKey(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SplayTreeLink<FreeChunk>* GetLink(FreeChunk* node)
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Compare(const FreeChunkKey& key, const FreeChunk* node)
|
|
||||||
{
|
|
||||||
return key.Compare(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FreeChunk** GetListLink(FreeChunk* node)
|
|
||||||
{
|
|
||||||
return node->NextLink();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
typedef IteratableSplayTree<FreeChunkTreeDefinition> FreeChunkTree;
|
|
||||||
|
|
||||||
|
|
||||||
struct LargeAllocation {
|
struct LargeAllocation {
|
||||||
LargeAllocation()
|
LargeAllocation()
|
||||||
{
|
{
|
||||||
@@ -252,19 +117,11 @@ typedef BOpenHashTable<LargeAllocationHashDefinition> LargeAllocationHashTable;
|
|||||||
|
|
||||||
static void* sHeapBase;
|
static void* sHeapBase;
|
||||||
static void* sHeapEnd;
|
static void* sHeapEnd;
|
||||||
static size_t sMaxHeapSize, sAvailable, sMaxHeapUsage;
|
static SimpleAllocator<kAlignment> sAllocator;
|
||||||
static FreeChunkTree sFreeChunkTree;
|
|
||||||
|
|
||||||
static LargeAllocationHashTable sLargeAllocations;
|
static LargeAllocationHashTable sLargeAllocations;
|
||||||
|
|
||||||
|
|
||||||
static inline size_t
|
|
||||||
align(size_t size)
|
|
||||||
{
|
|
||||||
return (size + kAlignment - 1) & ~(kAlignment - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void*
|
static void*
|
||||||
malloc_large(size_t size)
|
malloc_large(size_t size)
|
||||||
{
|
{
|
||||||
@@ -299,93 +156,6 @@ free_large(void* address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
FreeChunk::SetTo(size_t size)
|
|
||||||
{
|
|
||||||
fSize = size;
|
|
||||||
fNext = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Returns the amount of bytes that can be allocated
|
|
||||||
in this chunk.
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
FreeChunk::Size() const
|
|
||||||
{
|
|
||||||
return (addr_t)this + fSize - (addr_t)AllocatedAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Splits the upper half at the requested location and returns it. This chunk
|
|
||||||
will no longer be a valid FreeChunk object; only its fSize will be valid.
|
|
||||||
*/
|
|
||||||
FreeChunk*
|
|
||||||
FreeChunk::Split(size_t splitSize)
|
|
||||||
{
|
|
||||||
splitSize = align(splitSize);
|
|
||||||
|
|
||||||
FreeChunk* chunk = (FreeChunk*)((addr_t)AllocatedAddress() + splitSize);
|
|
||||||
size_t newSize = (addr_t)chunk - (addr_t)this;
|
|
||||||
chunk->fSize = fSize - newSize;
|
|
||||||
chunk->fNext = NULL;
|
|
||||||
|
|
||||||
fSize = newSize;
|
|
||||||
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Checks if the specified chunk touches this chunk, so
|
|
||||||
that they could be joined.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
FreeChunk::IsTouching(FreeChunk* chunk)
|
|
||||||
{
|
|
||||||
return chunk
|
|
||||||
&& (((uint8*)this + fSize == (uint8*)chunk)
|
|
||||||
|| (uint8*)chunk + chunk->fSize == (uint8*)this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Joins the chunk to this chunk and returns the pointer
|
|
||||||
to the new chunk - which will either be one of the
|
|
||||||
two chunks.
|
|
||||||
Note, the chunks must be joinable, or else this method
|
|
||||||
doesn't work correctly. Use FreeChunk::IsTouching()
|
|
||||||
to check if this method can be applied.
|
|
||||||
*/
|
|
||||||
FreeChunk*
|
|
||||||
FreeChunk::Join(FreeChunk* chunk)
|
|
||||||
{
|
|
||||||
if (chunk < this) {
|
|
||||||
chunk->fSize += fSize;
|
|
||||||
chunk->fNext = fNext;
|
|
||||||
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
fSize += chunk->fSize;
|
|
||||||
fNext = chunk->fNext;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void*
|
|
||||||
FreeChunk::AllocatedAddress() const
|
|
||||||
{
|
|
||||||
return (void*)static_cast<const FreeChunkData*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FreeChunk*
|
|
||||||
FreeChunk::SetToAllocated(void* allocated)
|
|
||||||
{
|
|
||||||
return static_cast<FreeChunk*>((FreeChunkData*)allocated);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -404,7 +174,7 @@ heap_release()
|
|||||||
platform_free_heap_region(sHeapBase, (addr_t)sHeapEnd - (addr_t)sHeapBase);
|
platform_free_heap_region(sHeapBase, (addr_t)sHeapEnd - (addr_t)sHeapBase);
|
||||||
|
|
||||||
sHeapBase = sHeapEnd = NULL;
|
sHeapBase = sHeapEnd = NULL;
|
||||||
memset((void*)&sFreeChunkTree, 0, sizeof(sFreeChunkTree));
|
memset((void*)&sAllocator, 0, sizeof(sAllocator));
|
||||||
memset((void*)&sLargeAllocations, 0, sizeof(sLargeAllocations));
|
memset((void*)&sLargeAllocations, 0, sizeof(sLargeAllocations));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,8 +183,8 @@ void
|
|||||||
heap_print_statistics()
|
heap_print_statistics()
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_MAX_HEAP_USAGE
|
#ifdef DEBUG_MAX_HEAP_USAGE
|
||||||
dprintf("maximum boot loader heap usage: %zu, currently used: %zu\n",
|
dprintf("maximum boot loader heap usage: %" B_PRIu32 ", currently used: %" B_PRIu32 "\n",
|
||||||
sMaxHeapUsage, sMaxHeapSize - sAvailable);
|
sAllocator.MaxHeapUsage(), sAllocator.MaxHeapSize() - sAllocator.Available());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,18 +202,8 @@ heap_init(stage2_args* args)
|
|||||||
|
|
||||||
sHeapBase = base;
|
sHeapBase = base;
|
||||||
sHeapEnd = (void*)((addr_t)base + size);
|
sHeapEnd = (void*)((addr_t)base + size);
|
||||||
sMaxHeapSize = (uint8*)sHeapEnd - (uint8*)sHeapBase;
|
|
||||||
|
|
||||||
// declare the whole heap as one chunk, and add it
|
sAllocator.AddChunk(sHeapBase, size);
|
||||||
// to the free list
|
|
||||||
FreeChunk* chunk = (FreeChunk*)base;
|
|
||||||
chunk->SetTo(sMaxHeapSize);
|
|
||||||
sFreeChunkTree.Insert(chunk);
|
|
||||||
|
|
||||||
sAvailable = chunk->Size();
|
|
||||||
#ifdef DEBUG_MAX_HEAP_USAGE
|
|
||||||
sMaxHeapUsage = sMaxHeapSize - sAvailable;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sLargeAllocations.Init(64) != B_OK)
|
if (sLargeAllocations.Init(64) != B_OK)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
@@ -452,77 +212,39 @@ heap_init(stage2_args* args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HEAP_TEST
|
|
||||||
void
|
|
||||||
dump_chunks(void)
|
|
||||||
{
|
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindMin();
|
|
||||||
while (chunk != NULL) {
|
|
||||||
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
|
|
||||||
chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
|
|
||||||
chunk->Next());
|
|
||||||
chunk = chunk->Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
heap_available(void)
|
heap_available()
|
||||||
{
|
{
|
||||||
return (uint32)sAvailable;
|
return sAllocator.Available();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void*
|
void*
|
||||||
malloc(size_t size)
|
malloc(size_t size)
|
||||||
{
|
{
|
||||||
if (sHeapBase == NULL || size == 0)
|
if (sHeapBase == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// align the size requirement to a kAlignment bytes boundary
|
|
||||||
if (size < sizeof(FreeChunkData))
|
|
||||||
size = sizeof(FreeChunkData);
|
|
||||||
size = align(size);
|
|
||||||
|
|
||||||
if (size >= kLargeAllocationThreshold)
|
if (size >= kLargeAllocationThreshold)
|
||||||
return malloc_large(size);
|
return malloc_large(size);
|
||||||
|
|
||||||
if (size > sAvailable) {
|
void* allocated = sAllocator.Allocate(size);
|
||||||
|
if (allocated == NULL) {
|
||||||
|
if (size == 0)
|
||||||
|
return allocated;
|
||||||
|
|
||||||
|
if (size > sAllocator.Available()) {
|
||||||
dprintf("malloc(): Out of memory allocating a block of %ld bytes, "
|
dprintf("malloc(): Out of memory allocating a block of %ld bytes, "
|
||||||
"only %ld left!\n", size, sAvailable);
|
"only %" B_PRId32 " left!\n", size, sAllocator.Available());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindClosest(FreeChunkKey(size), true,
|
dprintf("malloc(): Out of memory allocating a block of %ld bytes!\n", size);
|
||||||
true);
|
|
||||||
|
|
||||||
if (chunk == NULL) {
|
|
||||||
// could not find a free chunk as large as needed
|
|
||||||
dprintf("malloc(): Out of memory allocating a block of %ld bytes, "
|
|
||||||
"no free chunks!\n", size);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sFreeChunkTree.Remove(chunk);
|
TRACE("malloc(%lu) -> %p\n", size, allocated);
|
||||||
sAvailable -= chunk->Size();
|
return allocated;
|
||||||
|
|
||||||
void* allocatedAddress = chunk->AllocatedAddress();
|
|
||||||
|
|
||||||
// If this chunk is bigger than the requested size and there's enough space
|
|
||||||
// left over for a new chunk, we split it.
|
|
||||||
if (chunk->Size() >= size + align(sizeof(FreeChunk))) {
|
|
||||||
FreeChunk* freeChunk = chunk->Split(size);
|
|
||||||
sFreeChunkTree.Insert(freeChunk);
|
|
||||||
sAvailable += freeChunk->Size();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_MAX_HEAP_USAGE
|
|
||||||
sMaxHeapUsage = std::max(sMaxHeapUsage, sMaxHeapSize - sAvailable);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TRACE("malloc(%lu) -> %p\n", size, allocatedAddress);
|
|
||||||
return allocatedAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -538,8 +260,7 @@ realloc(void* oldBuffer, size_t newSize)
|
|||||||
size_t oldSize = 0;
|
size_t oldSize = 0;
|
||||||
if (oldBuffer != NULL) {
|
if (oldBuffer != NULL) {
|
||||||
if (oldBuffer >= sHeapBase && oldBuffer < sHeapEnd) {
|
if (oldBuffer >= sHeapBase && oldBuffer < sHeapEnd) {
|
||||||
FreeChunk* oldChunk = FreeChunk::SetToAllocated(oldBuffer);
|
oldSize = sAllocator.UsableSize(oldBuffer);
|
||||||
oldSize = oldChunk->Size();
|
|
||||||
} else {
|
} else {
|
||||||
LargeAllocation* allocation = sLargeAllocations.Lookup(oldBuffer);
|
LargeAllocation* allocation = sLargeAllocations.Lookup(oldBuffer);
|
||||||
if (allocation == NULL) {
|
if (allocation == NULL) {
|
||||||
@@ -597,50 +318,5 @@ free(void* allocated)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeChunk* freedChunk = FreeChunk::SetToAllocated(allocated);
|
sAllocator.Free(allocated);
|
||||||
|
|
||||||
#ifdef DEBUG_ALLOCATIONS
|
|
||||||
if (freedChunk->Size() > sMaxHeapSize - sAvailable) {
|
|
||||||
panic("freed chunk %p clobbered (%#zx)!\n", freedChunk,
|
|
||||||
freedChunk->Size());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindMin();
|
|
||||||
while (chunk) {
|
|
||||||
if (chunk->Size() > sAvailable || freedChunk == chunk)
|
|
||||||
panic("invalid chunk in free list (%p (%zu)), or double free\n",
|
|
||||||
chunk, chunk->Size());
|
|
||||||
chunk = chunk->Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// try to join the new free chunk with an existing one
|
|
||||||
// it may be joined with up to two chunks
|
|
||||||
|
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindMin();
|
|
||||||
int32 joinCount = 0;
|
|
||||||
|
|
||||||
while (chunk) {
|
|
||||||
FreeChunk* nextChunk = chunk->Next();
|
|
||||||
|
|
||||||
if (chunk->IsTouching(freedChunk)) {
|
|
||||||
sFreeChunkTree.Remove(chunk);
|
|
||||||
sAvailable -= chunk->Size();
|
|
||||||
|
|
||||||
freedChunk = chunk->Join(freedChunk);
|
|
||||||
|
|
||||||
if (++joinCount == 2)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = nextChunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
sFreeChunkTree.Insert(freedChunk);
|
|
||||||
sAvailable += freedChunk->Size();
|
|
||||||
#ifdef DEBUG_MAX_HEAP_USAGE
|
|
||||||
sMaxHeapUsage = std::max(sMaxHeapUsage, sMaxHeapSize - sAvailable);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,36 +6,17 @@
|
|||||||
|
|
||||||
#include "runtime_loader_private.h"
|
#include "runtime_loader_private.h"
|
||||||
|
|
||||||
#include <syscalls.h>
|
|
||||||
|
|
||||||
#ifdef HEAP_TEST
|
|
||||||
# include <stdio.h>
|
|
||||||
#endif
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <util/SplayTree.h>
|
|
||||||
|
|
||||||
#include <locks.h>
|
#include <locks.h>
|
||||||
|
#include <syscalls.h>
|
||||||
|
|
||||||
|
#include <util/SimpleAllocator.h>
|
||||||
|
|
||||||
|
|
||||||
/*! This is a very simple malloc()/free() implementation - it only
|
|
||||||
manages a free list.
|
|
||||||
After heap_init() is called, all free memory is contained in one
|
|
||||||
big chunk, the only entry in the free link list (which is a single
|
|
||||||
linked list).
|
|
||||||
When memory is allocated, the smallest free chunk that contains
|
|
||||||
the requested size is split (or taken as a whole if it can't be
|
|
||||||
splitted anymore), and it's lower half will be removed from the
|
|
||||||
free list.
|
|
||||||
The free list is ordered by size, starting with the smallest
|
|
||||||
free chunk available. When a chunk is freed, it will be joint
|
|
||||||
with its predecessor or successor, if possible.
|
|
||||||
To ease list handling, the list anchor itself is a free chunk with
|
|
||||||
size 0 that can't be allocated.
|
|
||||||
*/
|
|
||||||
#if __cplusplus >= 201103L
|
#if __cplusplus >= 201103L
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
const static size_t kAlignment = alignof(max_align_t);
|
const static size_t kAlignment = alignof(max_align_t);
|
||||||
@@ -50,214 +31,7 @@ const static size_t kHeapGrowthAlignment = 32 * 1024;
|
|||||||
static const char* const kLockName = "runtime_loader heap";
|
static const char* const kLockName = "runtime_loader heap";
|
||||||
static recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName);
|
static recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName);
|
||||||
|
|
||||||
|
static SimpleAllocator<kAlignment> sAllocator;
|
||||||
class Chunk {
|
|
||||||
public:
|
|
||||||
size_t CompleteSize() const
|
|
||||||
{
|
|
||||||
return fSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
union {
|
|
||||||
size_t fSize;
|
|
||||||
char fAlignment[kAlignment];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class FreeChunk;
|
|
||||||
|
|
||||||
|
|
||||||
struct FreeChunkData : SplayTreeLink<FreeChunk> {
|
|
||||||
|
|
||||||
FreeChunk* Next() const
|
|
||||||
{
|
|
||||||
return fNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeChunk** NextLink()
|
|
||||||
{
|
|
||||||
return &fNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
FreeChunk* fNext;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class FreeChunk : public Chunk, public FreeChunkData {
|
|
||||||
public:
|
|
||||||
void SetTo(size_t size);
|
|
||||||
|
|
||||||
size_t Size() const;
|
|
||||||
|
|
||||||
FreeChunk* Split(size_t splitSize);
|
|
||||||
bool IsTouching(FreeChunk* link);
|
|
||||||
FreeChunk* Join(FreeChunk* link);
|
|
||||||
|
|
||||||
void* AllocatedAddress() const;
|
|
||||||
static FreeChunk* SetToAllocated(void* allocated);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FreeChunkKey {
|
|
||||||
FreeChunkKey(size_t size)
|
|
||||||
:
|
|
||||||
fSize(size),
|
|
||||||
fChunk(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeChunkKey(const FreeChunk* chunk)
|
|
||||||
:
|
|
||||||
fSize(chunk->Size()),
|
|
||||||
fChunk(chunk)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int Compare(const FreeChunk* chunk) const
|
|
||||||
{
|
|
||||||
size_t chunkSize = chunk->Size();
|
|
||||||
if (chunkSize != fSize)
|
|
||||||
return fSize < chunkSize ? -1 : 1;
|
|
||||||
|
|
||||||
if (fChunk == chunk)
|
|
||||||
return 0;
|
|
||||||
return fChunk < chunk ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t fSize;
|
|
||||||
const FreeChunk* fChunk;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FreeChunkTreeDefinition {
|
|
||||||
typedef FreeChunkKey KeyType;
|
|
||||||
typedef FreeChunk NodeType;
|
|
||||||
|
|
||||||
static FreeChunkKey GetKey(const FreeChunk* node)
|
|
||||||
{
|
|
||||||
return FreeChunkKey(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SplayTreeLink<FreeChunk>* GetLink(FreeChunk* node)
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Compare(const FreeChunkKey& key, const FreeChunk* node)
|
|
||||||
{
|
|
||||||
return key.Compare(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FreeChunk** GetListLink(FreeChunk* node)
|
|
||||||
{
|
|
||||||
return node->NextLink();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
typedef IteratableSplayTree<FreeChunkTreeDefinition> FreeChunkTree;
|
|
||||||
|
|
||||||
|
|
||||||
static size_t sAvailable;
|
|
||||||
static FreeChunkTree sFreeChunkTree;
|
|
||||||
|
|
||||||
|
|
||||||
static inline size_t
|
|
||||||
align(size_t size, size_t alignment = kAlignment)
|
|
||||||
{
|
|
||||||
return (size + alignment - 1) & ~(alignment - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
FreeChunk::SetTo(size_t size)
|
|
||||||
{
|
|
||||||
fSize = size;
|
|
||||||
fNext = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Returns the amount of bytes that can be allocated
|
|
||||||
in this chunk.
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
FreeChunk::Size() const
|
|
||||||
{
|
|
||||||
return (addr_t)this + fSize - (addr_t)AllocatedAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Splits the upper half at the requested location and returns it. This chunk
|
|
||||||
will no longer be a valid FreeChunk object; only its fSize will be valid.
|
|
||||||
*/
|
|
||||||
FreeChunk*
|
|
||||||
FreeChunk::Split(size_t splitSize)
|
|
||||||
{
|
|
||||||
splitSize = align(splitSize);
|
|
||||||
|
|
||||||
FreeChunk* chunk = (FreeChunk*)((addr_t)AllocatedAddress() + splitSize);
|
|
||||||
size_t newSize = (addr_t)chunk - (addr_t)this;
|
|
||||||
chunk->fSize = fSize - newSize;
|
|
||||||
chunk->fNext = NULL;
|
|
||||||
|
|
||||||
fSize = newSize;
|
|
||||||
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Checks if the specified chunk touches this chunk, so
|
|
||||||
that they could be joined.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
FreeChunk::IsTouching(FreeChunk* chunk)
|
|
||||||
{
|
|
||||||
return chunk
|
|
||||||
&& (((uint8*)this + fSize == (uint8*)chunk)
|
|
||||||
|| (uint8*)chunk + chunk->fSize == (uint8*)this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Joins the chunk to this chunk and returns the pointer
|
|
||||||
to the new chunk - which will either be one of the
|
|
||||||
two chunks.
|
|
||||||
Note, the chunks must be joinable, or else this method
|
|
||||||
doesn't work correctly. Use FreeChunk::IsTouching()
|
|
||||||
to check if this method can be applied.
|
|
||||||
*/
|
|
||||||
FreeChunk*
|
|
||||||
FreeChunk::Join(FreeChunk* chunk)
|
|
||||||
{
|
|
||||||
if (chunk < this) {
|
|
||||||
chunk->fSize += fSize;
|
|
||||||
chunk->fNext = fNext;
|
|
||||||
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
fSize += chunk->fSize;
|
|
||||||
fNext = chunk->fNext;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void*
|
|
||||||
FreeChunk::AllocatedAddress() const
|
|
||||||
{
|
|
||||||
return (void*)static_cast<const FreeChunkData*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FreeChunk*
|
|
||||||
FreeChunk::SetToAllocated(void* allocated)
|
|
||||||
{
|
|
||||||
return static_cast<FreeChunk*>((FreeChunkData*)allocated);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
@@ -272,12 +46,7 @@ add_area(size_t size)
|
|||||||
if (area < 0)
|
if (area < 0)
|
||||||
return area;
|
return area;
|
||||||
|
|
||||||
// declare the whole area as one chunk, and add it to the free tree
|
sAllocator.AddChunk(base, size);
|
||||||
FreeChunk* chunk = (FreeChunk*)base;
|
|
||||||
chunk->SetTo(size);
|
|
||||||
sFreeChunkTree.Insert(chunk);
|
|
||||||
|
|
||||||
sAvailable += chunk->Size();
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +54,7 @@ add_area(size_t size)
|
|||||||
static status_t
|
static status_t
|
||||||
grow_heap(size_t bytes)
|
grow_heap(size_t bytes)
|
||||||
{
|
{
|
||||||
return add_area(align(align(sizeof(Chunk)) + bytes, kHeapGrowthAlignment));
|
return add_area(sAllocator.Align(kAlignment + bytes, kHeapGrowthAlignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -307,21 +76,6 @@ heap_reinit_after_fork()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HEAP_TEST
|
|
||||||
void
|
|
||||||
dump_chunks(void)
|
|
||||||
{
|
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindMin();
|
|
||||||
while (chunk != NULL) {
|
|
||||||
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
|
|
||||||
chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
|
|
||||||
chunk->Next());
|
|
||||||
chunk = chunk->Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void*
|
void*
|
||||||
malloc(size_t size)
|
malloc(size_t size)
|
||||||
{
|
{
|
||||||
@@ -330,84 +84,36 @@ malloc(size_t size)
|
|||||||
|
|
||||||
RecursiveLocker _(sLock);
|
RecursiveLocker _(sLock);
|
||||||
|
|
||||||
// align the size requirement to a kAlignment bytes boundary
|
void* allocated = sAllocator.Allocate(size);
|
||||||
if (size < sizeof(FreeChunkData))
|
if (allocated == NULL) {
|
||||||
size = sizeof(FreeChunkData);
|
|
||||||
size = align(size);
|
|
||||||
|
|
||||||
if (size > sAvailable) {
|
|
||||||
// try to enlarge heap
|
// try to enlarge heap
|
||||||
if (grow_heap(size) != B_OK)
|
if (grow_heap(size) != B_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
FreeChunkKey key(size);
|
allocated = sAllocator.Allocate(size);
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindClosest(key, true, true);
|
if (allocated == NULL) {
|
||||||
if (chunk == NULL) {
|
|
||||||
// could not find a free chunk as large as needed
|
|
||||||
if (grow_heap(size) != B_OK)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
chunk = sFreeChunkTree.FindClosest(key, true, true);
|
|
||||||
if (chunk == NULL) {
|
|
||||||
TRACE(("no allocation chunk found after growing the heap\n"));
|
TRACE(("no allocation chunk found after growing the heap\n"));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sFreeChunkTree.Remove(chunk);
|
|
||||||
sAvailable -= chunk->Size();
|
|
||||||
|
|
||||||
void* allocatedAddress = chunk->AllocatedAddress();
|
|
||||||
|
|
||||||
// If this chunk is bigger than the requested size and there's enough space
|
|
||||||
// left over for a new chunk, we split it.
|
|
||||||
if (chunk->Size() >= size + align(sizeof(FreeChunk))) {
|
|
||||||
FreeChunk* freeChunk = chunk->Split(size);
|
|
||||||
sFreeChunkTree.Insert(freeChunk);
|
|
||||||
sAvailable += freeChunk->Size();
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("malloc(%lu) -> %p\n", size, allocatedAddress));
|
TRACE(("malloc(%lu) -> %p\n", size, allocatedAddress));
|
||||||
return allocatedAddress;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void*
|
void*
|
||||||
realloc(void* oldBuffer, size_t newSize)
|
realloc(void* oldBuffer, size_t newSize)
|
||||||
{
|
{
|
||||||
if (newSize == 0) {
|
|
||||||
TRACE(("realloc(%p, %lu) -> NULL\n", oldBuffer, newSize));
|
|
||||||
free(oldBuffer);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecursiveLocker _(sLock);
|
RecursiveLocker _(sLock);
|
||||||
|
|
||||||
size_t oldSize = 0;
|
void* newBuffer = sAllocator.Reallocate(oldBuffer, newSize);
|
||||||
if (oldBuffer != NULL) {
|
if (oldBuffer == newBuffer) {
|
||||||
FreeChunk* oldChunk = FreeChunk::SetToAllocated(oldBuffer);
|
|
||||||
oldSize = oldChunk->Size();
|
|
||||||
|
|
||||||
// Check if the old buffer still fits, and if it makes sense to keep it.
|
|
||||||
if (oldSize >= newSize
|
|
||||||
&& (oldSize < 128 || newSize > oldSize / 3)) {
|
|
||||||
TRACE(("realloc(%p, %lu) old buffer is large enough\n",
|
TRACE(("realloc(%p, %lu) old buffer is large enough\n",
|
||||||
oldBuffer, newSize));
|
oldBuffer, newSize));
|
||||||
return oldBuffer;
|
} else {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void* newBuffer = malloc(newSize);
|
|
||||||
if (newBuffer == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (oldBuffer != NULL) {
|
|
||||||
memcpy(newBuffer, oldBuffer, std::min(oldSize, newSize));
|
|
||||||
free(oldBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("realloc(%p, %lu) -> %p\n", oldBuffer, newSize, newBuffer));
|
TRACE(("realloc(%p, %lu) -> %p\n", oldBuffer, newSize, newBuffer));
|
||||||
|
}
|
||||||
return newBuffer;
|
return newBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,31 +139,5 @@ free(void* allocated)
|
|||||||
|
|
||||||
TRACE(("free(%p)\n", allocated));
|
TRACE(("free(%p)\n", allocated));
|
||||||
|
|
||||||
|
sAllocator.Free(allocated);
|
||||||
FreeChunk* freedChunk = FreeChunk::SetToAllocated(allocated);
|
|
||||||
|
|
||||||
// try to join the new free chunk with an existing one
|
|
||||||
// it may be joined with up to two chunks
|
|
||||||
|
|
||||||
FreeChunk* chunk = sFreeChunkTree.FindMin();
|
|
||||||
int32 joinCount = 0;
|
|
||||||
|
|
||||||
while (chunk) {
|
|
||||||
FreeChunk* nextChunk = chunk->Next();
|
|
||||||
|
|
||||||
if (chunk->IsTouching(freedChunk)) {
|
|
||||||
sFreeChunkTree.Remove(chunk);
|
|
||||||
sAvailable -= chunk->Size();
|
|
||||||
|
|
||||||
freedChunk = chunk->Join(freedChunk);
|
|
||||||
|
|
||||||
if (++joinCount == 2)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = nextChunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
sFreeChunkTree.Insert(freedChunk);
|
|
||||||
sAvailable += freedChunk->Size();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,6 @@ SubDir HAIKU_TOP src tests system boot heap ;
|
|||||||
|
|
||||||
UsePrivateKernelHeaders ;
|
UsePrivateKernelHeaders ;
|
||||||
|
|
||||||
ObjectDefines heap.cpp : HEAP_TEST=1 ;
|
|
||||||
|
|
||||||
SimpleTest boot_heap_test :
|
SimpleTest boot_heap_test :
|
||||||
heap_test.cpp
|
heap_test.cpp
|
||||||
|
|
||||||
# from the boot loader
|
|
||||||
heap.cpp
|
|
||||||
;
|
;
|
||||||
|
|
||||||
SEARCH on [ FGristFiles
|
|
||||||
heap.cpp
|
|
||||||
] = [ FDirName $(HAIKU_TOP) src system boot loader ] ;
|
|
||||||
|
|||||||
@@ -4,46 +4,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <boot/platform.h>
|
|
||||||
#include <boot/heap.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <util/SimpleAllocator.h>
|
||||||
void* heap_malloc(size_t size);
|
|
||||||
void* heap_realloc(void* oldBuffer, size_t size);
|
|
||||||
void heap_free(void* buffer);
|
|
||||||
extern void dump_chunks(void);
|
|
||||||
extern uint32 heap_available(void);
|
|
||||||
|
|
||||||
|
|
||||||
|
static SimpleAllocator<> sAllocator;
|
||||||
const int32 kHeapSize = 32 * 1024;
|
const int32 kHeapSize = 32 * 1024;
|
||||||
|
|
||||||
int32 gVerbosity = 1;
|
int32 gVerbosity = 1;
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
platform_free_heap_region(void *_base, size_t size)
|
|
||||||
{
|
|
||||||
free(_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
platform_allocate_heap_region(size_t size, void **_base)
|
|
||||||
{
|
|
||||||
void* base = malloc(kHeapSize);
|
|
||||||
if (base == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
*_base = base;
|
|
||||||
return kHeapSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
panic(const char* format, ...)
|
panic(const char* format, ...)
|
||||||
{
|
{
|
||||||
@@ -71,21 +44,21 @@ dump_allocated_chunk(int32 index, void* buffer)
|
|||||||
size, *size);
|
size, *size);
|
||||||
|
|
||||||
if (gVerbosity > 3)
|
if (gVerbosity > 3)
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void*
|
static void*
|
||||||
test_malloc(size_t bytes)
|
test_malloc(size_t bytes)
|
||||||
{
|
{
|
||||||
return heap_malloc(bytes);
|
return sAllocator.Allocate(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void*
|
static void*
|
||||||
test_realloc(void* oldBuffer, size_t size)
|
test_realloc(void* oldBuffer, size_t size)
|
||||||
{
|
{
|
||||||
return heap_realloc(oldBuffer, size);
|
return sAllocator.Reallocate(oldBuffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -97,11 +70,11 @@ test_free(void* buffer)
|
|||||||
dump_allocated_chunk(-1, buffer);
|
dump_allocated_chunk(-1, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_free(buffer);
|
sAllocator.Free(buffer);
|
||||||
|
|
||||||
if (gVerbosity > 4) {
|
if (gVerbosity > 4) {
|
||||||
puts("\t- after:");
|
puts("\t- after:");
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,12 +91,12 @@ random_allocations(void* array[], size_t maxSize)
|
|||||||
size_t size = size_t(rand() * 1. * maxSize / RAND_MAX);
|
size_t size = size_t(rand() * 1. * maxSize / RAND_MAX);
|
||||||
array[i] = test_malloc(size);
|
array[i] = test_malloc(size);
|
||||||
if (array[i] == NULL) {
|
if (array[i] == NULL) {
|
||||||
if ((size > heap_available() || size == 0) && gVerbosity < 2)
|
if ((size > sAllocator.Available() || size == 0) && gVerbosity < 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
printf( "%ld. allocating %ld bytes failed (%ld bytes total allocated, "
|
printf( "%ld. allocating %ld bytes failed (%ld bytes total allocated, "
|
||||||
"%ld free (%ld))\n",
|
"%ld free (%ld))\n",
|
||||||
i, size, total, heap_available(), kHeapSize - total);
|
i, size, total, sAllocator.Available(), kHeapSize - total);
|
||||||
} else {
|
} else {
|
||||||
dump_allocated_chunk(i, array[i]);
|
dump_allocated_chunk(i, array[i]);
|
||||||
|
|
||||||
@@ -134,7 +107,7 @@ random_allocations(void* array[], size_t maxSize)
|
|||||||
|
|
||||||
printf("\t%ld bytes allocated\n", total);
|
printf("\t%ld bytes allocated\n", total);
|
||||||
if (gVerbosity > 3)
|
if (gVerbosity > 3)
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -146,18 +119,16 @@ main(int argc, char** argv)
|
|||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
gVerbosity = atoi(argv[1]);
|
gVerbosity = atoi(argv[1]);
|
||||||
|
|
||||||
stage2_args args;
|
void* base = malloc(kHeapSize);
|
||||||
memset(&args, 0, sizeof(args));
|
if (base == NULL) {
|
||||||
args.heap_size = kHeapSize;
|
|
||||||
|
|
||||||
if (heap_init(&args) < B_OK) {
|
|
||||||
fprintf(stderr, "Could not initialize heap.\n");
|
fprintf(stderr, "Could not initialize heap.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
sAllocator.AddChunk(base, kHeapSize);
|
||||||
|
|
||||||
printf("heap size == %" B_PRId32 "\n", kHeapSize);
|
printf("heap size == %" B_PRId32 "\n", kHeapSize);
|
||||||
if (gVerbosity > 2)
|
if (gVerbosity > 2)
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
|
|
||||||
puts("* simple allocation of 100 * 128 bytes");
|
puts("* simple allocation of 100 * 128 bytes");
|
||||||
void* array[100];
|
void* array[100];
|
||||||
@@ -167,7 +138,7 @@ main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gVerbosity > 2)
|
if (gVerbosity > 2)
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
|
|
||||||
puts("* testing different deleting order");
|
puts("* testing different deleting order");
|
||||||
if (gVerbosity > 2)
|
if (gVerbosity > 2)
|
||||||
@@ -179,7 +150,7 @@ main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gVerbosity > 2) {
|
if (gVerbosity > 2) {
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
puts("- free 40 from the middle (ascending):");
|
puts("- free 40 from the middle (ascending):");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +160,7 @@ main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gVerbosity > 2) {
|
if (gVerbosity > 2) {
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
puts("- free 30 from the start (ascending):");
|
puts("- free 30 from the start (ascending):");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +170,7 @@ main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gVerbosity > 2)
|
if (gVerbosity > 2)
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
|
|
||||||
puts("* allocate until it fails");
|
puts("* allocate until it fails");
|
||||||
int32 i = 0;
|
int32 i = 0;
|
||||||
@@ -209,7 +180,7 @@ main(int argc, char** argv)
|
|||||||
printf("\tallocation %ld failed - could allocate %" B_PRId32 " bytes (64th should fail).\n", i + 1, (kHeapSize / 64) * (i + 1));
|
printf("\tallocation %ld failed - could allocate %" B_PRId32 " bytes (64th should fail).\n", i + 1, (kHeapSize / 64) * (i + 1));
|
||||||
|
|
||||||
if (gVerbosity > 2)
|
if (gVerbosity > 2)
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
|
|
||||||
while (i-- > 0) {
|
while (i-- > 0) {
|
||||||
test_free(array[i]);
|
test_free(array[i]);
|
||||||
@@ -246,7 +217,7 @@ main(int argc, char** argv)
|
|||||||
|
|
||||||
if (gVerbosity > 2) {
|
if (gVerbosity > 2) {
|
||||||
puts("- freed one");
|
puts("- freed one");
|
||||||
dump_chunks();
|
sAllocator.DumpChunks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,7 +246,6 @@ main(int argc, char** argv)
|
|||||||
if (memcmp(newBuffer, "haiku", 5))
|
if (memcmp(newBuffer, "haiku", 5))
|
||||||
panic(" contents differ!");
|
panic(" contents differ!");
|
||||||
|
|
||||||
heap_release();
|
free(base);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user