diff --git a/src/lib/hash-standard.hpp b/src/lib/hash-standard.hpp index b603c8e6f..2703a14b8 100644 --- a/src/lib/hash-standard.hpp +++ b/src/lib/hash-standard.hpp @@ -44,9 +44,11 @@ ** Please ensure it is always included _before_ the latter. Failing to do so will result ** in mysterious failures. ** - ** @todo 4/2014 doesn't work as expected. My suspicion is that in the actual use case (PlacementIndex), - ** the type providing the hasher is mixed in through inheritance, and the template specialisation - ** for this base type is not considered on lookup. ///////TICKET #722 + ** @todo 4/2026 this shady trickery works without problems since many years now, + ** while it is not clear if we actually need to do these horrible things any more. + ** Even while we may retain that automatic bridge to boost-functional hash, doing so + ** should be possible with the current STDLIB without having to patch and rewire + ** the library implementation. //////////////////////////////////////////////////////////////////////TICKET #1422 : re-assess the situation ! ** ** @see HashIndexed ** @see LUID @@ -106,10 +108,6 @@ namespace std { { static_assert (sizeof(TY) < 0, "No hash implementation found. " "Either specialise std::hash or provide a boost-style hash_value via ADL."); - - // the default provides *no* hash implementation - // and adds a marker type for metaprogramming - typedef int NotHashable; }; /** @@ -129,8 +127,8 @@ namespace std { /** * Primary class template for std::hash. - * We provide no default implementation, but a marker type - * to allow detection of custom implementation through metaprogramming + * We provide no default implementation, but bridge automatically + * when a boost-style hash function can be picked up via ADL. * * @note this definition has been \em hijacked by Lumiera * to add an automatic bridge to use boost::hash functions @@ -140,12 +138,14 @@ namespace std { : public _HashImplementationSelector { }; -} +}// namespace std -/* ==== Evil Evil ==== */ + + +/* ==== Evil Evil ==== */ ////////////////////////////////////////////////////////////////TICKET #1422 : very likely we do not need this manipulation any more !!! #define hash hash_HIDDEN #define _Hash_impl _Hash_impl_HIDDEN @@ -204,6 +204,12 @@ namespace std { STD_HASH_IMPL (long double) #undef STD_HASH_IMPL -} + + /** rewire the partial specialisation for pointer types */ + template + struct hash : public hash_HIDDEN { }; + + +}// namespace std #endif /*LIB_HASH_STANDARD_H*/ diff --git a/src/steam/engine/engine-ctx.hpp b/src/steam/engine/engine-ctx.hpp index 78f9d91ea..35ab90618 100644 --- a/src/steam/engine/engine-ctx.hpp +++ b/src/steam/engine/engine-ctx.hpp @@ -27,8 +27,9 @@ ** are configured as part of starting the steam::engine::RenderEnvironment. ** @todo who is responsible for setup of the services for the actual render engine? ** Might be closely related to brining up façade interfaces. - ** @todo WIP-WIP 1/2026 provide a default implementation for test - ** and find a way to populate the Facilities with the + ** @todo WIP-WIP 1/2026 + ** - ✔ provide a default implementation for test + ** - 🔁 find a way to populate the Facilities with the ** production variant of the services... ** ** @see engine-ctx-test.cpp diff --git a/src/vault/mem/heap-mem-buffer-store.cpp b/src/vault/mem/heap-mem-buffer-store.cpp index 013a94c48..62551fa6d 100644 --- a/src/vault/mem/heap-mem-buffer-store.cpp +++ b/src/vault/mem/heap-mem-buffer-store.cpp @@ -14,262 +14,38 @@ /** @file heap-mem-buffer-store.cpp ** Implementation details of simple heap based engine::BufferProvider storage. + ** This BufferStore implementation is configured by default and thus used primarily + ** from the unit tests; for that reasons, some additional sanity checks were added, + ** and the implementation is wasteful and not performance oriented. Furthermore, + ** aspects of concurrency safety were not considered. + ** + ** For each allocation, a new vault::mem::HeapMemBufferStore::Alloc entry is attached + ** to the index table, which allows follow-up calls to rediscover this management entry + ** just based on the memory address of the allocation. These entries manage the actual + ** allocations, and **will be retained** for the lifespan of the BufferStore instance. + ** This allows diagnostic code to investigate the contents of the buffers + ** after invoking the code subject to testing. */ #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; - namespace vault { namespace mem { namespace err = lumiera::error; - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 1410 : need to question what implementation structures are needed, after »tracking« was extracted... - /** - * Simplistic implementation of buffer storage. - * Allocates a block of heap memory for buffers and never deallocates. - * This strange behaviour can be used to investigate and demonstrate - * buffer usage from a unit test setup. - * @see DiagnosticBufferProvider - */ - class HeapMemBufferStore::Block - : util::NonCopyable - { - unique_ptr storage_; - - bool was_released_; - - public: - explicit - Block(size_t bufferSize) - : storage_(bufferSize? new char[bufferSize] : NULL) - , was_released_(false) - { } - - bool - was_used() const - { - return bool(storage_); - } - - bool - was_closed() const - { - return was_released_; - } - - void* - accessMemory() const - { - REQUIRE (storage_, "Block was never prepared for use"); - return storage_.get(); - } - - void - markReleased() - { - was_released_ = true; - } - }; - namespace { // implementation helpers... - - inline Buff* - asBuffer(void* mem) - {// type tag to mark memory address as Buffer - return static_cast (mem); - } - - - using Block = HeapMemBufferStore::Block; - - /** helper to find Block entries - * based on their raw memory address */ - inline bool - identifyBlock (Block const& inQuestion, void* storage) - { - return storage == &inQuestion; - } - - /** build a searching predicate */ - inline function - search_for_block_using_this_storage (void* storage) - { - return bind (identifyBlock, _1, storage); - } - - template - inline Block* - pick_Block_by_storage (VEC& vec, void* blockLocation) - { - typename VEC::iterator pos - = std::find_if (vec.begin(),vec.end() - ,search_for_block_using_this_storage(blockLocation)); - if (pos!=vec.end()) - return &(*pos); - else - return NULL; - } - } - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 1410 : need to question what implementation structures are needed, after »tracking« was extracted... - - using PoolBlocks = ScopedPtrVect; - - /** - * @internal Pool of allocated buffer Blocks of a specific size. - * Helper for implementing a Diagnostic BufferProvider; actually does - * just heap allocations for the Blocks, but keeps a collection of - * allocated Blocks around. Individual entries can be retrieved - * and thus removed from the responsibility of BlockPool. - * - * The idea is that each buffer starts its lifecycle within some pool - * and later gets "emitted" to an output sequence, where it remains for - * later investigation and diagnostics. - */ - class HeapMemBufferStore::BlockPool - { - uint maxAllocCount_; - size_t memBlockSize_; - using PoolBlocks = ScopedPtrVect; - PoolBlocks blockList_; - - public: - BlockPool() - : maxAllocCount_(0) // unlimited by default - , memBlockSize_(0) - , blockList_{} - { } - - void - initialise (size_t blockSize) - { - memBlockSize_ = blockSize; - } - // standard copy operations are valid, but will - // raise an runtime error, once BlockPool is initialised. - - ~BlockPool() - { - if (!verify_all_children_idle()) - ERROR (test, "Block actively in use while shutting down BufferProvider " - "allocation pool. This might lead to Segfault and memory leaks."); - } - - /** mark all managed blocks as disposed */ - void - discard() - { - for (Block& block : blockList_) - block.markReleased(); - } - - uint - prepare_for (uint number_of_expected_buffers) - { - if (maxAllocCount_ && - maxAllocCount_ < blockList_.size() + number_of_expected_buffers) - { - ASSERT (maxAllocCount_ >= blockList_.size()); - return maxAllocCount_ - blockList_.size(); - } - // currently no hard limit imposed - return number_of_expected_buffers; - } - - - Block& - createBlock() - { - return blockList_.manage (new Block(memBlockSize_)); - } - - - Block* - find (void* blockLocation) - { - return pick_Block_by_storage (blockList_, blockLocation); - } - - - Block* - transferResponsibility (Block* allocatedBlock) - { - return blockList_.detach (allocatedBlock); - } - - - size_t - size() const - { - return blockList_.size(); - } - - bool - isValid() const - { - return not blockList_.empty(); - } - - explicit - operator bool() const - { - return isValid(); - } - - private: - bool - verify_all_children_idle() - { - try { - return and_all (blockList_, is_in_sane_state); - } - ERROR_LOG_AND_IGNORE (test, "State verification of diagnostic BufferProvider allocation pool"); - return true; - } - - - static bool - is_in_sane_state (Block const& block) - { - return not block.was_used() or block.was_closed(); - } - }; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 1410 : need to question what implementation structures are needed, after »tracking« was extracted... - - - - namespace { // Details of allocation and accounting - - const uint MAX_BUFFERS = 50; - - Block emptyPlaceholder(0); + namespace { // internal config and helpers... using StorageWord = uint64_t; constexpr inline size_t WORD_SIZ = sizeof(StorageWord); @@ -285,19 +61,21 @@ namespace mem { { 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 + } //(END) Internals + + /** @@ -313,7 +91,7 @@ namespace mem { Buff* mem_{nullptr}; size_t siz_{0}; BufferState state_{NIL}; - + void __requireState (BufferState expected) { @@ -324,7 +102,7 @@ namespace mem { ,LERR_(LIFECYCLE) }; } - + void allocate (size_t cnt) { @@ -412,13 +190,10 @@ namespace mem { - /** - * @internal create a memory tracking BufferProvider, - */ + + HeapMemBufferStore::HeapMemBufferStore() : allocIdx_{} - , pool_(new PoolTable) - , outSeq_() { } HeapMemBufferStore::~HeapMemBufferStore() { /* dtor of index table -> de-allocation here */ } @@ -427,6 +202,7 @@ namespace mem { /* ==== Implementation of the BufferProvider interface ==== */ + /** @note pre-anouncement is ignored */ uint HeapMemBufferStore::prepareBuffers (HashVal, uint numBuffers, size_t) { @@ -434,6 +210,7 @@ namespace mem { } + /** API: create a new buffer allocation */ BuffAlloc HeapMemBufferStore::provideBuffer (HashVal, size_t buffSiz, LocalTag specifics, int64_t) { @@ -442,6 +219,7 @@ namespace mem { } + /** API: mark a buffer as _emitted_ */ void HeapMemBufferStore::mark_emitted (HashVal, BuffAlloc storageSlot) { @@ -457,7 +235,7 @@ namespace mem { } - /** mark a buffer as officially discarded */ + /** API: mark a buffer as officially discarded */ void HeapMemBufferStore::detachBuffer (HashVal, BuffAlloc storageSlot) { @@ -473,68 +251,4 @@ namespace mem { } - - /* ==== Implementation details ==== */ - -#if false ////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : this additional tracking API is obsolete and need to be removed - size_t - HeapMemBufferStore::emittedCnt() const - { - return outSeq_.size(); - } - - void - HeapMemBufferStore::markAllEmitted() - { - for (auto& [_, blockPool] : *pool_) - blockPool.discard(); - } - - HeapMemBufferStore::Block& - HeapMemBufferStore::access_emitted (uint bufferID) - { - if (!withinOutputSequence (bufferID)) - return emptyPlaceholder; ////////////////////////////////TICKET #856 - else - return outSeq_[bufferID]; - } -#endif ////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (End) obsoleted API - - bool - HeapMemBufferStore::withinOutputSequence (uint bufferID) const - { - if (bufferID >= MAX_BUFFERS) - throw err::Fatal ("hardwired internal limit for test buffers exceeded"); - - return bufferID < outSeq_.size(); - } - - HeapMemBufferStore::BlockPool& - HeapMemBufferStore::getBlockPoolFor (size_t buffSiz, HashVal typeID) - { - BlockPool& pool = (*pool_)[typeID]; - if (not pool) - pool.initialise (buffSiz); - return pool; - } - - HeapMemBufferStore::Block* - HeapMemBufferStore::locateBlock (size_t buffSiz, HashVal typeID, void* storage) - { - BlockPool& pool = getBlockPoolFor (buffSiz, typeID); - Block* block4buffer = pool.find (storage); ////////////////////////////////TICKET #856 - return block4buffer? block4buffer - : searchInOutSeqeuence (storage); - } - - - - HeapMemBufferStore::Block* - HeapMemBufferStore::searchInOutSeqeuence (void* blockLocation) - { - return pick_Block_by_storage (outSeq_, blockLocation); ////////////////////////////////TICKET #856 - } - - - }} // namespace vault::mem diff --git a/src/vault/mem/heap-mem-buffer-store.hpp b/src/vault/mem/heap-mem-buffer-store.hpp index 4838933fb..c497d8e5e 100644 --- a/src/vault/mem/heap-mem-buffer-store.hpp +++ b/src/vault/mem/heap-mem-buffer-store.hpp @@ -12,8 +12,8 @@ */ /** @file heap-mem-provider.hpp - ** Dummy engine::BufferProvider storage implementation, for sake of conceptual - ** demonstration and unit testing. This BufferProvider implementation is notably straight forward, + ** Dummy engine::BufferProvider BufferStore implementation, for sake of conceptual + ** demonstration and unit testing. Notably this buffer allocator implementation is straight forward, ** if not outright silly: it happily claims more and more heap blocks and never releases any memory ** dynamically. This both demonstrates the simplest possible implementation of storage handling, and ** allows to investigate additional tracking status flags for each allocated block after the fact. @@ -21,33 +21,22 @@ ** @see DiagnosticOutputSlot ** @see DiagnosticBufferProvider ** @see buffer-provider-protocol-test.cpp + ** @see engine-ctx.hpp + ** @see naive-buffer-setup.hpp */ #ifndef VAULT_MEM_HEAP_MEM_BUFFER_STORE_H #define VAULT_MEM_HEAP_MEM_BUFFER_STORE_H -#include "lib/hash-value.h" #include "vault/mem/buffer-provider-setup.hpp" -#include "lib/scoped-ptrvect.hpp" #include -#include -namespace std { - /// Partial specializations for pointer types. - template - struct hash<_Tp*> : public hash_HIDDEN<_Tp*> { }; - -} namespace vault { namespace mem { - using std::unique_ptr; - using lib::ScopedPtrVect; - using lib::HashVal; - /** * Naive demo implementation of the BufferProvider::BufferStore. @@ -65,15 +54,8 @@ namespace mem { 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; - private: - using PoolTable = std::unordered_map; - unique_ptr pool_; - ScopedPtrVect outSeq_; - public: + /* === BufferStore interface === */ uint prepareBuffers (HashVal typeID, uint cnt, size_t) override; @@ -84,23 +66,6 @@ namespace mem { public: ~HeapMemBufferStore(); HeapMemBufferStore(); - -#if false ////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : this additional tracking API is obsolete and need to be removed - size_t emittedCnt() const; - - Block& access_emitted (uint bufferID); - - template - TY& accessAs (uint bufferID); - - void markAllEmitted(); -#endif ////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (End) obsoleted API - - private: - bool withinOutputSequence (uint bufferID) const; - BlockPool& getBlockPoolFor (size_t,HashVal typeID); - Block* locateBlock (size_t,HashVal typeID, void*); - Block* searchInOutSeqeuence (void* storage); };