diff --git a/src/vault/mem/buffer-provider.hpp b/src/vault/mem/buffer-provider.hpp index 4ba4f8b99..6eb4829d1 100644 --- a/src/vault/mem/buffer-provider.hpp +++ b/src/vault/mem/buffer-provider.hpp @@ -237,7 +237,7 @@ namespace mem { virtual ~BufferStore() { } ///< this is an interface virtual uint prepareBuffers (HashVal typeID, uint cnt,size_t) =0; - virtual BuffAlloc provideBuffer (HashVal typeID, size_t,LocalTag,int64_t) =0; + virtual BuffAlloc provideBuffer (HashVal typeID, size_t,LocalTag,int64_t customArg) =0; virtual void mark_emitted (HashVal typeID, BuffAlloc) =0; virtual void detachBuffer (HashVal typeID, BuffAlloc) =0; }; diff --git a/src/vault/mem/diagnostic-buffer-provider.cpp b/src/vault/mem/diagnostic-buffer-provider.cpp index 55be5ab1c..0a31955f7 100644 --- a/src/vault/mem/diagnostic-buffer-provider.cpp +++ b/src/vault/mem/diagnostic-buffer-provider.cpp @@ -217,7 +217,6 @@ namespace mem { DiagnosticBufferProvider::DiagnosticBufferProvider() : NaiveBufferSetup{} - , heapStore_{dynamic_cast (*bufferStore_)} //////////////////////////////////////////TICKET 1410 : obsolete after switch to newtracking-API , tracker_{std::make_unique()} { decorate (bufferStage_, *tracker_, *this); @@ -239,22 +238,6 @@ namespace mem { /* === diagnostic API === */ - bool - BufferDiagnostic::buffer_was_used (uint bufferID) - { - NOTREACHED ("shall use new API only"); -// return dbp_.heapStore_.access_emitted(bufferID).was_used(); //////////////////////////////////////////TICKET 1410 : switch to newly defined tracking-API - } - - - bool - BufferDiagnostic::buffer_was_closed (uint bufferID) - { - NOTREACHED ("shall use new API only"); -// return dbp_.heapStore_.access_emitted(bufferID).was_closed();//////////////////////////////////////////TICKET 1410 : switch to newly defined tracking-API - } - - Buff* BufferDiagnostic::accessMemory (HashVal id) { // may throw when found diff --git a/src/vault/mem/diagnostic-buffer-provider.hpp b/src/vault/mem/diagnostic-buffer-provider.hpp index eb7bf3eb3..3d6525e99 100644 --- a/src/vault/mem/diagnostic-buffer-provider.hpp +++ b/src/vault/mem/diagnostic-buffer-provider.hpp @@ -115,7 +115,6 @@ namespace mem { class DiagnosticBufferProvider : public NaiveBufferSetup { - HeapMemBufferStore& heapStore_; //////////////////////////////OOO fällt dann weg nach dem Umbau class InstrumentedStageProxy; struct BlockTracker; std::unique_ptr tracker_; @@ -138,8 +137,6 @@ namespace mem { public: BufferDiagnostic (DiagnosticBufferProvider&); - bool buffer_was_used (uint bufferID); ///< @deprecated ////////////////////////////////////////TICKET 1410 - bool buffer_was_closed (uint bufferID); ///< @deprecated ////////////////////////////////////////TICKET 1410 bool was_created (HashVal); bool was_emitted (HashVal); bool was_released (HashVal); diff --git a/src/vault/mem/heap-mem-buffer-store.cpp b/src/vault/mem/heap-mem-buffer-store.cpp index 13b5d4527..013a94c48 100644 --- a/src/vault/mem/heap-mem-buffer-store.cpp +++ b/src/vault/mem/heap-mem-buffer-store.cpp @@ -18,18 +18,27 @@ #include "lib/error.hpp" +#include "lib/integral.hpp" #include "include/logging.h" #include "lib/scoped-ptrvect.hpp" +#include "vault/mem/buffer-metadata.hpp" +#include "lib/format-string.hpp" +#include "lib/format-obj.hpp" #include "lib/util-foreach.hpp" +#include "lib/nocopy.hpp" #include "vault/mem/heap-mem-buffer-store.hpp" #include #include +#include using util::and_all; using std::vector; +using std::array; using lib::ScopedPtrVect; +using util::contains; +using util::_Fmt; @@ -261,73 +270,206 @@ namespace mem { const uint MAX_BUFFERS = 50; Block emptyPlaceholder(0); + + using StorageWord = uint64_t; + constexpr inline size_t WORD_SIZ = sizeof(StorageWord); + + constexpr size_t + ceilDiv (size_t num, size_t den) noexcept + { + return (num + den - 1u) / den; + } + + constexpr size_t + wordCnt (size_t sizeRequest) noexcept + { + return ceilDiv (sizeRequest, WORD_SIZ); + } + static_assert (0 == wordCnt(0)); + static_assert (1 == wordCnt(1)); + static_assert (1 == wordCnt(WORD_SIZ)); + + inline void + zeroFill (void* buf, size_t cnt) + { + auto begin = static_cast (buf); + std::fill (begin, begin+cnt, StorageWord(0)); + } + } // (END) Details of allocation and accounting + /** + * @internal smart-handle and administrative record + * to manage a single buffer allocation and verify + * the proper sequence of lifecycle steps. + * @remark since HeapMemBufferStore is the default for tests, + * it seems prudent to perform additional sanity checks. + */ + class HeapMemBufferStore::Alloc + : util::MoveOnly + { + Buff* mem_{nullptr}; + size_t siz_{0}; + BufferState state_{NIL}; + + void + __requireState (BufferState expected) + { + if (state_ != expected) + throw err::Logic{_Fmt{"HeapMemBufferStore: Lifecycle broken: " + "expected_state:%d actual_state:%d"} + % expected % state_ + ,LERR_(LIFECYCLE) + }; + } + + void + allocate (size_t cnt) + { + __requireState (NIL); + REQUIRE (!mem_,"Attempt to re-allocate / double-allocate"); + REQUIRE (cnt, "Attempt to create a zero-sized buffer"); + siz_ = cnt; + size_t words = wordCnt(cnt); + mem_ = static_cast (std::aligned_alloc (std::alignment_of(), words )); + ENSURE (mem_); + state_ = LOCKED; + } + + void + discard() + { + if (mem_) + std::free (mem_); + mem_ = nullptr; + siz_ = 0; + state_ = BLOCKED; + } + + + Alloc (size_t size) { allocate(size); } + + public: + ~Alloc() { discard(); } + Alloc() = default; + + Alloc (Alloc&& rr) + : Alloc{} + { + std::swap (state_, rr.state_); + std::swap (siz_, rr.siz_); + std::swap (mem_, rr.mem_); + } + + + using IdxEntry = Index::value_type; + + /** + * Create new allocation and map a new entry for the + * internal allocation index of HeapMemBufferStore. + * @remark this is the only way to allocate. + */ + static Buff* + makeNewAllocEntry (Index& idx, size_t buffSize) + { + Alloc newAlloc{buffSize}; + Buff* memLocation = newAlloc.mem_; + if (contains (idx, memLocation)) + throw err::State{"HeapMemBufferStore allocated already " + "registered memory address again."}; + idx.emplace (memLocation, move(newAlloc)); + ENSURE (contains (idx, memLocation)); + return memLocation; + } + + void + verify (Buff* address, size_t size) + { + if (mem_ != address or siz_ != size) + throw err::State{_Fmt{"HeapMemBufferStore allocation identity corrupted: " + "Expect (size=%d|%s) yet invoked at state:%d with (%d|%s)."} + % siz_ % util::showAdr(mem_) % state_ % size % util::showAdr(address)}; + } + + + void + emit() + { + __requireState (LOCKED); + state_ = EMITTED; + } + + void + release() + { + if (state_ != LOCKED) + __requireState (EMITTED); + state_ = FREE; + } // NOTE: Allocation retained (for diagnostics) + }; + + /** * @internal create a memory tracking BufferProvider, */ HeapMemBufferStore::HeapMemBufferStore() - : pool_(new PoolTable) + : allocIdx_{} + , pool_(new PoolTable) , outSeq_() { } + HeapMemBufferStore::~HeapMemBufferStore() { /* dtor of index table -> de-allocation here */ } - HeapMemBufferStore::~HeapMemBufferStore() { /* emit dtor of BlockPool here */ } /* ==== Implementation of the BufferProvider interface ==== */ uint - HeapMemBufferStore::prepareBuffers (HashVal typeID, uint numBuffers, size_t buffSiz) + HeapMemBufferStore::prepareBuffers (HashVal, uint numBuffers, size_t) { - BlockPool& responsiblePool = getBlockPoolFor (buffSiz, typeID); - return responsiblePool.prepare_for (numBuffers); + return numBuffers; } BuffAlloc - HeapMemBufferStore::provideBuffer (HashVal typeID, size_t buffSiz, LocalTag, int64_t) + HeapMemBufferStore::provideBuffer (HashVal, size_t buffSiz, LocalTag specifics, int64_t) { - BlockPool& blocks = getBlockPoolFor (buffSiz, typeID); - Block& newBlock = blocks.createBlock(); - LocalTag specifics{&newBlock}; // used by this implementation to find the storage to release later - return std::make_tuple (asBuffer(newBlock.accessMemory()), buffSiz, specifics); + Buff* storage = Alloc::makeNewAllocEntry (allocIdx_, buffSiz); + return std::make_tuple (storage, buffSiz, specifics); } void - HeapMemBufferStore::mark_emitted (HashVal typeID, BuffAlloc storageSlot) + HeapMemBufferStore::mark_emitted (HashVal, BuffAlloc storageSlot) { - auto& [_,buffSiz,specifics] = storageSlot; - Block* block4buffer = locateBlock (buffSiz, typeID, specifics); - if (!block4buffer) - throw err::Logic ("Attempt to emit a buffer not known to this BufferProvider" - , LUMIERA_ERROR_BUFFER_MANAGEMENT); - BlockPool& pool = getBlockPoolFor (buffSiz, typeID); - Block* active = pool.transferResponsibility (block4buffer); - if (active) - outSeq_.manage (active); - else - if (block4buffer->was_closed()) - WARN (proc_mem, "Attempt to emit() an already closed buffer."); - else - WARN (proc_mem, "Attempt to emit() a buffer not found in active pool. " - "Maybe duplicate call to emit()?"); + auto& [storage,buffSiz,specifics] = storageSlot; + if (not contains (allocIdx_, storage)) + throw err::Invalid{_Fmt{"Passed buffer (size=%d|%s) for emit() call " + "that is not recognised by this HeapMemBufferStore"} + % buffSiz % util::showAdr(storage)}; + + auto& entry = allocIdx_[storage]; + entry.verify (storage,buffSiz); + entry.emit(); } /** mark a buffer as officially discarded */ void - HeapMemBufferStore::detachBuffer (HashVal typeID, BuffAlloc storageSlot) + HeapMemBufferStore::detachBuffer (HashVal, BuffAlloc storageSlot) { auto& [storage,buffSiz,specifics] = storageSlot; - Block* block4buffer = locateBlock (buffSiz, typeID, specifics); - REQUIRE (block4buffer, "releasing a buffer not allocated through this provider"); - REQUIRE (util::isSameAdr (storage, block4buffer->accessMemory())); - block4buffer->markReleased(); + if (not contains (allocIdx_, storage)) + throw err::Invalid{_Fmt{"Passed buffer (size=%d|%s) for release() call " + "that is not recognised by this HeapMemBufferStore"} + % buffSiz % util::showAdr(storage)}; + + auto& entry = allocIdx_[storage]; + entry.verify (storage,buffSiz); + entry.release(); } diff --git a/src/vault/mem/heap-mem-buffer-store.hpp b/src/vault/mem/heap-mem-buffer-store.hpp index b87725c51..4838933fb 100644 --- a/src/vault/mem/heap-mem-buffer-store.hpp +++ b/src/vault/mem/heap-mem-buffer-store.hpp @@ -35,6 +35,12 @@ #include +namespace std { + /// Partial specializations for pointer types. + template + struct hash<_Tp*> : public hash_HIDDEN<_Tp*> { }; + +} namespace vault { namespace mem { @@ -55,6 +61,10 @@ namespace mem { class HeapMemBufferStore : public BufferProviderSetup::Store { + class Alloc; + using Index = std::unordered_map; + + Index allocIdx_; public: /////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : looks like I'll re-implement the storage from scratch, since »tracking« is no longer required here class Block; class BlockPool;