BufferProvider: remove rest of the old implementation

__note__: regarding hash-standard.hpp see Ticket #1422
Mostly this hack worked -- but it is very likely not needed any more.
Today I had to re-wire a partial specialisation for hashing pointers,
that was added somewhere in the past into the STL.
This commit is contained in:
Fischlurch 2026-04-11 23:52:11 +02:00
parent 7652ca05cd
commit 5686694999
4 changed files with 51 additions and 365 deletions

View file

@ -44,9 +44,11 @@
** Please ensure it is always included _before_ the latter. Failing to do so will result ** Please ensure it is always included _before_ the latter. Failing to do so will result
** in mysterious failures. ** in mysterious failures.
** **
** @todo 4/2014 doesn't work as expected. My suspicion is that in the actual use case (PlacementIndex), ** @todo 4/2026 this shady trickery works without problems since many years now,
** the type providing the hasher is mixed in through inheritance, and the template specialisation ** while it is not clear if we actually need to do these horrible things any more.
** for this base type is not considered on lookup. ///////TICKET #722 ** 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 HashIndexed
** @see LUID ** @see LUID
@ -106,10 +108,6 @@ namespace std {
{ {
static_assert (sizeof(TY) < 0, "No hash implementation found. " static_assert (sizeof(TY) < 0, "No hash implementation found. "
"Either specialise std::hash or provide a boost-style hash_value via ADL."); "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. * Primary class template for std::hash.
* We provide no default implementation, but a marker type * We provide no default implementation, but bridge automatically
* to allow detection of custom implementation through metaprogramming * when a boost-style hash function can be picked up via ADL.
* *
* @note this definition has been \em hijacked by Lumiera * @note this definition has been \em hijacked by Lumiera
* to add an automatic bridge to use boost::hash functions * to add an automatic bridge to use boost::hash functions
@ -140,12 +138,14 @@ namespace std {
: public _HashImplementationSelector<TY> : public _HashImplementationSelector<TY>
{ }; { };
} }// namespace std
/* ==== Evil Evil ==== */
/* ==== Evil Evil ==== */ ////////////////////////////////////////////////////////////////TICKET #1422 : very likely we do not need this manipulation any more !!!
#define hash hash_HIDDEN #define hash hash_HIDDEN
#define _Hash_impl _Hash_impl_HIDDEN #define _Hash_impl _Hash_impl_HIDDEN
@ -204,6 +204,12 @@ namespace std {
STD_HASH_IMPL (long double) STD_HASH_IMPL (long double)
#undef STD_HASH_IMPL #undef STD_HASH_IMPL
}
/** rewire the partial specialisation for pointer types */
template<typename TY>
struct hash<TY*> : public hash_HIDDEN<TY*> { };
}// namespace std
#endif /*LIB_HASH_STANDARD_H*/ #endif /*LIB_HASH_STANDARD_H*/

View file

@ -27,8 +27,9 @@
** are configured as part of starting the steam::engine::RenderEnvironment. ** are configured as part of starting the steam::engine::RenderEnvironment.
** @todo who is responsible for setup of the services for the actual render engine? ** @todo who is responsible for setup of the services for the actual render engine?
** Might be closely related to brining up façade interfaces. ** Might be closely related to brining up façade interfaces.
** @todo WIP-WIP 1/2026 provide a default implementation for test ** @todo WIP-WIP 1/2026
** and find a way to populate the Facilities with the ** - ✔ provide a default implementation for test
** - 🔁 find a way to populate the Facilities with the
** production variant of the services... ** production variant of the services...
** **
** @see engine-ctx-test.cpp ** @see engine-ctx-test.cpp

View file

@ -14,262 +14,38 @@
/** @file heap-mem-buffer-store.cpp /** @file heap-mem-buffer-store.cpp
** Implementation details of simple heap based engine::BufferProvider storage. ** 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/error.hpp"
#include "lib/integral.hpp" #include "lib/integral.hpp"
#include "include/logging.h"
#include "lib/scoped-ptrvect.hpp"
#include "vault/mem/buffer-metadata.hpp" #include "vault/mem/buffer-metadata.hpp"
#include "lib/format-string.hpp" #include "lib/format-string.hpp"
#include "lib/format-obj.hpp" #include "lib/format-obj.hpp"
#include "lib/util-foreach.hpp"
#include "lib/nocopy.hpp" #include "lib/nocopy.hpp"
#include "vault/mem/heap-mem-buffer-store.hpp" #include "vault/mem/heap-mem-buffer-store.hpp"
#include <algorithm>
#include <vector>
#include <array>
using util::and_all;
using std::vector;
using std::array;
using lib::ScopedPtrVect;
using util::contains; using util::contains;
using util::_Fmt; using util::_Fmt;
namespace vault { namespace vault {
namespace mem { namespace mem {
namespace err = lumiera::error; namespace err = lumiera::error;
namespace { // internal config and helpers...
/////////////////////////////////////////////////////////////////////////////////////////////////////////////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<char[]> 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<Buff*> (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<bool(Block const&)>
search_for_block_using_this_storage (void* storage)
{
return bind (identifyBlock, _1, storage);
}
template<class VEC>
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<HeapMemBufferStore::Block>;
/**
* @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<Block>;
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);
using StorageWord = uint64_t; using StorageWord = uint64_t;
constexpr inline size_t WORD_SIZ = sizeof(StorageWord); constexpr inline size_t WORD_SIZ = sizeof(StorageWord);
@ -285,19 +61,21 @@ namespace mem {
{ {
return ceilDiv (sizeRequest, WORD_SIZ); return ceilDiv (sizeRequest, WORD_SIZ);
} }
static_assert (0 == wordCnt(0)); static_assert (0 == wordCnt(0));
static_assert (1 == wordCnt(1)); static_assert (1 == wordCnt(1));
static_assert (1 == wordCnt(WORD_SIZ)); static_assert (1 == wordCnt(WORD_SIZ));
inline void inline void
zeroFill (void* buf, size_t cnt) zeroFill (void* buf, size_t cnt)
{ {
auto begin = static_cast<StorageWord*> (buf); auto begin = static_cast<StorageWord*> (buf);
std::fill (begin, begin+cnt, StorageWord(0)); std::fill (begin, begin+cnt, StorageWord(0));
} }
} //(END) Internals
} // (END) Details of allocation and accounting
/** /**
@ -313,7 +91,7 @@ namespace mem {
Buff* mem_{nullptr}; Buff* mem_{nullptr};
size_t siz_{0}; size_t siz_{0};
BufferState state_{NIL}; BufferState state_{NIL};
void void
__requireState (BufferState expected) __requireState (BufferState expected)
{ {
@ -324,7 +102,7 @@ namespace mem {
,LERR_(LIFECYCLE) ,LERR_(LIFECYCLE)
}; };
} }
void void
allocate (size_t cnt) allocate (size_t cnt)
{ {
@ -412,13 +190,10 @@ namespace mem {
/**
* @internal create a memory tracking BufferProvider,
*/
HeapMemBufferStore::HeapMemBufferStore() HeapMemBufferStore::HeapMemBufferStore()
: allocIdx_{} : allocIdx_{}
, pool_(new PoolTable)
, outSeq_()
{ } { }
HeapMemBufferStore::~HeapMemBufferStore() { /* dtor of index table -> de-allocation here */ } HeapMemBufferStore::~HeapMemBufferStore() { /* dtor of index table -> de-allocation here */ }
@ -427,6 +202,7 @@ namespace mem {
/* ==== Implementation of the BufferProvider interface ==== */ /* ==== Implementation of the BufferProvider interface ==== */
/** @note pre-anouncement is ignored */
uint uint
HeapMemBufferStore::prepareBuffers (HashVal, uint numBuffers, size_t) HeapMemBufferStore::prepareBuffers (HashVal, uint numBuffers, size_t)
{ {
@ -434,6 +210,7 @@ namespace mem {
} }
/** API: create a new buffer allocation */
BuffAlloc BuffAlloc
HeapMemBufferStore::provideBuffer (HashVal, size_t buffSiz, LocalTag specifics, int64_t) HeapMemBufferStore::provideBuffer (HashVal, size_t buffSiz, LocalTag specifics, int64_t)
{ {
@ -442,6 +219,7 @@ namespace mem {
} }
/** API: mark a buffer as _emitted_ */
void void
HeapMemBufferStore::mark_emitted (HashVal, BuffAlloc storageSlot) 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 void
HeapMemBufferStore::detachBuffer (HashVal, BuffAlloc storageSlot) 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 }} // namespace vault::mem

View file

@ -12,8 +12,8 @@
*/ */
/** @file heap-mem-provider.hpp /** @file heap-mem-provider.hpp
** Dummy engine::BufferProvider storage implementation, for sake of conceptual ** Dummy engine::BufferProvider BufferStore implementation, for sake of conceptual
** demonstration and unit testing. This BufferProvider implementation is notably straight forward, ** 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 ** 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 ** 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. ** allows to investigate additional tracking status flags for each allocated block after the fact.
@ -21,33 +21,22 @@
** @see DiagnosticOutputSlot ** @see DiagnosticOutputSlot
** @see DiagnosticBufferProvider ** @see DiagnosticBufferProvider
** @see buffer-provider-protocol-test.cpp ** @see buffer-provider-protocol-test.cpp
** @see engine-ctx.hpp
** @see naive-buffer-setup.hpp
*/ */
#ifndef VAULT_MEM_HEAP_MEM_BUFFER_STORE_H #ifndef VAULT_MEM_HEAP_MEM_BUFFER_STORE_H
#define 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 "vault/mem/buffer-provider-setup.hpp"
#include "lib/scoped-ptrvect.hpp"
#include <unordered_map> #include <unordered_map>
#include <memory>
namespace std {
/// Partial specializations for pointer types.
template<typename _Tp>
struct hash<_Tp*> : public hash_HIDDEN<_Tp*> { };
}
namespace vault { namespace vault {
namespace mem { namespace mem {
using std::unique_ptr;
using lib::ScopedPtrVect;
using lib::HashVal;
/** /**
* Naive demo implementation of the BufferProvider::BufferStore. * Naive demo implementation of the BufferProvider::BufferStore.
@ -65,15 +54,8 @@ namespace mem {
using Index = std::unordered_map<Buff*,Alloc>; using Index = std::unordered_map<Buff*,Alloc>;
Index allocIdx_; 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<HashVal,BlockPool>;
unique_ptr<PoolTable> pool_;
ScopedPtrVect<Block> outSeq_;
public:
/* === BufferStore interface === */ /* === BufferStore interface === */
uint prepareBuffers (HashVal typeID, uint cnt, size_t) override; uint prepareBuffers (HashVal typeID, uint cnt, size_t) override;
@ -84,23 +66,6 @@ namespace mem {
public: public:
~HeapMemBufferStore(); ~HeapMemBufferStore();
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<typename TY>
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);
}; };