#ifndef HOARD_HOARDMANAGER_H
#define HOARD_HOARDMANAGER_H
#include <cstdlib>
#include <new>
#include <mutex>
#include "statistics.h"
#include "emptyclass.h"
#include "array.h"
#include "manageonesuperblock.h"
#include "basehoardmanager.h"
#include "emptyhoardmanager.h"
#include "heaplayers.h"
using namespace HL;
namespace Hoard {
template <class SourceHeap,
class ParentHeap,
class SuperblockType_,
int EmptinessClasses,
class LockType,
class thresholdFunctionClass,
class HeapType>
class HoardManager : public BaseHoardManager<SuperblockType_>,
public thresholdFunctionClass
{
public:
HoardManager()
: _magic (MAGIC_NUMBER),
_cachedSize (binType::getClassSize(0)),
_cachedRealSize (_cachedSize),
_cachedSizeClass (0)
{}
virtual ~HoardManager() {}
typedef SuperblockType_ SuperblockType;
enum { Alignment = SuperblockType::Header::Alignment };
MALLOC_FUNCTION INLINE void * malloc (size_t sz)
{
Check<HoardManager, sanityCheck> check (this);
int binIndex;
size_t realSize;
if (false) { binIndex = _cachedSizeClass;
realSize = _cachedRealSize;
} else {
binIndex = binType::getSizeClass(sz);
realSize = binType::getClassSize (binIndex);
_cachedSize = sz;
_cachedSizeClass = binIndex;
_cachedRealSize = realSize;
}
assert (realSize >= sz);
auto ptr = getObject (binIndex, realSize);
if (!ptr) {
ptr = slowPathMalloc (realSize);
}
assert (SuperHeap::getSize(ptr) >= sz);
assert ((size_t) ptr % Alignment == 0);
return ptr;
}
NO_INLINE void put (SuperblockType * s, size_t sz) {
std::lock_guard<LockType> l (_theLock);
assert (s->getOwner() != this);
Check<HoardManager, sanityCheck> check (this);
const auto binIndex = binType::getSizeClass(sz);
auto& stats = _stats(binIndex);
auto a = stats.getAllocated() + s->getTotalObjects();
auto u = stats.getInUse() + (s->getTotalObjects() - s->getObjectsFree());
if (thresholdFunctionClass::function (u, a, sz)) {
_ph.put (reinterpret_cast<typename ParentHeap::SuperblockType *>(s), sz);
} else {
unlocked_put (s, sz);
}
}
NO_INLINE SuperblockType * get (size_t sz, HeapType * dest) {
std::lock_guard<LockType> l (_theLock);
Check<HoardManager, sanityCheck> check (this);
const auto binIndex = binType::getSizeClass (sz);
auto * s = _otherBins(binIndex).get();
if (s) {
assert (s->isValidSuperblock());
decStatsSuperblock (s, binIndex);
s->setOwner (dest);
}
return s;
}
INLINE void free (void * ptr) {
Check<HoardManager, sanityCheck> check (this);
SuperblockType * s = SuperHeap::getSuperblock (ptr);
assert (s->getOwner() == this);
assert (s->normalize (ptr) == ptr);
auto sz = s->getObjectSize ();
auto binIndex = (int) binType::getSizeClass (sz);
_otherBins(binIndex).free (ptr);
auto& stats = _stats(binIndex);
auto u = stats.getInUse();
auto a = stats.getAllocated();
u--;
stats.setInUse (u);
if (thresholdFunctionClass::function (u, a, sz)) {
slowPathFree (binIndex, u, a);
}
}
INLINE void lock() {
_theLock.lock();
}
INLINE void unlock() {
_theLock.unlock();
}
private:
typedef BaseHoardManager<SuperblockType_> SuperHeap;
enum { SuperblockSize = sizeof(SuperblockType_) };
static_assert((SuperblockSize & (SuperblockSize-1)) == 0,
"Superblock size must be a power of two.");
enum { MAGIC_NUMBER = 0xfeeddadd };
const unsigned long _magic;
size_t _cachedSize;
size_t _cachedRealSize;
int _cachedSizeClass;
inline int isValid() const {
return (_magic == MAGIC_NUMBER);
}
static_assert(sizeof(typename SuperblockType::Header) % sizeof(double) == 0,
"Header size must be a multiple of the size of a double.");
typedef HL::bins<typename SuperblockType::Header, SuperblockSize> binType;
enum { NumBins = binType::NUM_BINS };
NO_INLINE void slowPathFree (int binIndex, unsigned int u, unsigned int a) {
Check<HoardManager, sanityCheck> check (this);
SuperblockType * sb = _otherBins(binIndex).get ();
assert (sb);
if (sb) {
auto sz = binType::getClassSize (binIndex);
auto& stats = _stats(binIndex);
auto totalObjects = sb->getTotalObjects();
stats.setInUse (u - (totalObjects - sb->getObjectsFree()));
stats.setAllocated (a - totalObjects);
_ph.put (reinterpret_cast<typename ParentHeap::SuperblockType *>(sb), sz);
assert (sb->isValidSuperblock());
}
}
NO_INLINE void unlocked_put (SuperblockType * s, size_t sz) {
if (!s || !s->isValidSuperblock()) {
return;
}
Check<HoardManager, sanityCheck> check (this);
const auto binIndex = binType::getSizeClass(sz);
s->setOwner (reinterpret_cast<HeapType *>(this));
_otherBins(binIndex).put (s);
addStatsSuperblock (s, binIndex);
assert (s->isValidSuperblock());
}
void addStatsSuperblock (SuperblockType * s, int binIndex) {
auto& stats = _stats(binIndex);
auto a = stats.getAllocated();
auto u = stats.getInUse();
auto totalObjects = s->getTotalObjects();
stats.setInUse (u + (totalObjects - s->getObjectsFree()));
stats.setAllocated (a + totalObjects);
}
void decStatsSuperblock (SuperblockType * s, int binIndex) {
auto& stats = _stats(binIndex);
auto a = stats.getAllocated();
auto u = stats.getInUse();
auto totalObjects = s->getTotalObjects();
stats.setInUse (u - (totalObjects - s->getObjectsFree()));
stats.setAllocated (a - totalObjects);
}
MALLOC_FUNCTION NO_INLINE void * slowPathMalloc (size_t sz) {
auto binIndex = binType::getSizeClass (sz);
auto realSize = binType::getClassSize (binIndex);
assert (realSize >= sz);
for (;;) {
Check<HoardManager, sanityCheck> check1 (this);
auto * ptr = getObject (binIndex, realSize);
if (ptr) {
return ptr;
} else {
Check<HoardManager, sanityCheck> check2 (this);
if (!getAnotherSuperblock (realSize)) {
return 0;
}
}
}
}
MALLOC_FUNCTION INLINE void * getObject (int binIndex,
size_t sz) {
Check<HoardManager, sanityCheck> check (this);
void * ptr = _otherBins(binIndex).malloc (sz);
if (ptr) {
auto u = _stats(binIndex).getInUse();
_stats(binIndex).setInUse (u+1);
}
return ptr;
}
friend class sanityCheck;
class sanityCheck {
public:
inline static void precondition (HoardManager * h) {
checkInvariant(h);
}
inline static void postcondition (HoardManager * h) {
checkInvariant(h);
}
private:
inline static void checkInvariant (HoardManager * h) {
(void) h;
assert (h->isValid());
}
};
private:
NO_INLINE void * getAnotherSuperblock (size_t sz) {
auto * sb = reinterpret_cast<SuperblockType *>(_ph.get (sz, reinterpret_cast<ParentHeap *>(this)));
if (sb) {
if (!sb->isValidSuperblock()) {
sb = nullptr;
}
} else {
void * ptr = _sourceHeap.malloc (SuperblockSize);
if (!ptr) {
return 0;
}
sb = new (ptr) SuperblockType (sz);
}
if (sb) {
unlocked_put (sb, sz);
}
return sb;
}
LockType _theLock;
Array<NumBins, Statistics> _stats;
typedef SuperblockType * SuperblockTypePointer;
typedef EmptyClass<SuperblockType, EmptinessClasses> OrganizedByEmptiness;
typedef ManageOneSuperblock<OrganizedByEmptiness> BinManager;
Array<NumBins, BinManager> _otherBins;
ParentHeap _ph;
SourceHeap _sourceHeap;
};
}
#endif