ramfs: Drop now-unused Block* classes.
This commit is contained in:
@@ -1,319 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef BLOCK_H
|
|
||||||
#define BLOCK_H
|
|
||||||
|
|
||||||
class Block;
|
|
||||||
class BlockHeader;
|
|
||||||
class BlockReference;
|
|
||||||
class TFreeBlock;
|
|
||||||
|
|
||||||
#include <SupportDefs.h>
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
//#define inline
|
|
||||||
#define BA_DEFINE_INLINES 1
|
|
||||||
|
|
||||||
// BlockHeader
|
|
||||||
class BlockHeader {
|
|
||||||
public:
|
|
||||||
inline Block *ToBlock() { return (Block*)this; }
|
|
||||||
inline TFreeBlock *ToFreeBlock() { return (TFreeBlock*)this; }
|
|
||||||
|
|
||||||
inline void SetPreviousBlock(Block *block);
|
|
||||||
inline Block *GetPreviousBlock();
|
|
||||||
|
|
||||||
inline void SetNextBlock(Block *block);
|
|
||||||
inline Block *GetNextBlock();
|
|
||||||
inline bool HasNextBlock() { return (fSize & HAS_NEXT_FLAG); }
|
|
||||||
|
|
||||||
inline void SetSize(size_t size, bool hasNext = false);
|
|
||||||
inline size_t GetSize() const;
|
|
||||||
static inline size_t GetUsableSizeFor(size_t size);
|
|
||||||
inline size_t GetUsableSize() const;
|
|
||||||
|
|
||||||
inline void *GetData();
|
|
||||||
|
|
||||||
inline void SetFree(bool flag);
|
|
||||||
inline bool IsFree() const;
|
|
||||||
|
|
||||||
inline void SetReference(BlockReference *ref);
|
|
||||||
inline BlockReference *GetReference() const { return fReference; }
|
|
||||||
inline void FixReference();
|
|
||||||
|
|
||||||
inline void SetTo(Block *previous, size_t size, bool isFree, bool hasNext,
|
|
||||||
BlockReference *reference = NULL);
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum {
|
|
||||||
FREE_FLAG = 0x80000000,
|
|
||||||
BACK_SKIP_MASK = 0x7fffffff,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
HAS_NEXT_FLAG = 0x80000000,
|
|
||||||
SIZE_MASK = 0x7fffffff,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
BlockHeader();
|
|
||||||
~BlockHeader();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
size_t fBackSkip;
|
|
||||||
size_t fSize;
|
|
||||||
BlockReference *fReference;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Block
|
|
||||||
class Block : public BlockHeader {
|
|
||||||
public:
|
|
||||||
static inline Block *MakeBlock(void *address, ssize_t offset,
|
|
||||||
Block *previous, size_t size, bool isFree,
|
|
||||||
bool hasNext,
|
|
||||||
BlockReference *reference = NULL);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Block();
|
|
||||||
~Block();
|
|
||||||
};
|
|
||||||
|
|
||||||
// TFreeBlock
|
|
||||||
class TFreeBlock : public Block {
|
|
||||||
public:
|
|
||||||
|
|
||||||
inline void SetPreviousFreeBlock(TFreeBlock *block) { fPrevious = block; }
|
|
||||||
inline void SetNextFreeBlock(TFreeBlock *block) { fNext = block; }
|
|
||||||
inline TFreeBlock *GetPreviousFreeBlock() { return fPrevious; }
|
|
||||||
inline TFreeBlock *GetNextFreeBlock() { return fNext; }
|
|
||||||
|
|
||||||
inline void SetTo(Block *previous, size_t size, bool hasNext,
|
|
||||||
TFreeBlock *previousFree, TFreeBlock *nextFree);
|
|
||||||
|
|
||||||
// static inline TFreeBlock *MakeFreeBlock(void *address, ssize_t offset,
|
|
||||||
// Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree,
|
|
||||||
// TFreeBlock *nextFree);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
TFreeBlock();
|
|
||||||
~TFreeBlock();
|
|
||||||
|
|
||||||
private:
|
|
||||||
TFreeBlock *fPrevious;
|
|
||||||
TFreeBlock *fNext;
|
|
||||||
};
|
|
||||||
|
|
||||||
// BlockReference
|
|
||||||
class BlockReference {
|
|
||||||
public:
|
|
||||||
inline BlockReference() : fBlock(NULL) {}
|
|
||||||
inline BlockReference(Block *block) : fBlock(block) {}
|
|
||||||
|
|
||||||
inline void SetBlock(Block *block) { fBlock = block; }
|
|
||||||
inline Block *GetBlock() const { return fBlock; }
|
|
||||||
|
|
||||||
inline void *GetData() const { return fBlock->GetData(); }
|
|
||||||
inline void *GetDataAt(ssize_t offset) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Block *fBlock;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// inline methods
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
#if BA_DEFINE_INLINES
|
|
||||||
|
|
||||||
// BlockHeader
|
|
||||||
|
|
||||||
// SetPreviousBlock
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::SetPreviousBlock(Block *block)
|
|
||||||
{
|
|
||||||
size_t offset = (block ? (char*)this - (char*)block : 0);
|
|
||||||
fBackSkip = (fBackSkip & FREE_FLAG) | offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPreviousBlock
|
|
||||||
inline
|
|
||||||
Block *
|
|
||||||
BlockHeader::GetPreviousBlock()
|
|
||||||
{
|
|
||||||
if (fBackSkip & BACK_SKIP_MASK)
|
|
||||||
return (Block*)((char*)this - (fBackSkip & BACK_SKIP_MASK));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNextBlock
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::SetNextBlock(Block *block)
|
|
||||||
{
|
|
||||||
if (block)
|
|
||||||
fSize = ((char*)block - (char*)this) | HAS_NEXT_FLAG;
|
|
||||||
else
|
|
||||||
fSize &= SIZE_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNextBlock
|
|
||||||
inline
|
|
||||||
Block *
|
|
||||||
BlockHeader::GetNextBlock()
|
|
||||||
{
|
|
||||||
if (fSize & HAS_NEXT_FLAG)
|
|
||||||
return (Block*)((char*)this + (SIZE_MASK & fSize));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSize
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::SetSize(size_t size, bool hasNext)
|
|
||||||
{
|
|
||||||
fSize = size;
|
|
||||||
if (hasNext)
|
|
||||||
fSize |= HAS_NEXT_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize
|
|
||||||
inline
|
|
||||||
size_t
|
|
||||||
BlockHeader::GetSize() const
|
|
||||||
{
|
|
||||||
return (fSize & SIZE_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsableSizeFor
|
|
||||||
inline
|
|
||||||
size_t
|
|
||||||
BlockHeader::GetUsableSizeFor(size_t size)
|
|
||||||
{
|
|
||||||
return (size - sizeof(BlockHeader));
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsableSize
|
|
||||||
inline
|
|
||||||
size_t
|
|
||||||
BlockHeader::GetUsableSize() const
|
|
||||||
{
|
|
||||||
return GetUsableSizeFor(GetSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetData
|
|
||||||
inline
|
|
||||||
void *
|
|
||||||
BlockHeader::GetData()
|
|
||||||
{
|
|
||||||
return (char*)this + sizeof(BlockHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFree
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::SetFree(bool flag)
|
|
||||||
{
|
|
||||||
if (flag)
|
|
||||||
fBackSkip |= FREE_FLAG;
|
|
||||||
else
|
|
||||||
fBackSkip &= ~FREE_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFree
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
BlockHeader::IsFree() const
|
|
||||||
{
|
|
||||||
return (fBackSkip & FREE_FLAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTo
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::SetTo(Block *previous, size_t size, bool isFree, bool hasNext,
|
|
||||||
BlockReference *reference)
|
|
||||||
{
|
|
||||||
SetPreviousBlock(previous);
|
|
||||||
SetSize(size, hasNext);
|
|
||||||
SetFree(isFree);
|
|
||||||
SetReference(reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetReference
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::SetReference(BlockReference *ref)
|
|
||||||
{
|
|
||||||
fReference = ref;
|
|
||||||
FixReference();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixReference
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockHeader::FixReference()
|
|
||||||
{
|
|
||||||
if (fReference)
|
|
||||||
fReference->SetBlock(ToBlock());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Block
|
|
||||||
|
|
||||||
// MakeBlock
|
|
||||||
/*inline
|
|
||||||
Block *
|
|
||||||
Block::MakeBlock(void *address, ssize_t offset, Block *previous, size_t size,
|
|
||||||
bool isFree, bool hasNext, BlockReference *reference)
|
|
||||||
{
|
|
||||||
Block *block = (Block*)((char*)address + offset);
|
|
||||||
block->SetTo(previous, size, isFree, hasNext, reference);
|
|
||||||
return block;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
// TFreeBlock
|
|
||||||
|
|
||||||
// SetTo
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
TFreeBlock::SetTo(Block *previous, size_t size, bool hasNext,
|
|
||||||
TFreeBlock *previousFree, TFreeBlock *nextFree)
|
|
||||||
{
|
|
||||||
Block::SetTo(previous, size, true, hasNext, NULL);
|
|
||||||
SetPreviousFreeBlock(previousFree);
|
|
||||||
SetNextFreeBlock(nextFree);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeFreeBlock
|
|
||||||
/*inline
|
|
||||||
TFreeBlock *
|
|
||||||
TFreeBlock::MakeFreeBlock(void *address, ssize_t offset, Block *previous,
|
|
||||||
size_t size, bool hasNext, TFreeBlock *previousFree,
|
|
||||||
TFreeBlock *nextFree)
|
|
||||||
{
|
|
||||||
TFreeBlock *block = (TFreeBlock*)((char*)address + offset);
|
|
||||||
block->SetTo(previous, size, hasNext, previousFree, nextFree);
|
|
||||||
if (hasNext)
|
|
||||||
block->GetNextBlock()->SetPreviousBlock(block);
|
|
||||||
return block;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
// BlockReference
|
|
||||||
|
|
||||||
// GetDataAt
|
|
||||||
inline
|
|
||||||
void *
|
|
||||||
BlockReference::GetDataAt(ssize_t offset) const
|
|
||||||
{
|
|
||||||
return (char*)fBlock->GetData() + offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // BA_DEFINE_INLINES
|
|
||||||
|
|
||||||
#endif // BLOCK_H
|
|
||||||
@@ -1,429 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
#define BA_DEFINE_INLINES 1
|
|
||||||
|
|
||||||
#include "AllocationInfo.h"
|
|
||||||
#include "BlockAllocator.h"
|
|
||||||
#include "BlockAllocatorArea.h"
|
|
||||||
#include "BlockAllocatorAreaBucket.h"
|
|
||||||
#include "DebugSupport.h"
|
|
||||||
|
|
||||||
|
|
||||||
// BlockAllocator
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
BlockAllocator::BlockAllocator(size_t areaSize)
|
|
||||||
: fReferenceManager(),
|
|
||||||
fBuckets(NULL),
|
|
||||||
fBucketCount(0),
|
|
||||||
fAreaSize(areaSize),
|
|
||||||
fAreaCount(0),
|
|
||||||
fFreeBytes(0)
|
|
||||||
{
|
|
||||||
// create and init buckets
|
|
||||||
fBucketCount = bucket_containing_size(areaSize) + 1;
|
|
||||||
fBuckets = new(nothrow) AreaBucket[fBucketCount];
|
|
||||||
size_t minSize = 0;
|
|
||||||
for (int32 i = 0; i < fBucketCount; i++) {
|
|
||||||
size_t maxSize = (1 << i) * kMinNetBlockSize;
|
|
||||||
fBuckets[i].SetIndex(i);
|
|
||||||
fBuckets[i].SetSizeLimits(minSize, maxSize);
|
|
||||||
minSize = maxSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
BlockAllocator::~BlockAllocator()
|
|
||||||
{
|
|
||||||
if (fBuckets)
|
|
||||||
delete[] fBuckets;
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitCheck
|
|
||||||
status_t
|
|
||||||
BlockAllocator::InitCheck() const
|
|
||||||
{
|
|
||||||
RETURN_ERROR(fBuckets ? B_OK : B_NO_MEMORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllocateBlock
|
|
||||||
BlockReference *
|
|
||||||
BlockAllocator::AllocateBlock(size_t usableSize)
|
|
||||||
{
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
if (fPanic)
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
//PRINT(("BlockAllocator::AllocateBlock(%lu)\n", usableSize));
|
|
||||||
Block *block = NULL;
|
|
||||||
if (usableSize > 0 && usableSize <= Area::GetMaxFreeBytesFor(fAreaSize)) {
|
|
||||||
// get a block reference
|
|
||||||
BlockReference *reference = fReferenceManager.AllocateReference();
|
|
||||||
if (reference) {
|
|
||||||
block = _AllocateBlock(usableSize);
|
|
||||||
// set reference / cleanup on failure
|
|
||||||
if (block)
|
|
||||||
block->SetReference(reference);
|
|
||||||
else
|
|
||||||
fReferenceManager.FreeReference(reference);
|
|
||||||
}
|
|
||||||
D(SanityCheck(false));
|
|
||||||
}
|
|
||||||
//PRINT(("BlockAllocator::AllocateBlock() done: %p\n", block));
|
|
||||||
return (block ? block->GetReference() : NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FreeBlock
|
|
||||||
void
|
|
||||||
BlockAllocator::FreeBlock(BlockReference *blockReference)
|
|
||||||
{
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
if (fPanic)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
D(if (!CheckBlock(blockReference)) return;);
|
|
||||||
Block *block = (blockReference ? blockReference->GetBlock() : NULL);
|
|
||||||
//PRINT(("BlockAllocator::FreeBlock(%p)\n", block));
|
|
||||||
Area *area = NULL;
|
|
||||||
if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) {
|
|
||||||
_FreeBlock(area, block, true);
|
|
||||||
D(SanityCheck(false));
|
|
||||||
if (_DefragmentingRecommended())
|
|
||||||
_Defragment();
|
|
||||||
}
|
|
||||||
//PRINT(("BlockAllocator::FreeBlock() done\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResizeBlock
|
|
||||||
BlockReference *
|
|
||||||
BlockAllocator::ResizeBlock(BlockReference *blockReference, size_t usableSize)
|
|
||||||
{
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
if (fPanic)
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
D(if (!CheckBlock(blockReference)) return NULL;);
|
|
||||||
//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", blockReference, usableSize));
|
|
||||||
Block *block = (blockReference ? blockReference->GetBlock() : NULL);
|
|
||||||
Block *resultBlock = NULL;
|
|
||||||
Area *area = NULL;
|
|
||||||
if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) {
|
|
||||||
//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", block, usableSize));
|
|
||||||
if (usableSize) {
|
|
||||||
// try to let the area resize the block
|
|
||||||
size_t blockSize = block->GetSize();
|
|
||||||
size_t areaFreeBytes = area->GetFreeBytes();
|
|
||||||
bool needsDefragmenting = area->NeedsDefragmenting();
|
|
||||||
//PRINT((" block reference: %p / %p\n", blockReference, block->GetReference()));
|
|
||||||
resultBlock = area->ResizeBlock(block, usableSize);
|
|
||||||
block = blockReference->GetBlock();
|
|
||||||
if (resultBlock) {
|
|
||||||
//PRINT((" area succeeded in resizing the block\n"));
|
|
||||||
//PRINT((" block reference now: %p\n", resultBlock->GetReference()));
|
|
||||||
// the area was able to resize the block
|
|
||||||
_RethinkAreaBucket(area, area->GetBucket(),
|
|
||||||
needsDefragmenting);
|
|
||||||
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
|
||||||
// Defragment only, if the area was able to resize the block,
|
|
||||||
// the new block is smaller than the old one and defragmenting
|
|
||||||
// is recommended.
|
|
||||||
if (blockSize > resultBlock->GetSize()
|
|
||||||
&& _DefragmentingRecommended()) {
|
|
||||||
_Defragment();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//PRINT((" area failed to resize the block\n"));
|
|
||||||
// the area failed: allocate a new block, copy the data, and
|
|
||||||
// free the old one
|
|
||||||
resultBlock = _AllocateBlock(usableSize);
|
|
||||||
block = blockReference->GetBlock();
|
|
||||||
if (resultBlock) {
|
|
||||||
memcpy(resultBlock->GetData(), block->GetData(),
|
|
||||||
block->GetUsableSize());
|
|
||||||
resultBlock->SetReference(block->GetReference());
|
|
||||||
_FreeBlock(area, block, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
FreeBlock(blockReference);
|
|
||||||
D(SanityCheck(false));
|
|
||||||
//PRINT(("BlockAllocator::ResizeBlock() done: %p\n", resultBlock));
|
|
||||||
}
|
|
||||||
return (resultBlock ? resultBlock->GetReference() : NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SanityCheck
|
|
||||||
bool
|
|
||||||
BlockAllocator::SanityCheck(bool deep) const
|
|
||||||
{
|
|
||||||
// iterate through all areas of all buckets
|
|
||||||
int32 areaCount = 0;
|
|
||||||
size_t freeBytes = 0;
|
|
||||||
for (int32 i = 0; i < fBucketCount; i++) {
|
|
||||||
AreaBucket *bucket = fBuckets + i;
|
|
||||||
if (deep) {
|
|
||||||
if (!bucket->SanityCheck(deep))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (Area *area = bucket->GetFirstArea();
|
|
||||||
area;
|
|
||||||
area = bucket->GetNextArea(area)) {
|
|
||||||
areaCount++;
|
|
||||||
freeBytes += area->GetFreeBytes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// area count
|
|
||||||
if (areaCount != fAreaCount) {
|
|
||||||
FATAL("fAreaCount is %" B_PRId32 ", but should be %" B_PRId32 "\n", fAreaCount,
|
|
||||||
areaCount);
|
|
||||||
BA_PANIC("BlockAllocator: Bad free bytes.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// free bytes
|
|
||||||
if (fFreeBytes != freeBytes) {
|
|
||||||
FATAL("fFreeBytes is %lu, but should be %lu\n", fFreeBytes,
|
|
||||||
freeBytes);
|
|
||||||
BA_PANIC("BlockAllocator: Bad free bytes.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckArea
|
|
||||||
bool
|
|
||||||
BlockAllocator::CheckArea(Area *checkArea)
|
|
||||||
{
|
|
||||||
for (int32 i = 0; i < fBucketCount; i++) {
|
|
||||||
AreaBucket *bucket = fBuckets + i;
|
|
||||||
for (Area *area = bucket->GetFirstArea();
|
|
||||||
area;
|
|
||||||
area = bucket->GetNextArea(area)) {
|
|
||||||
if (area == checkArea)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FATAL("Area %p is not a valid Area!\n", checkArea);
|
|
||||||
BA_PANIC("Invalid Area.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckBlock
|
|
||||||
bool
|
|
||||||
BlockAllocator::CheckBlock(Block *block, size_t minSize)
|
|
||||||
{
|
|
||||||
Area *area = _AreaForBlock(block);
|
|
||||||
return (area/* && CheckArea(area)*/ && area->CheckBlock(block, minSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckBlock
|
|
||||||
bool
|
|
||||||
BlockAllocator::CheckBlock(BlockReference *reference, size_t minSize)
|
|
||||||
{
|
|
||||||
return (fReferenceManager.CheckReference(reference)
|
|
||||||
&& CheckBlock(reference->GetBlock(), minSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllocationInfo
|
|
||||||
void
|
|
||||||
BlockAllocator::GetAllocationInfo(AllocationInfo &info)
|
|
||||||
{
|
|
||||||
fReferenceManager.GetAllocationInfo(info);
|
|
||||||
info.AddOtherAllocation(sizeof(AreaBucket), fBucketCount);
|
|
||||||
info.AddAreaAllocation(fAreaSize, fAreaCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _AreaForBlock
|
|
||||||
inline
|
|
||||||
BlockAllocator::Area *
|
|
||||||
BlockAllocator::_AreaForBlock(Block *block)
|
|
||||||
{
|
|
||||||
Area *area = NULL;
|
|
||||||
area_id id = area_for(block);
|
|
||||||
area_info info;
|
|
||||||
if (id >= 0 && get_area_info(id, &info) == B_OK)
|
|
||||||
area = (Area*)info.address;
|
|
||||||
D(if (!CheckArea(area)) return NULL;);
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _AllocateBlock
|
|
||||||
Block *
|
|
||||||
BlockAllocator::_AllocateBlock(size_t usableSize, bool dontCreateArea)
|
|
||||||
{
|
|
||||||
Block *block = NULL;
|
|
||||||
// Get the last area (the one with the most free space) and try
|
|
||||||
// to let it allocate a block. If that fails, allocate a new area.
|
|
||||||
// find a bucket for the allocation
|
|
||||||
// TODO: optimize
|
|
||||||
AreaBucket *bucket = NULL;
|
|
||||||
int32 index = bucket_containing_min_size(usableSize);
|
|
||||||
for (; index < fBucketCount; index++) {
|
|
||||||
if (!fBuckets[index].IsEmpty()) {
|
|
||||||
bucket = fBuckets + index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// get an area: if we have one, from the bucket, else create a new
|
|
||||||
// area
|
|
||||||
Area *area = NULL;
|
|
||||||
if (bucket)
|
|
||||||
area = bucket->GetFirstArea();
|
|
||||||
else if (!dontCreateArea) {
|
|
||||||
area = Area::Create(fAreaSize);
|
|
||||||
if (area) {
|
|
||||||
fAreaCount++;
|
|
||||||
fFreeBytes += area->GetFreeBytes();
|
|
||||||
bucket = fBuckets + area->GetBucketIndex();
|
|
||||||
bucket->AddArea(area);
|
|
||||||
PRINT("New area allocated. area count now: %" B_PRId32 ", free bytes: %lu\n",
|
|
||||||
fAreaCount, fFreeBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// allocate a block
|
|
||||||
if (area) {
|
|
||||||
size_t areaFreeBytes = area->GetFreeBytes();
|
|
||||||
bool needsDefragmenting = area->NeedsDefragmenting();
|
|
||||||
block = area->AllocateBlock(usableSize);
|
|
||||||
// move the area into another bucket, if necessary
|
|
||||||
if (block) {
|
|
||||||
_RethinkAreaBucket(area, bucket, needsDefragmenting);
|
|
||||||
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
|
||||||
}
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
else if (!fPanic) {
|
|
||||||
FATAL("Block allocation failed unexpectedly.\n");
|
|
||||||
PRINT(" usableSize: %lu, areaFreeBytes: %lu\n", usableSize, areaFreeBytes);
|
|
||||||
BA_PANIC("Block allocation failed unexpectedly.");
|
|
||||||
//block = area->AllocateBlock(usableSize);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _FreeBlock
|
|
||||||
void
|
|
||||||
BlockAllocator::_FreeBlock(Area *area, Block *block, bool freeReference)
|
|
||||||
{
|
|
||||||
size_t areaFreeBytes = area->GetFreeBytes();
|
|
||||||
AreaBucket *bucket = area->GetBucket();
|
|
||||||
bool needsDefragmenting = area->NeedsDefragmenting();
|
|
||||||
// free the block and the block reference
|
|
||||||
BlockReference *reference = block->GetReference();
|
|
||||||
area->FreeBlock(block);
|
|
||||||
if (reference && freeReference)
|
|
||||||
fReferenceManager.FreeReference(reference);
|
|
||||||
// move the area into another bucket, if necessary
|
|
||||||
_RethinkAreaBucket(area, bucket, needsDefragmenting);
|
|
||||||
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _RethinkAreaBucket
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockAllocator::_RethinkAreaBucket(Area *area, AreaBucket *bucket,
|
|
||||||
bool needsDefragmenting)
|
|
||||||
{
|
|
||||||
AreaBucket *newBucket = fBuckets + area->GetBucketIndex();
|
|
||||||
if (newBucket != bucket
|
|
||||||
|| needsDefragmenting != area->NeedsDefragmenting()) {
|
|
||||||
bucket->RemoveArea(area);
|
|
||||||
newBucket->AddArea(area);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _DefragmentingRecommended
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
BlockAllocator::_DefragmentingRecommended()
|
|
||||||
{
|
|
||||||
// Don't know, whether this makes much sense: We don't try to defragment,
|
|
||||||
// when not at least a complete area could be deleted, and some tolerance
|
|
||||||
// being left (a fixed value plus 1/32 of the used bytes).
|
|
||||||
size_t usedBytes = fAreaCount * Area::GetMaxFreeBytesFor(fAreaSize)
|
|
||||||
- fFreeBytes;
|
|
||||||
return (fFreeBytes > fAreaSize + kDefragmentingTolerance + usedBytes / 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// _Defragment
|
|
||||||
bool
|
|
||||||
BlockAllocator::_Defragment()
|
|
||||||
{
|
|
||||||
bool success = false;
|
|
||||||
// We try to empty the least populated area by moving its blocks to other
|
|
||||||
// areas.
|
|
||||||
if (fFreeBytes > fAreaSize) {
|
|
||||||
// find the least populated area
|
|
||||||
// find the bucket with the least populated areas
|
|
||||||
AreaBucket *bucket = NULL;
|
|
||||||
for (int32 i = fBucketCount - 1; i >= 0; i--) {
|
|
||||||
if (!fBuckets[i].IsEmpty()) {
|
|
||||||
bucket = fBuckets + i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// find the area in the bucket
|
|
||||||
Area *area = NULL;
|
|
||||||
if (bucket) {
|
|
||||||
area = bucket->GetFirstArea();
|
|
||||||
Area *bucketArea = area;
|
|
||||||
while ((bucketArea = bucket->GetNextArea(bucketArea)) != NULL) {
|
|
||||||
if (bucketArea->GetFreeBytes() > area->GetFreeBytes())
|
|
||||||
area = bucketArea;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (area) {
|
|
||||||
// remove the area from the bucket
|
|
||||||
bucket->RemoveArea(area);
|
|
||||||
fFreeBytes -= area->GetFreeBytes();
|
|
||||||
// iterate through the blocks in the area and try to find a new
|
|
||||||
// home for them
|
|
||||||
success = true;
|
|
||||||
while (Block *block = area->GetFirstUsedBlock()) {
|
|
||||||
Block *newBlock = _AllocateBlock(block->GetUsableSize(), true);
|
|
||||||
if (newBlock) {
|
|
||||||
// got a new block: copy the data to it and free the old
|
|
||||||
// one
|
|
||||||
memcpy(newBlock->GetData(), block->GetData(),
|
|
||||||
block->GetUsableSize());
|
|
||||||
newBlock->SetReference(block->GetReference());
|
|
||||||
block->SetReference(NULL);
|
|
||||||
area->FreeBlock(block, true);
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
if (fPanic) {
|
|
||||||
PRINT("Panicked while trying to free block %p\n",
|
|
||||||
block);
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delete the area
|
|
||||||
if (success && area->IsEmpty()) {
|
|
||||||
area->Delete();
|
|
||||||
fAreaCount--;
|
|
||||||
PRINT("defragmenting: area deleted\n");
|
|
||||||
} else {
|
|
||||||
PRINT("defragmenting: failed to empty area\n");
|
|
||||||
// failed: re-add the area
|
|
||||||
fFreeBytes += area->GetFreeBytes();
|
|
||||||
AreaBucket *newBucket = fBuckets + area->GetBucketIndex();
|
|
||||||
newBucket->AddArea(area);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
D(SanityCheck(false));
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
bool BlockAllocator::fPanic = false;
|
|
||||||
#endif
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef BLOCK_ALLOCATOR_H
|
|
||||||
#define BLOCK_ALLOCATOR_H
|
|
||||||
|
|
||||||
#include <OS.h>
|
|
||||||
|
|
||||||
#include "Block.h"
|
|
||||||
#include "BlockReferenceManager.h"
|
|
||||||
#include "DebugSupport.h"
|
|
||||||
#include "List.h"
|
|
||||||
|
|
||||||
#define ENABLE_BA_PANIC 1
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
#define BA_PANIC(x) { PANIC(x); BlockAllocator::fPanic = true; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class AllocationInfo;
|
|
||||||
|
|
||||||
// BlockAllocator
|
|
||||||
class BlockAllocator {
|
|
||||||
public:
|
|
||||||
BlockAllocator(size_t areaSize);
|
|
||||||
~BlockAllocator();
|
|
||||||
|
|
||||||
status_t InitCheck() const;
|
|
||||||
|
|
||||||
BlockReference *AllocateBlock(size_t usableSize);
|
|
||||||
void FreeBlock(BlockReference *block);
|
|
||||||
BlockReference *ResizeBlock(BlockReference *block, size_t usableSize);
|
|
||||||
|
|
||||||
size_t GetAvailableBytes() const { return fAreaCount * fAreaSize; }
|
|
||||||
size_t GetFreeBytes() const { return fFreeBytes; }
|
|
||||||
size_t GetUsedBytes() const { return fAreaCount * fAreaSize
|
|
||||||
- fFreeBytes; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Area;
|
|
||||||
class AreaBucket;
|
|
||||||
|
|
||||||
// debugging only
|
|
||||||
bool SanityCheck(bool deep = false) const;
|
|
||||||
bool CheckArea(Area *area);
|
|
||||||
bool CheckBlock(Block *block, size_t minSize = 0);
|
|
||||||
bool CheckBlock(BlockReference *reference, size_t minSize = 0);
|
|
||||||
void GetAllocationInfo(AllocationInfo &info);
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline Area *_AreaForBlock(Block *block);
|
|
||||||
Block *_AllocateBlock(size_t usableSize, bool dontCreateArea = false);
|
|
||||||
void _FreeBlock(Area *area, Block *block, bool freeReference);
|
|
||||||
inline void _RethinkAreaBucket(Area *area, AreaBucket *bucket,
|
|
||||||
bool needsDefragmenting);
|
|
||||||
inline bool _DefragmentingRecommended();
|
|
||||||
bool _Defragment();
|
|
||||||
|
|
||||||
private:
|
|
||||||
BlockReferenceManager fReferenceManager;
|
|
||||||
AreaBucket *fBuckets;
|
|
||||||
int32 fBucketCount;
|
|
||||||
size_t fAreaSize;
|
|
||||||
int32 fAreaCount;
|
|
||||||
size_t fFreeBytes;
|
|
||||||
#if ENABLE_BA_PANIC
|
|
||||||
public:
|
|
||||||
static bool fPanic;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BLOCK_ALLOCATOR_H
|
|
||||||
@@ -1,599 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "BlockAllocatorArea.h"
|
|
||||||
#include "DebugSupport.h"
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
BlockAllocator::Area::Area(area_id id, size_t size)
|
|
||||||
: fBucket(NULL),
|
|
||||||
fID(id),
|
|
||||||
fSize(size),
|
|
||||||
fFreeBytes(0),
|
|
||||||
fFreeBlockCount(1),
|
|
||||||
fUsedBlockCount(0),
|
|
||||||
fFirstBlock(NULL),
|
|
||||||
fLastBlock(NULL),
|
|
||||||
fFirstFree(NULL),
|
|
||||||
fLastFree(NULL)
|
|
||||||
{
|
|
||||||
size_t headerSize = block_align_ceil(sizeof(Area));
|
|
||||||
fFirstFree = (TFreeBlock*)((char*)this + headerSize);
|
|
||||||
fFirstFree->SetTo(NULL, block_align_floor(fSize - headerSize), false, NULL,
|
|
||||||
NULL);
|
|
||||||
fFirstBlock = fLastBlock = fLastFree = fFirstFree;
|
|
||||||
fFreeBytes = fFirstFree->GetUsableSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create
|
|
||||||
BlockAllocator::Area *
|
|
||||||
BlockAllocator::Area::Create(size_t size)
|
|
||||||
{
|
|
||||||
Area *area = NULL;
|
|
||||||
void *base = NULL;
|
|
||||||
#if USER
|
|
||||||
area_id id = create_area("block alloc", &base, B_ANY_ADDRESS,
|
|
||||||
size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
|
||||||
#else
|
|
||||||
area_id id = create_area("block alloc", &base, B_ANY_KERNEL_ADDRESS,
|
|
||||||
size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
|
|
||||||
#endif
|
|
||||||
if (id >= 0) {
|
|
||||||
area = new(base) Area(id, size);
|
|
||||||
} else {
|
|
||||||
ERROR("BlockAllocator::Area::Create(%lu): Failed to create area: %s\n",
|
|
||||||
size, strerror(id));
|
|
||||||
}
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::Delete()
|
|
||||||
{
|
|
||||||
delete_area(fID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllocateBlock
|
|
||||||
Block *
|
|
||||||
BlockAllocator::Area::AllocateBlock(size_t usableSize, bool dontDefragment)
|
|
||||||
{
|
|
||||||
if (kMinBlockSize != block_align_ceil(sizeof(TFreeBlock))) {
|
|
||||||
FATAL("kMinBlockSize is not correctly initialized! Is %lu, but should be: "
|
|
||||||
"%lu\n", kMinBlockSize, block_align_ceil(sizeof(TFreeBlock)));
|
|
||||||
BA_PANIC("kMinBlockSize not correctly initialized.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (usableSize == 0)
|
|
||||||
return NULL;
|
|
||||||
Block *newBlock = NULL;
|
|
||||||
size_t size = max(usableSize + sizeof(BlockHeader), kMinBlockSize);
|
|
||||||
size = block_align_ceil(size);
|
|
||||||
if (size <= _GetBlockFreeBytes()) {
|
|
||||||
// find first fit
|
|
||||||
TFreeBlock *block = _FindFreeBlock(size);
|
|
||||||
if (!block && !dontDefragment) {
|
|
||||||
// defragmenting is necessary
|
|
||||||
_Defragment();
|
|
||||||
block = _FindFreeBlock(size);
|
|
||||||
if (!block) {
|
|
||||||
// no free block
|
|
||||||
// Our data structures seem to be corrupted, since
|
|
||||||
// _GetBlockFreeBytes() promised that we would have enough
|
|
||||||
// free space.
|
|
||||||
FATAL("Couldn't find free block of min size %lu after "
|
|
||||||
"defragmenting, although we should have %lu usable free "
|
|
||||||
"bytes!\n", size, _GetBlockFreeBytes());
|
|
||||||
BA_PANIC("Bad area free bytes.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (block) {
|
|
||||||
// found a free block
|
|
||||||
size_t remainder = block->GetSize() - size;
|
|
||||||
if (remainder >= kMinBlockSize) {
|
|
||||||
// enough space left for a free block
|
|
||||||
Block *freePrev = block->GetPreviousBlock();
|
|
||||||
// TFreeBlock *prevFree = block->GetPreviousFreeBlock();
|
|
||||||
// TFreeBlock *nextFree = block->GetNextFreeBlock();
|
|
||||||
// newBlock = block;
|
|
||||||
_MoveResizeFreeBlock(block, size, remainder);
|
|
||||||
// setup the new block
|
|
||||||
// newBlock->SetSize(size, true);
|
|
||||||
// newBlock->SetFree(false);
|
|
||||||
newBlock = _MakeUsedBlock(block, 0, freePrev, size, true);
|
|
||||||
} else {
|
|
||||||
// not enough space left: take the free block over completely
|
|
||||||
// remove the block from the free list
|
|
||||||
_RemoveFreeBlock(block);
|
|
||||||
newBlock = block;
|
|
||||||
newBlock->SetFree(false);
|
|
||||||
}
|
|
||||||
if (fFreeBlockCount)
|
|
||||||
fFreeBytes -= newBlock->GetSize();
|
|
||||||
else
|
|
||||||
fFreeBytes = 0;
|
|
||||||
fUsedBlockCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
D(SanityCheck());
|
|
||||||
return newBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FreeBlock
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::FreeBlock(Block *block, bool dontDefragment)
|
|
||||||
{
|
|
||||||
if (block) {
|
|
||||||
// mark the block free and insert it into the free list
|
|
||||||
block->SetFree(true);
|
|
||||||
TFreeBlock *freeBlock = (TFreeBlock*)block;
|
|
||||||
_InsertFreeBlock(freeBlock);
|
|
||||||
fUsedBlockCount--;
|
|
||||||
if (fFreeBlockCount == 1)
|
|
||||||
fFreeBytes += freeBlock->GetUsableSize();
|
|
||||||
else
|
|
||||||
fFreeBytes += freeBlock->GetSize();
|
|
||||||
// try coalescing with the next and the previous free block
|
|
||||||
D(SanityCheck());
|
|
||||||
_CoalesceWithNext(freeBlock);
|
|
||||||
D(SanityCheck());
|
|
||||||
_CoalesceWithNext(freeBlock->GetPreviousFreeBlock());
|
|
||||||
// defragment, if sensible
|
|
||||||
if (!dontDefragment && _DefragmentingRecommended())
|
|
||||||
_Defragment();
|
|
||||||
D(SanityCheck());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResizeBlock
|
|
||||||
Block *
|
|
||||||
BlockAllocator::Area::ResizeBlock(Block *block, size_t newUsableSize,
|
|
||||||
bool dontDefragment)
|
|
||||||
{
|
|
||||||
//PRINT(("Area::ResizeBlock(%p, %lu)\n", block, newUsableSize));
|
|
||||||
// newUsableSize must be >0 !
|
|
||||||
if (newUsableSize == 0)
|
|
||||||
return NULL;
|
|
||||||
Block *resultBlock = NULL;
|
|
||||||
if (block) {
|
|
||||||
size_t size = block->GetSize();
|
|
||||||
size_t newSize = max(newUsableSize + sizeof(BlockHeader),
|
|
||||||
kMinBlockSize);
|
|
||||||
newSize = block_align_ceil(newSize);
|
|
||||||
if (newSize == size) {
|
|
||||||
// size doesn't change: nothing to do
|
|
||||||
resultBlock = block;
|
|
||||||
} else if (newSize < size) {
|
|
||||||
// shrink the block
|
|
||||||
size_t sizeDiff = size - newSize;
|
|
||||||
Block *nextBlock = block->GetNextBlock();
|
|
||||||
if (nextBlock && nextBlock->IsFree()) {
|
|
||||||
// join the space with the adjoining free block
|
|
||||||
TFreeBlock *freeBlock = nextBlock->ToFreeBlock();
|
|
||||||
_MoveResizeFreeBlock(freeBlock, -sizeDiff,
|
|
||||||
freeBlock->GetSize() + sizeDiff);
|
|
||||||
// resize the block and we're done
|
|
||||||
block->SetSize(newSize, true);
|
|
||||||
fFreeBytes += sizeDiff;
|
|
||||||
} else if (sizeDiff >= sizeof(TFreeBlock)) {
|
|
||||||
// the freed space is large enough for a free block
|
|
||||||
TFreeBlock *newFree = _MakeFreeBlock(block, newSize, block,
|
|
||||||
sizeDiff, nextBlock, NULL, NULL);
|
|
||||||
_InsertFreeBlock(newFree);
|
|
||||||
block->SetSize(newSize, true);
|
|
||||||
if (fFreeBlockCount == 1)
|
|
||||||
fFreeBytes += newFree->GetUsableSize();
|
|
||||||
else
|
|
||||||
fFreeBytes += newFree->GetSize();
|
|
||||||
if (!dontDefragment && _DefragmentingRecommended())
|
|
||||||
_Defragment();
|
|
||||||
} // else: insufficient space for a free block: no changes
|
|
||||||
resultBlock = block;
|
|
||||||
} else {
|
|
||||||
//PRINT((" grow...\n"));
|
|
||||||
// grow the block
|
|
||||||
size_t sizeDiff = newSize - size;
|
|
||||||
Block *nextBlock = block->GetNextBlock();
|
|
||||||
if (nextBlock && nextBlock->IsFree()
|
|
||||||
&& nextBlock->GetSize() >= sizeDiff) {
|
|
||||||
//PRINT((" adjoining free block\n"));
|
|
||||||
// there is a adjoining free block and it is large enough
|
|
||||||
TFreeBlock *freeBlock = nextBlock->ToFreeBlock();
|
|
||||||
size_t freeSize = freeBlock->GetSize();
|
|
||||||
if (freeSize - sizeDiff >= sizeof(TFreeBlock)) {
|
|
||||||
// the remaining space is still large enough for a free
|
|
||||||
// block
|
|
||||||
_MoveResizeFreeBlock(freeBlock, sizeDiff,
|
|
||||||
freeSize - sizeDiff);
|
|
||||||
block->SetSize(newSize, true);
|
|
||||||
fFreeBytes -= sizeDiff;
|
|
||||||
} else {
|
|
||||||
// the remaining free space wouldn't be large enough for
|
|
||||||
// a free block: consume the free block completely
|
|
||||||
Block *freeNext = freeBlock->GetNextBlock();
|
|
||||||
_RemoveFreeBlock(freeBlock);
|
|
||||||
block->SetSize(size + freeSize, freeNext);
|
|
||||||
_FixBlockList(block, block->GetPreviousBlock(), freeNext);
|
|
||||||
if (fFreeBlockCount == 0)
|
|
||||||
fFreeBytes = 0;
|
|
||||||
else
|
|
||||||
fFreeBytes -= freeSize;
|
|
||||||
}
|
|
||||||
resultBlock = block;
|
|
||||||
} else {
|
|
||||||
//PRINT((" no adjoining free block\n"));
|
|
||||||
// no (large enough) adjoining free block: allocate
|
|
||||||
// a new block and copy the data to it
|
|
||||||
BlockReference *reference = block->GetReference();
|
|
||||||
resultBlock = AllocateBlock(newUsableSize, dontDefragment);
|
|
||||||
block = reference->GetBlock();
|
|
||||||
if (resultBlock) {
|
|
||||||
resultBlock->SetReference(reference);
|
|
||||||
memcpy(resultBlock->GetData(), block->GetData(),
|
|
||||||
block->GetUsableSize());
|
|
||||||
FreeBlock(block, dontDefragment);
|
|
||||||
resultBlock = reference->GetBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
D(SanityCheck());
|
|
||||||
//PRINT(("Area::ResizeBlock() done: %p\n", resultBlock));
|
|
||||||
return resultBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SanityCheck
|
|
||||||
bool
|
|
||||||
BlockAllocator::Area::SanityCheck() const
|
|
||||||
{
|
|
||||||
// area ID
|
|
||||||
if (fID < 0) {
|
|
||||||
FATAL("Area ID < 0: %" B_PRIx32 "\n", fID);
|
|
||||||
BA_PANIC("Bad area ID.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// size
|
|
||||||
size_t areaHeaderSize = block_align_ceil(sizeof(Area));
|
|
||||||
if (fSize < areaHeaderSize + sizeof(TFreeBlock)) {
|
|
||||||
FATAL("Area too small to contain area header and at least one free "
|
|
||||||
"block: %lu bytes\n", fSize);
|
|
||||||
BA_PANIC("Bad area size.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// free bytes
|
|
||||||
if (fFreeBytes > fSize) {
|
|
||||||
FATAL("Free size greater than area size: %lu vs %lu\n", fFreeBytes,
|
|
||||||
fSize);
|
|
||||||
BA_PANIC("Bad area free bytes.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// block count
|
|
||||||
if (fFreeBlockCount + fUsedBlockCount == 0) {
|
|
||||||
FATAL("Area contains no blocks at all.\n");
|
|
||||||
BA_PANIC("Bad area block count.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// block list
|
|
||||||
uint32 usedBlockCount = 0;
|
|
||||||
uint32 freeBlockCount = 0;
|
|
||||||
size_t freeBytes = 0;
|
|
||||||
if (!fFirstBlock || !fLastBlock) {
|
|
||||||
FATAL("Invalid block list: first or last block NULL: first: %p, "
|
|
||||||
"last: %p\n", fFirstBlock, fLastBlock);
|
|
||||||
BA_PANIC("Bad area block list.");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// iterate through block list and also check free list
|
|
||||||
int32 blockCount = fFreeBlockCount + fUsedBlockCount;
|
|
||||||
Block *block = fFirstBlock;
|
|
||||||
Block *prevBlock = NULL;
|
|
||||||
Block *prevFree = NULL;
|
|
||||||
Block *nextFree = fFirstFree;
|
|
||||||
bool blockListOK = true;
|
|
||||||
for (int32 i = 0; i < blockCount; i++) {
|
|
||||||
blockListOK = false;
|
|
||||||
if (!block) {
|
|
||||||
FATAL("Encountered NULL in block list at index %" B_PRId32 ", although "
|
|
||||||
"list should have %" B_PRId32 " blocks\n", i, blockCount);
|
|
||||||
BA_PANIC("Bad area block list.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint64 address = (addr_t)block;
|
|
||||||
// block within area?
|
|
||||||
if (address < (addr_t)this + areaHeaderSize
|
|
||||||
|| address + sizeof(TFreeBlock) > (addr_t)this + fSize) {
|
|
||||||
FATAL("Utterly mislocated block: %p, area: %p, "
|
|
||||||
"size: %lu\n", block, this, fSize);
|
|
||||||
BA_PANIC("Bad area block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// block too large for area?
|
|
||||||
size_t blockSize = block->GetSize();
|
|
||||||
if (blockSize < sizeof(TFreeBlock)
|
|
||||||
|| address + blockSize > (addr_t)this + fSize) {
|
|
||||||
FATAL("Mislocated block: %p, size: %lu, area: %p, "
|
|
||||||
"size: %lu\n", block, blockSize, this, fSize);
|
|
||||||
BA_PANIC("Bad area block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// alignment
|
|
||||||
if (block_align_floor(address) != address
|
|
||||||
|| block_align_floor(blockSize) != blockSize) {
|
|
||||||
FATAL("Block %" B_PRId32 " not properly aligned: %p, size: %lu\n",
|
|
||||||
i, block, blockSize);
|
|
||||||
BA_PANIC("Bad area block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// previous block
|
|
||||||
if (block->GetPreviousBlock() != prevBlock) {
|
|
||||||
FATAL("Previous block of block %" B_PRId32 " was not the previous "
|
|
||||||
"block in list: %p vs %p\n", i,
|
|
||||||
block->GetPreviousBlock(), prevBlock);
|
|
||||||
BA_PANIC("Bad area block list.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// additional checks for free block list
|
|
||||||
if (block->IsFree()) {
|
|
||||||
freeBlockCount++;
|
|
||||||
TFreeBlock *freeBlock = block->ToFreeBlock();
|
|
||||||
if (prevFree)
|
|
||||||
freeBytes += freeBlock->GetSize();
|
|
||||||
else
|
|
||||||
freeBytes += freeBlock->GetUsableSize();
|
|
||||||
// block == next free block of previous free block
|
|
||||||
if (freeBlock != nextFree) {
|
|
||||||
FATAL("Free block %" B_PRId32 " is not the next block in free "
|
|
||||||
"list: %p vs %p\n", i, freeBlock, nextFree);
|
|
||||||
BA_PANIC("Bad area free list.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// previous free block
|
|
||||||
if (freeBlock->GetPreviousFreeBlock() != prevFree) {
|
|
||||||
FATAL("Previous free block of block %" B_PRId32 " was not the "
|
|
||||||
" previous block in free list: %p vs %p\n", i,
|
|
||||||
freeBlock->GetPreviousFreeBlock(), prevFree);
|
|
||||||
BA_PANIC("Bad area free list.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
prevFree = freeBlock;
|
|
||||||
nextFree = freeBlock->GetNextFreeBlock();
|
|
||||||
} else
|
|
||||||
usedBlockCount++;
|
|
||||||
prevBlock = block;
|
|
||||||
block = block->GetNextBlock();
|
|
||||||
blockListOK = true;
|
|
||||||
}
|
|
||||||
// final checks on block list
|
|
||||||
if (blockListOK) {
|
|
||||||
if (block) {
|
|
||||||
FATAL("More blocks in block list than expected\n");
|
|
||||||
BA_PANIC("Bad area block count.");
|
|
||||||
return false;
|
|
||||||
} else if (fLastBlock != prevBlock) {
|
|
||||||
FATAL("last block in block list was %p, but should be "
|
|
||||||
"%p\n", fLastBlock, prevBlock);
|
|
||||||
BA_PANIC("Bad area last block.");
|
|
||||||
return false;
|
|
||||||
} else if (prevFree != fLastFree) {
|
|
||||||
FATAL("last block in free list was %p, but should be %p\n",
|
|
||||||
fLastFree, prevFree);
|
|
||||||
BA_PANIC("Bad area last free block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// block counts (a bit reduntant)
|
|
||||||
if (freeBlockCount != fFreeBlockCount) {
|
|
||||||
FATAL("Free block count is %" B_PRIuSIZE ", but should be %" B_PRIu32 "\n",
|
|
||||||
fFreeBlockCount, freeBlockCount);
|
|
||||||
BA_PANIC("Bad area free block count.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (usedBlockCount != fUsedBlockCount) {
|
|
||||||
FATAL("Used block count is %" B_PRIuSIZE ", but should be %" B_PRIu32 "\n",
|
|
||||||
fUsedBlockCount, usedBlockCount);
|
|
||||||
BA_PANIC("Bad area used block count.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// free bytes
|
|
||||||
if (fFreeBytes != freeBytes) {
|
|
||||||
FATAL("Free bytes is %lu, but should be %lu\n",
|
|
||||||
fFreeBytes, freeBytes);
|
|
||||||
BA_PANIC("Bad area free bytes.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckBlock
|
|
||||||
bool
|
|
||||||
BlockAllocator::Area::CheckBlock(Block *checkBlock, size_t minSize)
|
|
||||||
{
|
|
||||||
for (Block *block = fFirstBlock; block; block = block->GetNextBlock()) {
|
|
||||||
if (block == checkBlock)
|
|
||||||
return (block->GetUsableSize() >= minSize);
|
|
||||||
}
|
|
||||||
FATAL("Block %p is not in area %p!\n", checkBlock, this);
|
|
||||||
BA_PANIC("Invalid Block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _FindFreeBlock
|
|
||||||
TFreeBlock *
|
|
||||||
BlockAllocator::Area::_FindFreeBlock(size_t minSize)
|
|
||||||
{
|
|
||||||
// first fit
|
|
||||||
for (TFreeBlock *block = GetFirstFreeBlock();
|
|
||||||
block;
|
|
||||||
block = block->GetNextFreeBlock()) {
|
|
||||||
if (block->GetSize() >= minSize)
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _InsertFreeBlock
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::_InsertFreeBlock(TFreeBlock *block)
|
|
||||||
{
|
|
||||||
if (block) {
|
|
||||||
// find the free block before which this one has to be inserted
|
|
||||||
TFreeBlock *nextFree = NULL;
|
|
||||||
for (nextFree = GetFirstFreeBlock();
|
|
||||||
nextFree;
|
|
||||||
nextFree = nextFree->GetNextFreeBlock()) {
|
|
||||||
if ((addr_t)nextFree > (addr_t)block)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// get the previous block and insert the block between the two
|
|
||||||
TFreeBlock *prevFree
|
|
||||||
= (nextFree ? nextFree->GetPreviousFreeBlock() : fLastFree);
|
|
||||||
_FixFreeList(block, prevFree, nextFree);
|
|
||||||
fFreeBlockCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _RemoveFreeBlock
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::_RemoveFreeBlock(TFreeBlock *block)
|
|
||||||
{
|
|
||||||
if (block) {
|
|
||||||
TFreeBlock *prevFree = block->GetPreviousFreeBlock();
|
|
||||||
TFreeBlock *nextFree = block->GetNextFreeBlock();
|
|
||||||
if (prevFree)
|
|
||||||
prevFree->SetNextFreeBlock(nextFree);
|
|
||||||
else
|
|
||||||
fFirstFree = nextFree;
|
|
||||||
if (nextFree)
|
|
||||||
nextFree->SetPreviousFreeBlock(prevFree);
|
|
||||||
else
|
|
||||||
fLastFree = prevFree;
|
|
||||||
}
|
|
||||||
fFreeBlockCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _MoveResizeFreeBlock
|
|
||||||
TFreeBlock *
|
|
||||||
BlockAllocator::Area::_MoveResizeFreeBlock(TFreeBlock *freeBlock,
|
|
||||||
ssize_t offset, size_t newSize)
|
|
||||||
{
|
|
||||||
TFreeBlock *movedFree = NULL;
|
|
||||||
if (freeBlock && offset) {
|
|
||||||
// move the header of the free block
|
|
||||||
Block *freePrev = freeBlock->GetPreviousBlock();
|
|
||||||
TFreeBlock *prevFree = freeBlock->GetPreviousFreeBlock();
|
|
||||||
TFreeBlock *nextFree = freeBlock->GetNextFreeBlock();
|
|
||||||
movedFree = _MakeFreeBlock(freeBlock, offset, freePrev, newSize,
|
|
||||||
freeBlock->HasNextBlock(), prevFree, nextFree);
|
|
||||||
// update the free list
|
|
||||||
_FixFreeList(movedFree, prevFree, nextFree);
|
|
||||||
}
|
|
||||||
return movedFree;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _MakeFreeBlock
|
|
||||||
inline
|
|
||||||
TFreeBlock *
|
|
||||||
BlockAllocator::Area::_MakeFreeBlock(void *address, ssize_t offset,
|
|
||||||
Block *previous, size_t size,
|
|
||||||
bool hasNext, TFreeBlock *previousFree,
|
|
||||||
TFreeBlock *nextFree)
|
|
||||||
{
|
|
||||||
TFreeBlock *block = (TFreeBlock*)((char*)address + offset);
|
|
||||||
block->SetTo(previous, size, hasNext, previousFree, nextFree);
|
|
||||||
if (hasNext)
|
|
||||||
block->GetNextBlock()->SetPreviousBlock(block);
|
|
||||||
else
|
|
||||||
fLastBlock = block;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _CoalesceWithNext
|
|
||||||
bool
|
|
||||||
BlockAllocator::Area::_CoalesceWithNext(TFreeBlock *block)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
TFreeBlock *nextFree = NULL;
|
|
||||||
if (block && (nextFree = block->GetNextFreeBlock()) != NULL
|
|
||||||
&& block->GetNextBlock() == nextFree) {
|
|
||||||
_RemoveFreeBlock(nextFree);
|
|
||||||
Block *nextBlock = nextFree->GetNextBlock();
|
|
||||||
block->SetSize(block->GetSize() + nextFree->GetSize(), nextBlock);
|
|
||||||
if (nextBlock)
|
|
||||||
nextBlock->SetPreviousBlock(block);
|
|
||||||
else
|
|
||||||
fLastBlock = block;
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _MakeUsedBlock
|
|
||||||
inline
|
|
||||||
Block *
|
|
||||||
BlockAllocator::Area::_MakeUsedBlock(void *address, ssize_t offset,
|
|
||||||
Block *previous, size_t size,
|
|
||||||
bool hasNext)
|
|
||||||
{
|
|
||||||
Block *block = (Block*)((char*)address + offset);
|
|
||||||
block->SetTo(previous, size, false, hasNext, NULL);
|
|
||||||
if (hasNext)
|
|
||||||
block->GetNextBlock()->SetPreviousBlock(block);
|
|
||||||
else
|
|
||||||
fLastBlock = block;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// _Defragment
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::_Defragment()
|
|
||||||
{
|
|
||||||
D(SanityCheck());
|
|
||||||
//PRINT(("BlockAllocator::Area::_Defragment()\n"));
|
|
||||||
// A trivial strategy for now: Keep the last free block and move the
|
|
||||||
// others so that they can be joined with it. This is done iteratively
|
|
||||||
// by moving the first free block to adjoin to the second one and
|
|
||||||
// coalescing them. A free block is moved by moving the data blocks in
|
|
||||||
// between.
|
|
||||||
TFreeBlock *nextFree = NULL;
|
|
||||||
while (fFirstFree && (nextFree = fFirstFree->GetNextFreeBlock()) != NULL) {
|
|
||||||
Block *prevBlock = fFirstFree->GetPreviousBlock();
|
|
||||||
Block *nextBlock = fFirstFree->GetNextBlock();
|
|
||||||
size_t size = fFirstFree->GetSize();
|
|
||||||
// Used blocks are relatively position independed. We can move them
|
|
||||||
// en bloc and only need to adjust the previous pointer of the first
|
|
||||||
// one.
|
|
||||||
if (!nextBlock->IsFree()) {
|
|
||||||
// move the used blocks
|
|
||||||
size_t chunkSize = (char*)nextFree - (char*)nextBlock;
|
|
||||||
Block *nextFreePrev = nextFree->GetPreviousBlock();
|
|
||||||
Block *movedBlock = fFirstFree;
|
|
||||||
memmove(movedBlock, nextBlock, chunkSize);
|
|
||||||
movedBlock->SetPreviousBlock(prevBlock);
|
|
||||||
// init the first free block
|
|
||||||
Block *movedNextFreePrev = (Block*)((char*)nextFreePrev - size);
|
|
||||||
fFirstFree = _MakeFreeBlock(movedBlock, chunkSize,
|
|
||||||
movedNextFreePrev, size, true, NULL, nextFree);
|
|
||||||
nextFree->SetPreviousFreeBlock(fFirstFree);
|
|
||||||
// fix the references of the moved blocks
|
|
||||||
for (Block *block = movedBlock;
|
|
||||||
block != fFirstFree;
|
|
||||||
block = block->GetNextBlock()) {
|
|
||||||
block->FixReference();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// uncoalesced adjoining free block: That should never happen,
|
|
||||||
// since we always coalesce as early as possible.
|
|
||||||
INFORM(("Warning: Found uncoalesced adjoining free blocks!\n"));
|
|
||||||
}
|
|
||||||
// coalesce the first two blocks
|
|
||||||
D(SanityCheck());
|
|
||||||
_CoalesceWithNext(fFirstFree);
|
|
||||||
D(SanityCheck());
|
|
||||||
}
|
|
||||||
//D(SanityCheck());
|
|
||||||
//PRINT(("BlockAllocator::Area::_Defragment() done\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef BLOCK_ALLOCATOR_AREA_H
|
|
||||||
#define BLOCK_ALLOCATOR_AREA_H
|
|
||||||
|
|
||||||
#include <util/DoublyLinkedList.h>
|
|
||||||
|
|
||||||
#include "BlockAllocator.h"
|
|
||||||
#include "BlockAllocatorMisc.h"
|
|
||||||
|
|
||||||
|
|
||||||
class BlockAllocator::Area : public DoublyLinkedListLinkImpl<Area> {
|
|
||||||
public:
|
|
||||||
static Area *Create(size_t size);
|
|
||||||
void Delete();
|
|
||||||
|
|
||||||
inline void SetBucket(AreaBucket *bucket) { fBucket = bucket; }
|
|
||||||
inline AreaBucket *GetBucket() const { return fBucket; }
|
|
||||||
|
|
||||||
inline Block *GetFirstBlock() const { return fFirstBlock; }
|
|
||||||
inline Block *GetLastBlock() const { return fLastBlock; }
|
|
||||||
|
|
||||||
inline TFreeBlock *GetFirstFreeBlock() const { return fFirstFree; }
|
|
||||||
inline TFreeBlock *GetLastFreeBlock() const { return fLastFree; }
|
|
||||||
|
|
||||||
inline bool IsEmpty() const { return (fUsedBlockCount == 0); }
|
|
||||||
inline Block *GetFirstUsedBlock() const;
|
|
||||||
|
|
||||||
static inline size_t GetMaxFreeBytesFor(size_t areaSize);
|
|
||||||
|
|
||||||
inline size_t GetFreeBytes() const { return fFreeBytes; }
|
|
||||||
inline bool NeedsDefragmenting() const { return (fFreeBlockCount > 1); }
|
|
||||||
|
|
||||||
inline int32 GetBucketIndex();
|
|
||||||
|
|
||||||
Block *AllocateBlock(size_t usableSize, bool dontDefragment = false);
|
|
||||||
void FreeBlock(Block *block, bool dontDefragment = false);
|
|
||||||
Block *ResizeBlock(Block *block, size_t newSize,
|
|
||||||
bool dontDefragment = false);
|
|
||||||
|
|
||||||
// debugging only
|
|
||||||
bool SanityCheck() const;
|
|
||||||
bool CheckBlock(Block *block, size_t minSize = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline size_t _GetBlockFreeBytes()
|
|
||||||
{ return fFreeBytes + sizeof(BlockHeader); }
|
|
||||||
|
|
||||||
Area(area_id id, size_t size);
|
|
||||||
~Area();
|
|
||||||
|
|
||||||
inline void _FixBlockList(Block *block, Block *prevBlock,
|
|
||||||
Block *nextBlock);
|
|
||||||
inline void _FixFreeList(TFreeBlock *block, TFreeBlock *prevFree,
|
|
||||||
TFreeBlock *nextFree);
|
|
||||||
|
|
||||||
TFreeBlock *_FindFreeBlock(size_t minSize);
|
|
||||||
void _InsertFreeBlock(TFreeBlock *block);
|
|
||||||
void _RemoveFreeBlock(TFreeBlock *block);
|
|
||||||
TFreeBlock * _MoveResizeFreeBlock(TFreeBlock *freeBlock, ssize_t offset,
|
|
||||||
size_t newSize);
|
|
||||||
inline TFreeBlock *_MakeFreeBlock(void *address, ssize_t offset,
|
|
||||||
Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree,
|
|
||||||
TFreeBlock *nextFree);
|
|
||||||
bool _CoalesceWithNext(TFreeBlock *block);
|
|
||||||
|
|
||||||
inline Block *_MakeUsedBlock(void *address, ssize_t offset,
|
|
||||||
Block *previous, size_t size, bool hasNext);
|
|
||||||
|
|
||||||
inline bool _DefragmentingRecommended();
|
|
||||||
void _Defragment();
|
|
||||||
|
|
||||||
private:
|
|
||||||
AreaBucket *fBucket;
|
|
||||||
area_id fID;
|
|
||||||
size_t fSize;
|
|
||||||
size_t fFreeBytes;
|
|
||||||
size_t fFreeBlockCount;
|
|
||||||
size_t fUsedBlockCount;
|
|
||||||
Block *fFirstBlock;
|
|
||||||
Block *fLastBlock;
|
|
||||||
TFreeBlock *fFirstFree;
|
|
||||||
TFreeBlock *fLastFree;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef BlockAllocator::Area Area;
|
|
||||||
|
|
||||||
|
|
||||||
// inline methods
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
#if BA_DEFINE_INLINES
|
|
||||||
|
|
||||||
// GetFirstUsedBlock
|
|
||||||
inline
|
|
||||||
Block *
|
|
||||||
BlockAllocator::Area::GetFirstUsedBlock() const
|
|
||||||
{
|
|
||||||
// Two assumptions:
|
|
||||||
// 1) There is always a first block. If that isn't so, our structure are
|
|
||||||
// corrupt.
|
|
||||||
// 2) If the first block is free, the second (if any) is not. Otherwise
|
|
||||||
// there were adjoining free blocks, which our coalescing strategy
|
|
||||||
// prevents.
|
|
||||||
return (fFirstBlock->IsFree() ? fFirstBlock->GetNextBlock() : fFirstBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMaxFreeBytesFor
|
|
||||||
inline
|
|
||||||
size_t
|
|
||||||
BlockAllocator::Area::GetMaxFreeBytesFor(size_t areaSize)
|
|
||||||
{
|
|
||||||
size_t headerSize = block_align_ceil(sizeof(Area));
|
|
||||||
return Block::GetUsableSizeFor(block_align_floor(areaSize - headerSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBucketIndex
|
|
||||||
inline
|
|
||||||
int32
|
|
||||||
BlockAllocator::Area::GetBucketIndex()
|
|
||||||
{
|
|
||||||
return bucket_containing_size(GetFreeBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// _FixBlockList
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::_FixBlockList(Block *block, Block *prevBlock,
|
|
||||||
Block *nextBlock)
|
|
||||||
{
|
|
||||||
if (block) {
|
|
||||||
if (prevBlock)
|
|
||||||
prevBlock->SetNextBlock(block);
|
|
||||||
else
|
|
||||||
fFirstBlock = block;
|
|
||||||
if (nextBlock)
|
|
||||||
nextBlock->SetPreviousBlock(block);
|
|
||||||
else
|
|
||||||
fLastBlock = block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _FixFreeList
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockAllocator::Area::_FixFreeList(TFreeBlock *block, TFreeBlock *prevFree,
|
|
||||||
TFreeBlock *nextFree)
|
|
||||||
{
|
|
||||||
if (block) {
|
|
||||||
if (prevFree)
|
|
||||||
prevFree->SetNextFreeBlock(block);
|
|
||||||
else
|
|
||||||
fFirstFree = block;
|
|
||||||
if (nextFree)
|
|
||||||
nextFree->SetPreviousFreeBlock(block);
|
|
||||||
else
|
|
||||||
fLastFree = block;
|
|
||||||
block->SetPreviousFreeBlock(prevFree);
|
|
||||||
block->SetNextFreeBlock(nextFree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _DefragmentingRecommended
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
BlockAllocator::Area::_DefragmentingRecommended()
|
|
||||||
{
|
|
||||||
// Defragmenting Condition: At least more than 5 free blocks and
|
|
||||||
// free / block ratio greater 1 / 10. Don't know, if that makes any
|
|
||||||
// sense. ;-)
|
|
||||||
return (fFreeBlockCount > 5 && fUsedBlockCount / fFreeBlockCount < 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // BA_DEFINE_INLINES
|
|
||||||
|
|
||||||
|
|
||||||
#endif // BLOCK_ALLOCATOR_AREA_H
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#include "BlockAllocatorAreaBucket.h"
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
BlockAllocator::AreaBucket::AreaBucket()
|
|
||||||
: fAreas(),
|
|
||||||
fIndex(-1),
|
|
||||||
fMinSize(0),
|
|
||||||
fMaxSize(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
BlockAllocator::AreaBucket::~AreaBucket()
|
|
||||||
{
|
|
||||||
while (Area *area = fAreas.First()) {
|
|
||||||
RemoveArea(area);
|
|
||||||
area->Delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SanityCheck
|
|
||||||
bool
|
|
||||||
BlockAllocator::AreaBucket::SanityCheck(bool deep) const
|
|
||||||
{
|
|
||||||
// check area list
|
|
||||||
for (Area *area = GetFirstArea(); area; area = GetNextArea(area)) {
|
|
||||||
if (deep) {
|
|
||||||
if (!area->SanityCheck())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// bucket
|
|
||||||
if (area->GetBucket() != this) {
|
|
||||||
FATAL("Area %p is in bucket %p, but thinks it is in bucket %p\n",
|
|
||||||
area, this, area->GetBucket());
|
|
||||||
BA_PANIC("Wrong area bucket.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// size
|
|
||||||
size_t areaSize = area->GetFreeBytes();
|
|
||||||
if (areaSize < fMinSize || areaSize >= fMaxSize) {
|
|
||||||
FATAL("Area is in wrong bucket: free: %lu, min: %lu, max: %lu\n",
|
|
||||||
areaSize, fMinSize, fMaxSize);
|
|
||||||
BA_PANIC("Area in wrong bucket.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef BLOCK_ALLOCATOR_AREA_BUCKET_H
|
|
||||||
#define BLOCK_ALLOCATOR_AREA_BUCKET_H
|
|
||||||
|
|
||||||
#include <util/DoublyLinkedList.h>
|
|
||||||
|
|
||||||
#include "BlockAllocator.h"
|
|
||||||
#include "BlockAllocatorArea.h"
|
|
||||||
#include "DebugSupport.h"
|
|
||||||
|
|
||||||
|
|
||||||
class BlockAllocator::AreaBucket {
|
|
||||||
public:
|
|
||||||
AreaBucket();
|
|
||||||
~AreaBucket();
|
|
||||||
|
|
||||||
inline void SetIndex(int32 index) { fIndex = index; }
|
|
||||||
inline int32 GetIndex() const { return fIndex; }
|
|
||||||
|
|
||||||
inline void SetSizeLimits(size_t minSize, size_t maxSize);
|
|
||||||
inline size_t GetMinSize() const { return fMinSize; } // incl.
|
|
||||||
inline size_t GetMaxSize() const { return fMaxSize; } // excl.
|
|
||||||
|
|
||||||
inline void AddArea(Area *area);
|
|
||||||
inline void RemoveArea(Area *area);
|
|
||||||
|
|
||||||
inline Area *GetFirstArea() const { return fAreas.First(); }
|
|
||||||
inline Area *GetLastArea() const { return fAreas.Last(); }
|
|
||||||
inline Area *GetNextArea(Area* area) const;
|
|
||||||
|
|
||||||
inline bool IsEmpty() const { return fAreas.IsEmpty(); }
|
|
||||||
|
|
||||||
// debugging only
|
|
||||||
bool SanityCheck(bool deep = false) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
DoublyLinkedList<Area> fAreas;
|
|
||||||
int32 fIndex;
|
|
||||||
size_t fMinSize;
|
|
||||||
size_t fMaxSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef BlockAllocator::AreaBucket AreaBucket;
|
|
||||||
|
|
||||||
|
|
||||||
// inline methods
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
#if BA_DEFINE_INLINES
|
|
||||||
|
|
||||||
// SetSizeLimits
|
|
||||||
/*! \brief Sets the size limits for areas this bucket may contain.
|
|
||||||
\param minSize Minimal area size. Inclusively.
|
|
||||||
\param maxSize Maximal area size. Exlusively.
|
|
||||||
*/
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockAllocator::AreaBucket::SetSizeLimits(size_t minSize, size_t maxSize)
|
|
||||||
{
|
|
||||||
fMinSize = minSize;
|
|
||||||
fMaxSize = maxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddArea
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockAllocator::AreaBucket::AddArea(Area *area)
|
|
||||||
{
|
|
||||||
if (area) {
|
|
||||||
fAreas.Insert(area, area->NeedsDefragmenting());
|
|
||||||
area->SetBucket(this);
|
|
||||||
D(SanityCheck(false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveArea
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
BlockAllocator::AreaBucket::RemoveArea(Area *area)
|
|
||||||
{
|
|
||||||
if (area) {
|
|
||||||
fAreas.Remove(area);
|
|
||||||
area->SetBucket(NULL);
|
|
||||||
D(SanityCheck(false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNextArea
|
|
||||||
inline
|
|
||||||
Area *
|
|
||||||
BlockAllocator::AreaBucket::GetNextArea(Area* area) const
|
|
||||||
{
|
|
||||||
return fAreas.GetNext(area);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // BA_DEFINE_INLINES
|
|
||||||
|
|
||||||
#endif // BLOCK_ALLOCATOR_AREA_BUCKET_H
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef BLOCK_ALLOCATOR_MISC_H
|
|
||||||
#define BLOCK_ALLOCATOR_MISC_H
|
|
||||||
|
|
||||||
#include "Block.h"
|
|
||||||
#include "Misc.h"
|
|
||||||
|
|
||||||
// block alignment -- start offsets and size
|
|
||||||
static const size_t kBlockAlignment = 4; // must be a power of 2
|
|
||||||
|
|
||||||
// block_align_{floor,ceil}
|
|
||||||
static inline size_t block_align_floor(size_t value)
|
|
||||||
{ return value & ~(kBlockAlignment - 1); }
|
|
||||||
static inline size_t block_align_ceil(size_t value)
|
|
||||||
{ return (value + kBlockAlignment - 1) & ~(kBlockAlignment - 1); }
|
|
||||||
|
|
||||||
// minimal size of a gross/net block
|
|
||||||
// BAD DOG: No initializers in the kernel!
|
|
||||||
//static const size_t kMinBlockSize = block_align_ceil(sizeof(TFreeBlock));
|
|
||||||
#define kMinBlockSize (block_align_ceil(sizeof(TFreeBlock)))
|
|
||||||
static const size_t kMinNetBlockSize = 8;
|
|
||||||
|
|
||||||
static const size_t kDefragmentingTolerance = 10240;
|
|
||||||
|
|
||||||
// bucket_containing_size -- bucket for to contain an area with size
|
|
||||||
static inline int bucket_containing_size(size_t size)
|
|
||||||
{ return fls(size / kMinNetBlockSize) + 1; }
|
|
||||||
|
|
||||||
// bucket_containing_min_size -- bucket containing areas >= size
|
|
||||||
static inline int
|
|
||||||
bucket_containing_min_size(size_t size)
|
|
||||||
{ return (size ? bucket_containing_size(size - 1) + 1 : 0); }
|
|
||||||
|
|
||||||
|
|
||||||
#endif // BLOCK_ALLOCATOR_MISC_H
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "AllocationInfo.h"
|
|
||||||
#include "Block.h"
|
|
||||||
#include "BlockAllocator.h" // only for BA_PANIC
|
|
||||||
#include "BlockReferenceManager.h"
|
|
||||||
#include "DebugSupport.h"
|
|
||||||
|
|
||||||
static const int kBlockReferenceTableSize = 128;
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
BlockReferenceManager::BlockReferenceManager()
|
|
||||||
: fTables(10),
|
|
||||||
fFreeList(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
BlockReferenceManager::~BlockReferenceManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllocateReference
|
|
||||||
BlockReference *
|
|
||||||
BlockReferenceManager::AllocateReference()
|
|
||||||
{
|
|
||||||
BlockReference *reference = NULL;
|
|
||||||
if (!fFreeList)
|
|
||||||
_AddTable();
|
|
||||||
if (fFreeList) {
|
|
||||||
reference = fFreeList;
|
|
||||||
fFreeList = *(BlockReference**)fFreeList;
|
|
||||||
}
|
|
||||||
return reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FreeReference
|
|
||||||
void
|
|
||||||
BlockReferenceManager::FreeReference(BlockReference *reference)
|
|
||||||
{
|
|
||||||
if (reference) {
|
|
||||||
*(BlockReference**)reference = fFreeList;
|
|
||||||
fFreeList = reference;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckReference
|
|
||||||
bool
|
|
||||||
BlockReferenceManager::CheckReference(BlockReference *reference)
|
|
||||||
{
|
|
||||||
if (reference) {
|
|
||||||
uint32 address = (addr_t)reference;
|
|
||||||
int32 tableCount = fTables.CountItems();
|
|
||||||
for (int32 i = 0; i < tableCount; i++) {
|
|
||||||
Table *table = &fTables.ItemAt(i);
|
|
||||||
uint32 first = (addr_t)table->GetReferences();
|
|
||||||
uint32 last = (addr_t)(table->GetReferences() + table->GetSize());
|
|
||||||
if (first <= address && address < last)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FATAL("BlockReference %p does not exist!\n", reference);
|
|
||||||
BA_PANIC("BlockReference doesn't exist.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllocationInfo
|
|
||||||
void
|
|
||||||
BlockReferenceManager::GetAllocationInfo(AllocationInfo &info)
|
|
||||||
{
|
|
||||||
info.AddListAllocation(fTables.GetCapacity(), sizeof(Table));
|
|
||||||
int32 count = fTables.CountItems();
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
Table &table = fTables.ItemAt(i);
|
|
||||||
info.AddOtherAllocation(table.GetSize() * sizeof(BlockReference));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _AddTable
|
|
||||||
status_t
|
|
||||||
BlockReferenceManager::_AddTable()
|
|
||||||
{
|
|
||||||
status_t error = B_OK;
|
|
||||||
// add a new table
|
|
||||||
Table dummy;
|
|
||||||
if (fTables.AddItem(dummy)) {
|
|
||||||
int32 index = fTables.CountItems() - 1;
|
|
||||||
Table &table = fTables.ItemAt(index);
|
|
||||||
error = table.Init(kBlockReferenceTableSize);
|
|
||||||
if (error == B_OK) {
|
|
||||||
// add the references to the free list
|
|
||||||
uint32 count = table.GetSize();
|
|
||||||
BlockReference *references = table.GetReferences();
|
|
||||||
for (uint32 i = 0; i < count; i++) {
|
|
||||||
BlockReference *reference = references + i;
|
|
||||||
*(BlockReference**)reference = fFreeList;
|
|
||||||
fFreeList = reference;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
fTables.RemoveItem(index);
|
|
||||||
} else
|
|
||||||
SET_ERROR(error, B_NO_MEMORY);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Table
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
BlockReferenceManager::Table::~Table()
|
|
||||||
{
|
|
||||||
if (fReferences)
|
|
||||||
delete[] fReferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init
|
|
||||||
status_t
|
|
||||||
BlockReferenceManager::Table::Init(int32 size)
|
|
||||||
{
|
|
||||||
status_t error = (size > 0 ? B_OK : B_BAD_VALUE);
|
|
||||||
if (error == B_OK) {
|
|
||||||
fReferences = new(std::nothrow) BlockReference[size];
|
|
||||||
if (fReferences)
|
|
||||||
fSize = size;
|
|
||||||
else
|
|
||||||
SET_ERROR(error, B_NO_MEMORY);
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
|
||||||
* All rights reserved. Distributed under the terms of the MIT license.
|
|
||||||
*/
|
|
||||||
#ifndef BLOCK_REFERENCE_MANAGER_H
|
|
||||||
#define BLOCK_REFERENCE_MANAGER_H
|
|
||||||
|
|
||||||
#include <new>
|
|
||||||
#include "List.h"
|
|
||||||
|
|
||||||
class AllocationInfo;
|
|
||||||
class BlockReference;
|
|
||||||
|
|
||||||
class BlockReferenceManager {
|
|
||||||
public:
|
|
||||||
BlockReferenceManager();
|
|
||||||
~BlockReferenceManager();
|
|
||||||
|
|
||||||
BlockReference *AllocateReference();
|
|
||||||
void FreeReference(BlockReference *reference);
|
|
||||||
|
|
||||||
// debugging only
|
|
||||||
bool CheckReference(BlockReference *reference);
|
|
||||||
void GetAllocationInfo(AllocationInfo &info);
|
|
||||||
|
|
||||||
private:
|
|
||||||
status_t _AddTable();
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Table {
|
|
||||||
public:
|
|
||||||
Table() : fSize(0), fReferences(NULL) {}
|
|
||||||
Table(int) : fSize(0), fReferences(NULL) {}
|
|
||||||
~Table();
|
|
||||||
|
|
||||||
status_t Init(int32 size);
|
|
||||||
|
|
||||||
BlockReference *GetReferences() { return fReferences; }
|
|
||||||
|
|
||||||
int32 GetSize() const { return fSize; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32 fSize;
|
|
||||||
BlockReference *fReferences;
|
|
||||||
};
|
|
||||||
|
|
||||||
List<Table> fTables;
|
|
||||||
BlockReference *fFreeList;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BLOCK_REFERENCE_MANAGER_H
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user