#ifndef _HEAP_H_
#define _HEAP_H_
#include <OS.h>
#include <cstddef>
#include "config.h"
#include "arch-specific.h"
#include "superblock.h"
#include "heapstats.h"
namespace BPrivate {
class processHeap;
class hoardHeap {
public:
hoardHeap(void);
enum { SUPERBLOCK_SIZE = 8192 };
enum { EMPTY_FRACTION = SUPERBLOCK_FULLNESS_GROUP - 1 };
enum { RESET_LEAST_EMPTY_BIN = SUPERBLOCK_FULLNESS_GROUP - 2 };
enum { MAX_EMPTY_SUPERBLOCKS = EMPTY_FRACTION };
#if MAX_INTERNAL_FRAGMENTATION == 2
enum { SIZE_CLASSES = 115 };
#elif MAX_INTERNAL_FRAGMENTATION == 6
enum { SIZE_CLASSES = 46 };
#elif MAX_INTERNAL_FRAGMENTATION == 10
enum { SIZE_CLASSES = 32 };
#else
# error "Undefined size class base."
#endif
enum { ALIGNMENT = HAIKU_MEMORY_ALIGNMENT };
enum { ALIGNMENT_MASK = ALIGNMENT - 1 };
enum { HEAP_MAGIC = 0x0badcafe };
inline void getStats(int sizeclass, int &U, int &A);
#if HEAP_STATS
inline int maxInUse(int sizeclass);
inline int maxAllocated(int sizeclass);
#endif
void insertSuperblock(int sizeclass, superblock *sb, processHeap *pHeap);
superblock *removeMaxSuperblock(int sizeclass);
inline superblock *findAvailableSuperblock(int sizeclass,
block * &b, processHeap * pHeap);
inline void lock(void);
inline void unlock(void);
inline void initLock(void);
inline void setIndex(int i);
inline int getIndex(void);
int freeBlock(block * &b, superblock * &sb, int sizeclass,
processHeap * pHeap);
inline static int sizeClass(const size_t sz);
inline static size_t sizeFromClass(const int sizeclass);
inline static int getReleaseThreshold(const int sizeclass);
inline static int numBlocks(const int sizeclass);
inline static size_t align(const size_t sz);
private:
hoardHeap(const hoardHeap &);
const hoardHeap & operator=(const hoardHeap &);
inline void recycle(superblock *);
inline superblock *reuse(int sizeclass);
void removeSuperblock(superblock *, int sizeclass);
void moveSuperblock(superblock *,
int sizeclass, int fromBin, int toBin);
inline void incStats(int sizeclass, int updateU, int updateA);
inline void incUStats(int sizeclass);
inline void decStats(int sizeclass, int updateU, int updateA);
inline void decUStats(int sizeclass);
heapStats _stats[SIZE_CLASSES];
hoardLockType _lock;
int _index;
superblock *_reusableSuperblocks;
int _reusableSuperblocksCount;
superblock *_superblocks[SUPERBLOCK_FULLNESS_GROUP][SIZE_CLASSES];
int _leastEmptyBin[SIZE_CLASSES];
#if HEAP_DEBUG
const unsigned long _magic;
#else
# define _magic HEAP_MAGIC
#endif
static size_t _sizeTable[SIZE_CLASSES];
static size_t _threshold[SIZE_CLASSES];
public:
static void initNumProcs(void);
protected:
static int fMaxThreadHeaps;
static int _numProcessors;
static int _numProcessorsMask;
};
void
hoardHeap::incStats(int sizeclass, int updateU, int updateA)
{
assert(_magic == HEAP_MAGIC);
assert(updateU >= 0);
assert(updateA >= 0);
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
_stats[sizeclass].incStats(updateU, updateA);
}
void
hoardHeap::incUStats(int sizeclass)
{
assert(_magic == HEAP_MAGIC);
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
_stats[sizeclass].incUStats();
}
void
hoardHeap::decStats(int sizeclass, int updateU, int updateA)
{
assert(_magic == HEAP_MAGIC);
assert(updateU >= 0);
assert(updateA >= 0);
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
_stats[sizeclass].decStats(updateU, updateA);
}
void
hoardHeap::decUStats(int sizeclass)
{
assert(_magic == HEAP_MAGIC);
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
_stats[sizeclass].decUStats();
}
void
hoardHeap::getStats(int sizeclass, int &U, int &A)
{
assert(_magic == HEAP_MAGIC);
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
_stats[sizeclass].getStats(U, A);
}
#if HEAP_STATS
int
hoardHeap::maxInUse(int sizeclass)
{
assert(_magic == HEAP_MAGIC);
return _stats[sizeclass].getUmax();
}
int
hoardHeap::maxAllocated(int sizeclass)
{
assert(_magic == HEAP_MAGIC);
return _stats[sizeclass].getAmax();
}
#endif
superblock *
hoardHeap::findAvailableSuperblock(int sizeclass,
block * &b, processHeap * pHeap)
{
assert(this);
assert(_magic == HEAP_MAGIC);
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
superblock *sb = NULL;
int reUsed = 0;
for (int i = _leastEmptyBin[sizeclass]; i >= 0; i--) {
sb = _superblocks[i][sizeclass];
if (sb == NULL) {
if (i == _leastEmptyBin[sizeclass]) {
_leastEmptyBin[sizeclass]--;
}
} else if (sb->getNumAvailable() > 0) {
assert(sb->getOwner() == this);
break;
}
sb = NULL;
}
#if 1
if (sb == NULL) {
sb = reuse(sizeclass);
if (sb) {
assert(sb->getOwner() == this);
reUsed = 1;
}
}
#endif
if (sb != NULL) {
assert(sb->isValid());
assert(sb->getOwner() == this);
int oldFullness = sb->getFullness();
b = sb->getBlock();
assert(b != NULL);
incUStats(sizeclass);
if (reUsed) {
insertSuperblock(sizeclass, sb, pHeap);
decStats(sizeclass,
sb->getNumBlocks() - sb->getNumAvailable(),
sb->getNumBlocks());
} else {
int fullness = sb->getFullness();
if (fullness != oldFullness) {
moveSuperblock(sb, sizeclass, oldFullness, fullness);
}
}
}
assert((sb == NULL) || (b != NULL));
assert((b == NULL) || (sb != NULL));
return sb;
}
int
hoardHeap::sizeClass(const size_t sz)
{
int sizeclass = 0;
while (_sizeTable[sizeclass] < sz) {
sizeclass++;
assert(sizeclass < SIZE_CLASSES);
}
return sizeclass;
}
size_t
hoardHeap::sizeFromClass(const int sizeclass)
{
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
return _sizeTable[sizeclass];
}
int
hoardHeap::getReleaseThreshold(const int sizeclass)
{
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
return _threshold[sizeclass];
}
int
hoardHeap::numBlocks(const int sizeclass)
{
assert(sizeclass >= 0);
assert(sizeclass < SIZE_CLASSES);
const size_t s = sizeFromClass(sizeclass);
assert(s > 0);
const int blksize = align(sizeof(block) + s);
int nb = max_c(1, ((SUPERBLOCK_SIZE - sizeof(superblock)) / blksize));
return nb;
}
void
hoardHeap::lock(void)
{
assert(_magic == HEAP_MAGIC);
hoardLock(_lock);
}
void
hoardHeap::unlock(void)
{
assert(_magic == HEAP_MAGIC);
hoardUnlock(_lock);
}
void
hoardHeap::initLock(void)
{
hoardLockInit(_lock, "hoard heap");
}
size_t
hoardHeap::align(const size_t sz)
{
return (sz + ALIGNMENT_MASK) & ~ALIGNMENT_MASK;
}
void
hoardHeap::setIndex(int i)
{
_index = i;
}
int
hoardHeap::getIndex(void)
{
return _index;
}
void
hoardHeap::recycle(superblock *sb)
{
assert(sb != NULL);
assert(sb->getOwner() == this);
assert(sb->getNumBlocks() > 1);
assert(sb->getNext() == NULL);
assert(sb->getPrev() == NULL);
assert(hoardHeap::numBlocks(sb->getBlockSizeClass()) > 1);
sb->insertBefore(_reusableSuperblocks);
_reusableSuperblocks = sb;
++_reusableSuperblocksCount;
}
superblock *
hoardHeap::reuse(int sizeclass)
{
if (_reusableSuperblocks == NULL)
return NULL;
if (hoardHeap::numBlocks(sizeclass) <= 1)
return NULL;
assert(_reusableSuperblocksCount > 0);
superblock *sb = _reusableSuperblocks;
_reusableSuperblocks = sb->getNext();
sb->remove();
assert(sb->getNumBlocks() > 1);
--_reusableSuperblocksCount;
if (sb->getBlockSizeClass() != sizeclass) {
decStats(sb->getBlockSizeClass(),
sb->getNumBlocks() - sb->getNumAvailable(),
sb->getNumBlocks());
sb = new((char *)sb) superblock(numBlocks(sizeclass),
sizeclass, this);
incStats(sizeclass,
sb->getNumBlocks() - sb->getNumAvailable(),
sb->getNumBlocks());
}
assert(sb->getOwner() == this);
assert(sb->getBlockSizeClass() == sizeclass);
return sb;
}
}
#endif