diff --git a/headers/private/kernel/util/SimpleAllocator.h b/headers/private/kernel/util/SimpleAllocator.h new file mode 100644 index 0000000000..df2a5be6ac --- /dev/null +++ b/headers/private/kernel/util/SimpleAllocator.h @@ -0,0 +1,379 @@ +/* + * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2005-2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2025, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _SIMPLE_ALLOCATOR_H +#define _SIMPLE_ALLOCATOR_H + + +#include +#include + + +/*! 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 +class SimpleAllocator { + class Chunk { + public: + size_t CompleteSize() const + { + return fSize; + } + + protected: + union { + uint32 fSize; + char fAlignment[Alignment]; + }; + }; + + class FreeChunk; + + struct FreeChunkData : SplayTreeLink { + + 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(this); + } + + static FreeChunk* SetToAllocated(void* allocated) + { + return static_cast((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* 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 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 */ diff --git a/src/system/boot/loader/heap.cpp b/src/system/boot/loader/heap.cpp index 9da1620663..9fbd1e8436 100644 --- a/src/system/boot/loader/heap.cpp +++ b/src/system/boot/loader/heap.cpp @@ -1,5 +1,6 @@ /* * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2005-2013, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -13,17 +14,15 @@ #include #include -#include -#ifdef HEAP_TEST -#include -#define dprintf printf -#define malloc heap_malloc -#define free heap_free -#define realloc heap_realloc -void panic(const char* format, ...); -void free(void*); -#endif + +#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 + +#include //#define TRACE_HEAP @@ -34,29 +33,6 @@ void free(void*); #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; // 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 -class Chunk { -public: - size_t CompleteSize() const - { - return fSize; - } - -protected: - union { - size_t fSize; - char fAlignment[kAlignment]; - }; -}; - - -class FreeChunk; - - -struct FreeChunkData : SplayTreeLink { - - 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* 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 FreeChunkTree; - - struct LargeAllocation { LargeAllocation() { @@ -252,19 +117,11 @@ typedef BOpenHashTable LargeAllocationHashTable; static void* sHeapBase; static void* sHeapEnd; -static size_t sMaxHeapSize, sAvailable, sMaxHeapUsage; -static FreeChunkTree sFreeChunkTree; +static SimpleAllocator sAllocator; static LargeAllocationHashTable sLargeAllocations; -static inline size_t -align(size_t size) -{ - return (size + kAlignment - 1) & ~(kAlignment - 1); -} - - static void* 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(this); -} - - -FreeChunk* -FreeChunk::SetToAllocated(void* allocated) -{ - return static_cast((FreeChunkData*)allocated); -} - - // #pragma mark - @@ -404,7 +174,7 @@ heap_release() platform_free_heap_region(sHeapBase, (addr_t)sHeapEnd - (addr_t)sHeapBase); sHeapBase = sHeapEnd = NULL; - memset((void*)&sFreeChunkTree, 0, sizeof(sFreeChunkTree)); + memset((void*)&sAllocator, 0, sizeof(sAllocator)); memset((void*)&sLargeAllocations, 0, sizeof(sLargeAllocations)); } @@ -413,8 +183,8 @@ void heap_print_statistics() { #ifdef DEBUG_MAX_HEAP_USAGE - dprintf("maximum boot loader heap usage: %zu, currently used: %zu\n", - sMaxHeapUsage, sMaxHeapSize - sAvailable); + dprintf("maximum boot loader heap usage: %" B_PRIu32 ", currently used: %" B_PRIu32 "\n", + sAllocator.MaxHeapUsage(), sAllocator.MaxHeapSize() - sAllocator.Available()); #endif } @@ -432,18 +202,8 @@ heap_init(stage2_args* args) sHeapBase = base; sHeapEnd = (void*)((addr_t)base + size); - sMaxHeapSize = (uint8*)sHeapEnd - (uint8*)sHeapBase; - // declare the whole heap as one chunk, and add it - // 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 + sAllocator.AddChunk(sHeapBase, size); if (sLargeAllocations.Init(64) != B_OK) 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 -heap_available(void) +heap_available() { - return (uint32)sAvailable; + return sAllocator.Available(); } void* malloc(size_t size) { - if (sHeapBase == NULL || size == 0) + if (sHeapBase == 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) return malloc_large(size); - if (size > sAvailable) { - dprintf("malloc(): Out of memory allocating a block of %ld bytes, " - "only %ld left!\n", 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, " + "only %" B_PRId32 " left!\n", size, sAllocator.Available()); + return NULL; + } + + dprintf("malloc(): Out of memory allocating a block of %ld bytes!\n", size); return NULL; } - FreeChunk* chunk = sFreeChunkTree.FindClosest(FreeChunkKey(size), true, - 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; - } - - 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(); - } - -#ifdef DEBUG_MAX_HEAP_USAGE - sMaxHeapUsage = std::max(sMaxHeapUsage, sMaxHeapSize - sAvailable); -#endif - - TRACE("malloc(%lu) -> %p\n", size, allocatedAddress); - return allocatedAddress; + TRACE("malloc(%lu) -> %p\n", size, allocated); + return allocated; } @@ -538,8 +260,7 @@ realloc(void* oldBuffer, size_t newSize) size_t oldSize = 0; if (oldBuffer != NULL) { if (oldBuffer >= sHeapBase && oldBuffer < sHeapEnd) { - FreeChunk* oldChunk = FreeChunk::SetToAllocated(oldBuffer); - oldSize = oldChunk->Size(); + oldSize = sAllocator.UsableSize(oldBuffer); } else { LargeAllocation* allocation = sLargeAllocations.Lookup(oldBuffer); if (allocation == NULL) { @@ -597,50 +318,5 @@ free(void* allocated) return; } - FreeChunk* freedChunk = FreeChunk::SetToAllocated(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 + sAllocator.Free(allocated); } - diff --git a/src/system/runtime_loader/heap.cpp b/src/system/runtime_loader/heap.cpp index d108892a79..5208ace974 100644 --- a/src/system/runtime_loader/heap.cpp +++ b/src/system/runtime_loader/heap.cpp @@ -6,36 +6,17 @@ #include "runtime_loader_private.h" -#include - -#ifdef HEAP_TEST -# include -#endif #include #include #include -#include - #include +#include + +#include -/*! 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 #include 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 recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName); - -class Chunk { -public: - size_t CompleteSize() const - { - return fSize; - } - -protected: - union { - size_t fSize; - char fAlignment[kAlignment]; - }; -}; - - -class FreeChunk; - - -struct FreeChunkData : SplayTreeLink { - - 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* 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 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(this); -} - - -FreeChunk* -FreeChunk::SetToAllocated(void* allocated) -{ - return static_cast((FreeChunkData*)allocated); -} +static SimpleAllocator sAllocator; // #pragma mark - @@ -272,12 +46,7 @@ add_area(size_t size) if (area < 0) return area; - // declare the whole area as one chunk, and add it to the free tree - FreeChunk* chunk = (FreeChunk*)base; - chunk->SetTo(size); - sFreeChunkTree.Insert(chunk); - - sAvailable += chunk->Size(); + sAllocator.AddChunk(base, size); return B_OK; } @@ -285,7 +54,7 @@ add_area(size_t size) static status_t 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* malloc(size_t size) { @@ -330,84 +84,36 @@ malloc(size_t size) RecursiveLocker _(sLock); - // align the size requirement to a kAlignment bytes boundary - if (size < sizeof(FreeChunkData)) - size = sizeof(FreeChunkData); - size = align(size); - - if (size > sAvailable) { + void* allocated = sAllocator.Allocate(size); + if (allocated == NULL) { // try to enlarge heap if (grow_heap(size) != B_OK) return NULL; - } - FreeChunkKey key(size); - FreeChunk* chunk = sFreeChunkTree.FindClosest(key, true, true); - 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) { + allocated = sAllocator.Allocate(size); + if (allocated == NULL) { TRACE(("no allocation chunk found after growing the heap\n")); 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)); - return allocatedAddress; + return allocated; } void* realloc(void* oldBuffer, size_t newSize) { - if (newSize == 0) { - TRACE(("realloc(%p, %lu) -> NULL\n", oldBuffer, newSize)); - free(oldBuffer); - return NULL; - } - RecursiveLocker _(sLock); - size_t oldSize = 0; - if (oldBuffer != NULL) { - 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", - oldBuffer, newSize)); - return oldBuffer; - } + void* newBuffer = sAllocator.Reallocate(oldBuffer, newSize); + if (oldBuffer == newBuffer) { + TRACE(("realloc(%p, %lu) old buffer is large enough\n", + oldBuffer, newSize)); + } else { + TRACE(("realloc(%p, %lu) -> %p\n", oldBuffer, newSize, newBuffer)); } - - 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)); return newBuffer; } @@ -433,31 +139,5 @@ free(void* allocated) TRACE(("free(%p)\n", 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(); + sAllocator.Free(allocated); } diff --git a/src/tests/system/boot/heap/Jamfile b/src/tests/system/boot/heap/Jamfile index 370b277759..e146f92699 100644 --- a/src/tests/system/boot/heap/Jamfile +++ b/src/tests/system/boot/heap/Jamfile @@ -2,15 +2,6 @@ SubDir HAIKU_TOP src tests system boot heap ; UsePrivateKernelHeaders ; -ObjectDefines heap.cpp : HEAP_TEST=1 ; - SimpleTest boot_heap_test : heap_test.cpp - - # from the boot loader - heap.cpp ; - -SEARCH on [ FGristFiles - heap.cpp - ] = [ FDirName $(HAIKU_TOP) src system boot loader ] ; diff --git a/src/tests/system/boot/heap/heap_test.cpp b/src/tests/system/boot/heap/heap_test.cpp index a3762615e2..34541e20e1 100644 --- a/src/tests/system/boot/heap/heap_test.cpp +++ b/src/tests/system/boot/heap/heap_test.cpp @@ -4,46 +4,19 @@ */ -#include -#include - #include #include #include #include - -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); +#include +static SimpleAllocator<> sAllocator; const int32 kHeapSize = 32 * 1024; - 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 panic(const char* format, ...) { @@ -71,21 +44,21 @@ dump_allocated_chunk(int32 index, void* buffer) size, *size); if (gVerbosity > 3) - dump_chunks(); + sAllocator.DumpChunks(); } static void* test_malloc(size_t bytes) { - return heap_malloc(bytes); + return sAllocator.Allocate(bytes); } static void* 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); } - heap_free(buffer); + sAllocator.Free(buffer); if (gVerbosity > 4) { 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); array[i] = test_malloc(size); if (array[i] == NULL) { - if ((size > heap_available() || size == 0) && gVerbosity < 2) + if ((size > sAllocator.Available() || size == 0) && gVerbosity < 2) continue; printf( "%ld. allocating %ld bytes failed (%ld bytes total allocated, " "%ld free (%ld))\n", - i, size, total, heap_available(), kHeapSize - total); + i, size, total, sAllocator.Available(), kHeapSize - total); } else { dump_allocated_chunk(i, array[i]); @@ -134,7 +107,7 @@ random_allocations(void* array[], size_t maxSize) printf("\t%ld bytes allocated\n", total); if (gVerbosity > 3) - dump_chunks(); + sAllocator.DumpChunks(); return count; } @@ -146,18 +119,16 @@ main(int argc, char** argv) if (argc > 1) gVerbosity = atoi(argv[1]); - stage2_args args; - memset(&args, 0, sizeof(args)); - args.heap_size = kHeapSize; - - if (heap_init(&args) < B_OK) { + void* base = malloc(kHeapSize); + if (base == NULL) { fprintf(stderr, "Could not initialize heap.\n"); return -1; } + sAllocator.AddChunk(base, kHeapSize); printf("heap size == %" B_PRId32 "\n", kHeapSize); if (gVerbosity > 2) - dump_chunks(); + sAllocator.DumpChunks(); puts("* simple allocation of 100 * 128 bytes"); void* array[100]; @@ -167,7 +138,7 @@ main(int argc, char** argv) } if (gVerbosity > 2) - dump_chunks(); + sAllocator.DumpChunks(); puts("* testing different deleting order"); if (gVerbosity > 2) @@ -179,7 +150,7 @@ main(int argc, char** argv) } if (gVerbosity > 2) { - dump_chunks(); + sAllocator.DumpChunks(); puts("- free 40 from the middle (ascending):"); } @@ -189,7 +160,7 @@ main(int argc, char** argv) } if (gVerbosity > 2) { - dump_chunks(); + sAllocator.DumpChunks(); puts("- free 30 from the start (ascending):"); } @@ -199,7 +170,7 @@ main(int argc, char** argv) } if (gVerbosity > 2) - dump_chunks(); + sAllocator.DumpChunks(); puts("* allocate until it fails"); 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)); if (gVerbosity > 2) - dump_chunks(); + sAllocator.DumpChunks(); while (i-- > 0) { test_free(array[i]); @@ -246,7 +217,7 @@ main(int argc, char** argv) if (gVerbosity > 2) { puts("- freed one"); - dump_chunks(); + sAllocator.DumpChunks(); } } } @@ -275,7 +246,6 @@ main(int argc, char** argv) if (memcmp(newBuffer, "haiku", 5)) panic(" contents differ!"); - heap_release(); + free(base); return 0; } -