Files
Alex Harker dfe6a8361b Whitespace
2019-08-12 12:15:41 +01:00

422 lines
10 KiB
Objective-C

#ifndef FRAMELIB_MEMORY_H
#define FRAMELIB_MEMORY_H
#include "../FrameLib_Dependencies/tlsf/tlsf.h"
#include "FrameLib_Types.h"
#include "FrameLib_Errors.h"
#include "FrameLib_Parameters.h"
#include "FrameLib_Threading.h"
#include <vector>
#include <ctime>
#include <string>
/**
@defgroup Memory
*/
/**
@class FrameLib_GlobalAllocator
@ingroup Memory
@brief a global threadsafe memory allocator suitable for realtime usage.
*/
class FrameLib_GlobalAllocator
{
private:
/**
@class CoreAllocator
@brief the underlying single-threaded allocator.
*/
class CoreAllocator
{
/**
@struct Pool
@brief a memory pool from system memory.
*/
struct Pool
{
Pool(void *mem, size_t size) : mUsedRecently(true), mTime(0), mSize(size), mPrev(nullptr), mNext(nullptr), mMem(mem) {}
bool isFree() { return tlsf_pool_is_free(mMem); }
bool mUsedRecently;
time_t mTime;
size_t mSize;
Pool *mPrev;
Pool *mNext;
void *mMem;
};
/**
@class NewThread
@brief a thread for allocating new memory pools from system memory.
*/
class NewThread final : public FrameLib_DelegateThread
{
public:
NewThread(CoreAllocator& allocator) : FrameLib_DelegateThread(FrameLib_Thread::kHighPriority), mAllocator(allocator) {}
private:
void doTask() override { mAllocator.addScheduledPool(); };
CoreAllocator& mAllocator;
};
/**
@class FreeThread
@brief a thread for freeing memory pools back to system memory.
*/
class FreeThread final : public FrameLib_TriggerableThread
{
public:
FreeThread(CoreAllocator& allocator) : FrameLib_TriggerableThread(FrameLib_Thread::kLowPriority), mAllocator(allocator) {}
private:
void doTask() override { mAllocator.destroyScheduledPool(); };
CoreAllocator& mAllocator;
};
public:
CoreAllocator(FrameLib_ErrorReporter& errorReporter);
~CoreAllocator();
// Allocate and deallocate memory (plus pruning)
void *alloc(size_t size);
void dealloc(void *ptr);
void prune();
private:
// Get a Pool Class a tlsf pool_t
Pool *getPool(pool_t pool);
// Pool Helpers
static Pool *createPool(size_t size);
static void destroyPool(Pool *pool);
void linkPool(Pool *pool);
void unlinkPool(Pool *pool);
void poolToTop(Pool *pool);
void insertPool(Pool *pool);
void removePool(Pool *pool);
// Scheduled creation/deletion
void addScheduledPool();
void destroyScheduledPool();
// Member Variables
tlsf_t mTLSF;
Pool *mPools;
size_t mOSAllocated;
size_t mAllocated;
size_t mLastDisposedPoolSize;
std::atomic<Pool *> mScheduledNewPool;
std::atomic<Pool *> mScheduledDisposePool;
NewThread mAllocThread;
FreeThread mFreeThread;
FrameLib_ErrorReporter& mErrorReporter;
};
public:
/**
@class Pruner
@brief an RAII utility for repeated deallocation with only a single lock.
*/
class Pruner
{
public:
Pruner(FrameLib_GlobalAllocator& allocator) : mAllocator(allocator)
{
mAllocator.mLock.acquire();
}
~Pruner()
{
mAllocator.mAllocator.prune();
mAllocator.mLock.release();
}
// Non-copyable
Pruner(const Pruner&) = delete;
Pruner& operator=(const Pruner&) = delete;
void dealloc(void *ptr) { mAllocator.mAllocator.dealloc(ptr); }
private:
// Allocator
FrameLib_GlobalAllocator& mAllocator;
};
// Constructor / Destructor
FrameLib_GlobalAllocator(FrameLib_ErrorReporter& errorReporter) : mAllocator(errorReporter) {}
~FrameLib_GlobalAllocator() {}
// Non-copyable
FrameLib_GlobalAllocator(const FrameLib_GlobalAllocator&) = delete;
FrameLib_GlobalAllocator& operator=(const FrameLib_GlobalAllocator&) = delete;
// Allocate / Deallocate Memory
void *alloc(size_t size);
void dealloc(void *ptr);
// Alignment Helpers
static size_t getAlignment();
static size_t alignSize(size_t x);
private:
// Member Variables
FrameLib_SpinLock mLock;
CoreAllocator mAllocator;
};
/**
@class FrameLib_LocalAllocator
@ingroup Memory
@brief a memory allocator suitable for usage in a given FrameLib context.
@sa FrameLib_Context
*/
class FrameLib_LocalAllocator
{
static const int numLocalFreeBlocks = 16;
/**
@struct FreeBlock
@brief a memory block that can be addressed as part of double-linked list.
*/
struct FreeBlock
{
FreeBlock() : mMemory(nullptr), mSize(0), mPrev(nullptr), mNext(nullptr) {}
void *mMemory;
size_t mSize;
FreeBlock *mPrev;
FreeBlock *mNext;
};
public:
/**
@class Storage
@brief named storage local to a specific context.
*/
class Storage
{
using Serial = FrameLib_Parameters::Serial;
friend class FrameLib_LocalAllocator;
public:
/**
@class Access
@brief an RAII utility for safely accessing a Storage object.
*/
class Access
{
public:
// Constructor and Destructor
Access(Storage *storage) : mStorage(storage) { mStorage->mLock.acquire(); }
~Access() { mStorage->mLock.release(); }
// Non-copyable
Access(const Access&) = delete;
Access& operator=(const Access&) = delete;
// Getters
FrameType getType() const { return mStorage->getType(); }
double *getVector() const { return mStorage->getVector(); }
unsigned long getVectorSize() const { return mStorage->getVectorSize(); }
unsigned long getTaggedSize() const { return mStorage->getTaggedSize(); }
Serial *getTagged() const { return mStorage->getTagged(); }
// Resize
void resize(bool tagged, unsigned long size) { mStorage->resize(tagged, size); }
private:
// Data
Storage *mStorage;
};
const char *getName() const { return mName.c_str(); }
protected:
// Getters
FrameType getType() const { return mType; }
double *getVector() const { return mType == kFrameNormal ? static_cast<double *>(mData) : nullptr; }
unsigned long getVectorSize() const { return mType == kFrameNormal ? static_cast<unsigned long>(mSize) : 0; }
unsigned long getTaggedSize() const { return mType == kFrameTagged ? static_cast<unsigned long>(mSize) : 0; }
Serial *getTagged() const { return mType == kFrameTagged ? static_cast<Serial *>(mData) : nullptr; }
// Resize the storage
void resize(bool tagged, unsigned long size);
// Constructor / Destructor
Storage(const char *name, FrameLib_LocalAllocator& allocator);
~Storage();
// Non-copyable
Storage(const Storage&) = delete;
Storage& operator=(const Storage&) = delete;
// Reference Counting
void increment() { mCount++; }
unsigned long decrement() { return --mCount; }
private:
// Member Variables
std::string mName;
FrameType mType;
void *mData;
size_t mSize;
size_t mMaxSize;
unsigned long mCount;
FrameLib_SpinLock mLock;
FrameLib_LocalAllocator& mAllocator;
};
// Constructor / Destructor
FrameLib_LocalAllocator(FrameLib_GlobalAllocator& allocator);
~FrameLib_LocalAllocator();
// Non-copyable
FrameLib_LocalAllocator(const FrameLib_LocalAllocator&) = delete;
FrameLib_LocalAllocator& operator=(const FrameLib_LocalAllocator&) = delete;
// Allocate / Deallocate Memory
void *alloc(size_t size);
void dealloc(void *ptr);
// Clear Local Free Blocks (and prune global allocator)
void clear();
// Alignment Helpers
static size_t getAlignment() { return FrameLib_GlobalAllocator::getAlignment(); }
static size_t alignSize(size_t x) { return FrameLib_GlobalAllocator::alignSize(x); }
// Register and Release Storage
Storage *registerStorage(const char *name);
void releaseStorage(const char *name);
private:
// Find Storage by Name
std::vector<Storage *>::iterator findStorage(const char *name);
// Remove a Free Block after Allocation and Return the Pointer
void *removeBlock(FreeBlock *block);
// Member Variables
FrameLib_GlobalAllocator& mAllocator;
FreeBlock mFreeLists[numLocalFreeBlocks];
FreeBlock *mTail;
std::vector<Storage *> mStorage;
};
#endif