* Made a class out of free_chunk.

* Fixed realloc() - "size" wasn't what I thought it would be.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34412 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2009-12-01 15:39:55 +00:00
parent 86a3b75e8a
commit 943bec1287
+108 -86
View File
@@ -42,19 +42,29 @@
#define DEBUG_ALLOCATIONS #define DEBUG_ALLOCATIONS
// if defined, freed memory is filled with 0xcc // if defined, freed memory is filled with 0xcc
struct free_chunk { class FreeChunk {
uint32 size; public:
free_chunk* next; void SetTo(size_t size, FreeChunk* next);
uint32 Size() const; uint32 Size() const;
free_chunk* Split(uint32 splitSize); uint32 CompleteSize() const { return fSize; }
bool IsTouching(free_chunk* link);
free_chunk* Join(free_chunk* link);
void Remove(free_chunk* previous = NULL);
void Enqueue();
void* AllocatedAddress() const; FreeChunk* Next() const { return fNext; }
static free_chunk* SetToAllocated(void* allocated); void SetNext(FreeChunk* next) { fNext = next; }
FreeChunk* Split(uint32 splitSize);
bool IsTouching(FreeChunk* link);
FreeChunk* Join(FreeChunk* link);
void Remove(FreeChunk* previous = NULL);
void Enqueue();
void* AllocatedAddress() const;
static FreeChunk* SetToAllocated(void* allocated);
static addr_t NextOffset() { return sizeof(uint32); }
private:
uint32 fSize;
FreeChunk* fNext;
}; };
@@ -63,30 +73,41 @@ const static uint32 kAlignment = 4;
static void* sHeapBase; static void* sHeapBase;
static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable; static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable;
static free_chunk sFreeAnchor; static FreeChunk sFreeAnchor;
void
FreeChunk::SetTo(size_t size, FreeChunk* next)
{
fSize = size;
fNext = next;
}
/*! Returns the amount of bytes that can be allocated /*! Returns the amount of bytes that can be allocated
in this chunk. in this chunk.
*/ */
uint32 uint32
free_chunk::Size() const FreeChunk::Size() const
{ {
return size - sizeof(uint32); return fSize - FreeChunk::NextOffset();
} }
/*! Splits the upper half at the requested location /*! Splits the upper half at the requested location
and returns it. and returns it.
*/ */
free_chunk* FreeChunk*
free_chunk::Split(uint32 splitSize) FreeChunk::Split(uint32 splitSize)
{ {
free_chunk* chunk = (free_chunk*)((uint8*)this + sizeof(uint32) + splitSize); splitSize = (splitSize - 1 + kAlignment) & ~(kAlignment - 1);
chunk->size = size - splitSize - sizeof(uint32);
chunk->next = next;
size = splitSize + sizeof(uint32); FreeChunk* chunk
= (FreeChunk*)((uint8*)this + FreeChunk::NextOffset() + splitSize);
chunk->fSize = fSize - splitSize - FreeChunk::NextOffset();
chunk->fNext = fNext;
fSize = splitSize + FreeChunk::NextOffset();
return chunk; return chunk;
} }
@@ -96,11 +117,11 @@ free_chunk::Split(uint32 splitSize)
that they could be joined. that they could be joined.
*/ */
bool bool
free_chunk::IsTouching(free_chunk* chunk) FreeChunk::IsTouching(FreeChunk* chunk)
{ {
return chunk return chunk
&& (((uint8*)this + size == (uint8*)chunk) && (((uint8*)this + fSize == (uint8*)chunk)
|| (uint8*)chunk + chunk->size == (uint8*)this); || (uint8*)chunk + chunk->fSize == (uint8*)this);
} }
@@ -108,77 +129,78 @@ free_chunk::IsTouching(free_chunk* chunk)
to the new chunk - which will either be one of the to the new chunk - which will either be one of the
two chunks. two chunks.
Note, the chunks must be joinable, or else this method Note, the chunks must be joinable, or else this method
doesn't work correctly. Use free_chunk::IsTouching() doesn't work correctly. Use FreeChunk::IsTouching()
to check if this method can be applied. to check if this method can be applied.
*/ */
free_chunk* FreeChunk*
free_chunk::Join(free_chunk* chunk) FreeChunk::Join(FreeChunk* chunk)
{ {
if (chunk < this) { if (chunk < this) {
chunk->size += size; chunk->fSize += fSize;
chunk->next = next; chunk->fNext = fNext;
return chunk; return chunk;
} }
size += chunk->size; fSize += chunk->fSize;
next = chunk->next; fNext = chunk->fNext;
return this; return this;
} }
void void
free_chunk::Remove(free_chunk* previous) FreeChunk::Remove(FreeChunk* previous)
{ {
if (previous == NULL) { if (previous == NULL) {
// find the previous chunk in the list // find the previous chunk in the list
free_chunk* chunk = sFreeAnchor.next; FreeChunk* chunk = sFreeAnchor.fNext;
while (chunk != NULL && chunk != this) { while (chunk != NULL && chunk != this) {
previous = chunk; previous = chunk;
chunk = chunk->next; chunk = chunk->fNext;
} }
if (chunk == NULL) if (chunk == NULL)
panic("try to remove chunk that's not in list"); panic("try to remove chunk that's not in list");
} }
previous->next = this->next; previous->fNext = fNext;
this->next = NULL; fNext = NULL;
} }
void void
free_chunk::Enqueue() FreeChunk::Enqueue()
{ {
free_chunk* chunk = sFreeAnchor.next; FreeChunk* chunk = sFreeAnchor.fNext;
free_chunk* last = &sFreeAnchor; FreeChunk* last = &sFreeAnchor;
while (chunk && chunk->Size() < size) { while (chunk && chunk->Size() < fSize) {
last = chunk; last = chunk;
chunk = chunk->next; chunk = chunk->fNext;
} }
this->next = chunk; fNext = chunk;
last->next = this; last->fNext = this;
#ifdef DEBUG_ALLOCATIONS #ifdef DEBUG_ALLOCATIONS
memset((uint8*)this + sizeof(free_chunk), 0xcc, this->size - sizeof(free_chunk)); memset((uint8*)this + sizeof(FreeChunk), 0xde,
fSize - sizeof(FreeChunk));
#endif #endif
} }
void* void*
free_chunk::AllocatedAddress() const FreeChunk::AllocatedAddress() const
{ {
return (void*)&next; return (void*)&fNext;
} }
free_chunk* FreeChunk*
free_chunk::SetToAllocated(void* allocated) FreeChunk::SetToAllocated(void* allocated)
{ {
return (free_chunk*)((uint8*)allocated - sizeof(uint32)); return (FreeChunk*)((uint8*)allocated - FreeChunk::NextOffset());
} }
@@ -207,12 +229,9 @@ heap_init(stage2_args* args)
// declare the whole heap as one chunk, and add it // declare the whole heap as one chunk, and add it
// to the free list // to the free list
free_chunk* chunk = (free_chunk*)base; FreeChunk* chunk = (FreeChunk*)base;
chunk->size = sMaxHeapSize; chunk->SetTo(sMaxHeapSize, NULL);
chunk->next = NULL; sFreeAnchor.SetTo(0, chunk);
sFreeAnchor.size = 0;
sFreeAnchor.next = chunk;
return B_OK; return B_OK;
} }
@@ -239,14 +258,15 @@ grow_heap(uint32 bytes)
void void
dump_chunks(void) dump_chunks(void)
{ {
free_chunk* chunk = sFreeAnchor.next; FreeChunk* chunk = sFreeAnchor.Next();
free_chunk* last = &sFreeAnchor; FreeChunk* last = &sFreeAnchor;
while (chunk != NULL) { while (chunk != NULL) {
last = chunk; last = chunk;
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk, printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
chunk->size, (uint8*)chunk + chunk->size, chunk->next); chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
chunk = chunk->next; chunk->Next());
chunk = chunk->Next();
} }
} }
#endif #endif
@@ -272,11 +292,11 @@ malloc(size_t size)
return NULL; return NULL;
} }
free_chunk* chunk = sFreeAnchor.next; FreeChunk* chunk = sFreeAnchor.Next();
free_chunk* last = &sFreeAnchor; FreeChunk* last = &sFreeAnchor;
while (chunk && chunk->Size() < size) { while (chunk && chunk->Size() < size) {
last = chunk; last = chunk;
chunk = chunk->next; chunk = chunk->Next();
} }
if (chunk == NULL) { if (chunk == NULL) {
@@ -285,13 +305,13 @@ malloc(size_t size)
return NULL; return NULL;
} }
if (chunk->Size() > size + sizeof(free_chunk) + kAlignment) { if (chunk->Size() > size + sizeof(FreeChunk) + kAlignment) {
// if this chunk is bigger than the requested size, // if this chunk is bigger than the requested size,
// we split it to form two chunks (with a minimal // we split it to form two chunks (with a minimal
// size of kAlignment allocatable bytes). // size of kAlignment allocatable bytes).
free_chunk* freeChunk = chunk->Split(size); FreeChunk* freeChunk = chunk->Split(size);
last->next = freeChunk; last->SetNext(freeChunk);
// re-enqueue the free chunk at the correct position // re-enqueue the free chunk at the correct position
freeChunk->Remove(last); freeChunk->Remove(last);
@@ -299,7 +319,7 @@ malloc(size_t size)
} else { } else {
// remove the chunk from the free list // remove the chunk from the free list
last->next = chunk->next; last->SetNext(chunk->Next());
} }
sAvailable -= size + sizeof(uint32); sAvailable -= size + sizeof(uint32);
@@ -320,18 +340,18 @@ realloc(void* oldBuffer, size_t newSize)
size_t copySize = newSize; size_t copySize = newSize;
if (oldBuffer != NULL) { if (oldBuffer != NULL) {
free_chunk* oldChunk = free_chunk::SetToAllocated(oldBuffer); FreeChunk* oldChunk = FreeChunk::SetToAllocated(oldBuffer);
// Check if the old buffer still fits, and if it makes sense to keep it // Check if the old buffer still fits, and if it makes sense to keep it
if (oldChunk->size >= newSize if (oldChunk->Size() >= newSize
&& (oldChunk->size < 128 || newSize > oldChunk->size / 3)) { && (oldChunk->Size() < 128 || newSize > oldChunk->Size() / 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 oldChunk->AllocatedAddress(); return oldChunk->AllocatedAddress();
} }
if (copySize > oldChunk->size) if (copySize > oldChunk->Size())
copySize = oldChunk->size; copySize = oldChunk->Size();
} }
void* newBuffer = malloc(newSize); void* newBuffer = malloc(newSize);
@@ -356,39 +376,41 @@ free(void* allocated)
TRACE("free(%p)\n", allocated); TRACE("free(%p)\n", allocated);
free_chunk* freedChunk = free_chunk::SetToAllocated(allocated); FreeChunk* freedChunk = FreeChunk::SetToAllocated(allocated);
#ifdef DEBUG_ALLOCATIONS #ifdef DEBUG_ALLOCATIONS
if (freedChunk->size > sMaxHeapSize) if (freedChunk->CompleteSize() > sMaxHeapSize) {
panic("freed chunk %p clobbered (%lx)!\n", freedChunk, freedChunk->size); panic("freed chunk %p clobbered (%lx)!\n", freedChunk,
{ freedChunk->Size());
free_chunk* chunk = sFreeAnchor.next; }
while (chunk) { {
if (chunk->size > sMaxHeapSize || freedChunk == chunk) FreeChunk* chunk = sFreeAnchor.Next();
panic("invalid chunk in free list, or double free\n"); while (chunk) {
chunk = chunk->next; if (chunk->CompleteSize() > sMaxHeapSize || freedChunk == chunk)
panic("invalid chunk in free list, or double free\n");
chunk = chunk->Next();
}
} }
}
#endif #endif
sAvailable += freedChunk->size; sAvailable += freedChunk->CompleteSize();
// try to join the new free chunk with an existing one // try to join the new free chunk with an existing one
// it may be joined with up to two chunks // it may be joined with up to two chunks
free_chunk* chunk = sFreeAnchor.next; FreeChunk* chunk = sFreeAnchor.Next();
free_chunk* last = &sFreeAnchor; FreeChunk* last = &sFreeAnchor;
int32 joinCount = 0; int32 joinCount = 0;
while (chunk) { while (chunk) {
if (chunk->IsTouching(freedChunk)) { if (chunk->IsTouching(freedChunk)) {
// almost "insert" it into the list before joining // almost "insert" it into the list before joining
// because the next pointer is inherited by the chunk // because the next pointer is inherited by the chunk
freedChunk->next = chunk->next; freedChunk->SetNext(chunk->Next());
freedChunk = chunk->Join(freedChunk); freedChunk = chunk->Join(freedChunk);
// remove the joined chunk from the list // remove the joined chunk from the list
last->next = freedChunk->next; last->SetNext(freedChunk->Next());
chunk = last; chunk = last;
if (++joinCount == 2) if (++joinCount == 2)
@@ -396,7 +418,7 @@ free(void* allocated)
} }
last = chunk; last = chunk;
chunk = chunk->next; chunk = chunk->Next();
} }
// enqueue the link at the right position; the // enqueue the link at the right position; the