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:
parent
7652ca05cd
commit
5686694999
4 changed files with 51 additions and 365 deletions
|
|
@ -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<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_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<typename TY>
|
||||
struct hash<TY*> : public hash_HIDDEN<TY*> { };
|
||||
|
||||
|
||||
}// namespace std
|
||||
|
||||
#endif /*LIB_HASH_STANDARD_H*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <algorithm>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
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<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);
|
||||
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<StorageWord*> (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
|
||||
|
|
|
|||
|
|
@ -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 <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace std {
|
||||
/// Partial specializations for pointer types.
|
||||
template<typename _Tp>
|
||||
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<Buff*,Alloc>;
|
||||
|
||||
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 === */
|
||||
|
||||
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<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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue