#ifndef MARS_MemoryManager #define MARS_MemoryManager #ifndef __CINT__ #include #else namespace std { class mutex; class condition_variable; template class shared_ptr; template class forward_list; } #endif class MemoryStock { friend class MemoryChunk; friend class MemoryManager; size_t fChunkSize; size_t fMaxMemory; size_t fInUse; size_t fAllocated; size_t fMaxInUse; std::mutex fMutexMem; std::mutex fMutexCond; std::condition_variable fCond; std::forward_list> fMemoryStock; public: MemoryStock(size_t chunk, size_t max) : fChunkSize(chunk), fMaxMemory(max pop(bool block) { if (block) { // No free slot available, next alloc would exceed max memory: // block until a slot is available std::unique_lock lock(fMutexCond); while (fMemoryStock.empty() && fAllocated+fChunkSize>fMaxMemory) fCond.wait(lock); } else { // No free slot available, next alloc would exceed max memory // return an empty pointer if (fMemoryStock.empty() && fAllocated+fChunkSize>fMaxMemory) return std::shared_ptr(); } // We will return this amount of memory // This is not 100% thread safe, but it is not a super accurate measure anyway fInUse += fChunkSize; if (fInUse>fMaxInUse) fMaxInUse = fInUse; if (fMemoryStock.empty()) { // No free slot available, allocate a new one fAllocated += fChunkSize; return std::shared_ptr(new char[fChunkSize]); } // Get the next free slot from the stack and return it const std::lock_guard lock(fMutexMem); const auto mem = fMemoryStock.front(); fMemoryStock.pop_front(); return mem; }; void push(const std::shared_ptr &mem) { if (!mem) return; // Decrease the amont of memory in use accordingly fInUse -= fChunkSize; // If the maximum memory has changed, we might be over the limit. // In this case: free a slot if (fAllocated>fMaxMemory) { fAllocated -= fChunkSize; return; } { const std::lock_guard lock(fMutexMem); fMemoryStock.emplace_front(mem); } { const std::lock_guard lock(fMutexCond); fCond.notify_one(); } } }; class MemoryChunk { friend class MemoryManager; std::shared_ptr fMemoryStock; std::shared_ptr fPointer; MemoryChunk(const std::shared_ptr &mem, bool block) : fMemoryStock(mem) { fPointer = fMemoryStock->pop(block); } public: ~MemoryChunk() { fMemoryStock->push(fPointer); } }; class MemoryManager { std::shared_ptr fMemoryStock; public: MemoryManager(size_t chunk, size_t max) : fMemoryStock(std::make_shared(chunk, max)) { } std::shared_ptr malloc(bool block=true) { const std::shared_ptr chunk(new MemoryChunk(fMemoryStock, block)); return std::shared_ptr(chunk, chunk->fPointer.get()); } size_t getChunkSize() const { return fMemoryStock->fChunkSize; } bool setChunkSize(const size_t size) { #ifdef __EXCEPTIONS if (getInUse()) throw std::runtime_error("Cannot change the chunk size while there is memory in use"); if (getMaxMemory()fChunkSize = size; return true; } size_t getMaxMemory() const { return fMemoryStock->fMaxMemory; } size_t getInUse() const { return fMemoryStock->fInUse; } size_t getAllocated() const { return fMemoryStock->fAllocated; } size_t getMaxInUse() const { return fMemoryStock->fMaxInUse; } }; #endif