BufferProvider: add the bulk of the LocalBufferStore implementation

...can mostly be implemented by delegating to already developed code.

first test case runs, but behaves unexpectedly
This commit is contained in:
Fischlurch 2026-05-18 23:12:43 +02:00
parent 0204438147
commit a4b83135a0
6 changed files with 107 additions and 24 deletions

View file

@ -143,10 +143,6 @@ namespace mem {
* the client should be prepared for exceptions. The #announce operation allows
* to establish a reliably available baseline.
* @todo 2/2025 might need to accept a cache key as additional parameter //////////////////////////////////TICKET #1392 : get cache key from computation to the cache backend
* @todo 2/2026 my intention is to use metadata::Entry within a threadsafe allocator
* to pass buffers around. For this to be possible, buffer-metadata.hpp needs
* some refactoring, and then the BufferStore::provideBuffer would have to
* return a metadata::Entry
*/
BuffHandle
BufferProvider::lockBuffer (BuffDescr const& type, int64_t arg)

View file

@ -14,10 +14,10 @@
/** @file local-buffer-store.hpp
** Production-grade implementation of engine::BufferProvider memory handling.
**
** @see LocalBufferStore_test
** @see LocalBufferStore_test
** @see buffer-provider.hpp
** @see buffer-provider-protocol-test.cpp
** @see simple-buffer-state-registry.hpp
** @see local-buffer-stage.hpp
*/
#ifndef VAULT_MEM_LOCAL_BUFFER_STORE_H
@ -29,11 +29,13 @@
#include "vault/mem/local-mem-pool.hpp"
#include "lib/local-slice.hpp"
#include "lib/depend.hpp"
#include "lib/format-string.hpp"
namespace vault {
namespace mem {
using util::_Fmt;
/**
@ -75,34 +77,80 @@ namespace mem {
/* ==== Implementation of the BufferStore interface ==== */
/** API: announce demand for some buffer allocations.
* @return number of allocations that were immediately reserved, locally.
* @remark This operation is crucial for the interplay between local and global buffer pool.
* In case the local pool can not (fully) handle the demand, an asynchronous request
* is sent to the global pool, that will be processed there in bulk, on next occasion.
*/
uint
prepareBuffers (HashVal, uint numBuffers, size_t) override
prepareBuffers (HashVal, uint numBuffers, size_t sizRequest) override
{
UNIMPLEMENTED ("announce");
uint cnt = localReg_->reserve(numBuffers, sizRequest);
for (uint n{cnt}; n < numBuffers; ++n)
globalReg_().async_requestAllocation (*localReg_, sizRequest);
// signal back the number of buffers reserved and thus immediately available
return cnt;
}
/** API: create a new buffer allocation */
/** API: create a new buffer allocation.
* @return a memory allocation ready to use
* @throws err::State in case this allocation can not be satisfied.
* @remark This is the central access point to get buffer memory.
* Due to the asynchronous nature of allocation requests, it might be necessary to
* process pending global allocations immediately in this thread, thereby acquiring
* a global lock on the EngineBufferManager. Since there is no guarantee that an
* allocation has actually be announced, we need to perform a direct call to the
* central hub, in a second attempt to get the required allocation.
* @note this is the production-grade implementation, and thus no additional verifications
* are performed, other than the lookup of the »buffer type« and the lifecycle state,
* that is recorded in the BufferMetadata.
*/
BuffAlloc
provideBuffer (HashVal, size_t buffSiz, LocalTag specifics, int64_t) override
provideBuffer (HashVal, size_t sizRequest, LocalTag specifics, int64_t) override
{
UNIMPLEMENTED ("retrieve alloc");
if (not localReg_->canServe(sizRequest))
globalReg_().processPendingRequests();
// attempt to satisfy allocation request from the local pool...
Alloc alloc = localReg_->retrieve (sizRequest);
auto& [storage,buffSiz] = alloc;
// possibly the request was not announced beforehand...?
if (not storage)
alloc = globalReg_().requestAllocation (sizRequest);
if (not storage)
throw err::State{_Fmt{"Unable to provide further %d bytes of buffer memory."} % sizRequest
,LUMIERA_ERROR_BUFFER_MANAGEMENT};
return std::make_tuple (storage, buffSiz, specifics);
}
/** API: mark a buffer as _emitted_ */
/** API: mark a buffer as _emitted_.
* @note the production-grade implementation does not need to do anything,
* since the buffer state transition was already recorded in the buffer metadata.
*/
void
mark_emitted (HashVal, BuffAlloc storageSlot) override
mark_emitted (HashVal, BuffAlloc) override
{
UNIMPLEMENTED ("emit");
/* nothing to do for a regular buffer allocation -- emit state captured in metadata */
}
/** API: mark a buffer as officially discarded */
/** API: mark a buffer as officially discarded.
* @remark usually the buffer is only added back into the thread-local pool,
* yet some heuristic clean-up of unfrequently-used buffer allocations
* might be triggered an that point.
*/
void
detachBuffer (HashVal, BuffAlloc storageSlot) override
{
UNIMPLEMENTED ("reclaim alloc");
auto& [storage,buffSiz,specifics] = storageSlot;
REQUIRE (storage);
localReg_->reSupply (storage);
/////////////////////////////////////////////////////OOO trigger heuristic clean-up here
}
};

View file

@ -370,7 +370,7 @@ namespace mem {
* @warning there must not be any allocations in use when invoking this function
*/
template<invocable<Buff*,size_t> FUN>
uint
void
purge (FUN&& consumer)
{
ingest();
@ -390,7 +390,9 @@ namespace mem {
{
inQueue_.consume_all ([this](Alloc alloc)
{
blocks_.emplace_front (move(alloc));
if (alloc.mem)
blocks_.emplace_front (move(alloc));
// otherwise: allocation not possible, do not add anything
});
}

View file

@ -26,6 +26,7 @@
#include "lib/thread.hpp"
//#include "lib/error.hpp"
#include "lib/util.hpp"
#include "test/diagnostic-output.hpp"
#include <algorithm>
@ -98,7 +99,7 @@ namespace test {
const size_t ALLOC = HeapBufferAllocator::numTiles(BUFFSIZ) * HeapBufferAllocator::TILE_SIZ;
Thread testWorker{[&] /* === Play through one typical usage cycle === */ // ◁─────────────────┨ bla
Thread testWorker{[&] /* === Play through one typical usage cycle === */
{
uint avail = storeAPI.prepareBuffers(TYPEID, CNT, BUFFSIZ);
CHECK (0 == avail);
@ -131,7 +132,7 @@ namespace test {
CHECK (sto1);
CHECK (sto2);
CHECK (isSameAdr (storage, sto1));
CHECK (not isSameAdr (storage, sto1));
CHECK (not isSameAdr (storage, sto2));
CHECK (BUFFSIZ <= siz1);
CHECK (BUFFSIZ <= siz2);
@ -145,6 +146,7 @@ namespace test {
// before shutdown, LocalMemPool has sent back all allocations;
// these should sit now in the EngineBufferManager's input queue
SHOW_EXPR(watch(*globalPool).numAllocs());
CHECK ( CNT+1 == watch(*globalPool).numAllocs());
CHECK ((CNT+1)*ALLOC == watch(*globalPool).bytesLeased());

View file

@ -117,7 +117,7 @@ namespace test {
CHECK (watch(pool).cnt(SIZ10) == 0); // new allotment not ingested yet,....
CHECK (not watch(pool).isEmpty()); // however, there is an entry in the in-queue
CHECK (pool.canServe(SIZ10)); // query causes a scan and retrieval from the in-queu
CHECK (pool.canServe(SIZ10)); // query causes a scan and retrieval from the in-queue
CHECK (watch(pool).cnt(SIZ10) == 1);
CHECK (not watch(pool).isEmpty());

View file

@ -144995,8 +144995,8 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</html></richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1778979188506" ID="ID_1435066016" MODIFIED="1778979206192" TEXT="Zugriff auf Thread-Local Mem-Pool und EngineBufferManager">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1778979188506" ID="ID_1435066016" MODIFIED="1779148168359" TEXT="Zugriff auf Thread-Local Mem-Pool und EngineBufferManager">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1779128982798" ID="ID_1913652442" MODIFIED="1779128995471" TEXT="Problem Clean-up">
<icon BUILTIN="messagebox_warning"/>
@ -145231,6 +145231,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1778948838374" ID="ID_1190893477" MODIFIED="1778979151352" TEXT="verify_API">
<icon BUILTIN="pencil"/>
<node CREATED="1779148191788" ID="ID_39449611" MODIFIED="1779148194561" TEXT="Schritte">
<node CREATED="1778948874440" ID="ID_619417705" MODIFIED="1778948892430" TEXT="Allokation ank&#xfc;ndigen"/>
<node CREATED="1778948897541" ID="ID_625050889" MODIFIED="1778948901900" TEXT="Allokation beziehen"/>
<node CREATED="1778948902738" ID="ID_324329976" MODIFIED="1778948908603" TEXT="weitere Allokation beziehen"/>
@ -145239,6 +145240,25 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1778948939771" ID="ID_1407930950" MODIFIED="1778948943134" TEXT="Thread beenden"/>
<node CREATED="1778979132916" ID="ID_393345688" MODIFIED="1778979147235" TEXT="Anzahl tats&#xe4;chlicher Allokationen pr&#xfc;fen"/>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1779149341620" ID="ID_1844089253" MODIFIED="1779149360400" TEXT="preparBuffers beachtet Reservierungen nicht">
<icon BUILTIN="broken-line"/>
<node CREATED="1779149362001" ID="ID_674281546" MODIFIED="1779149386315" TEXT="eigentlich sollten von den 5 angeforderten Buffern 4 noch &#xbb;reserved&#xab; sein"/>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1779148203212" ID="ID_784985187" MODIFIED="1779148210922" STYLE="fork" TEXT="SEGFAULT">
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="broken-line"/>
<node CREATED="1779148212791" ID="ID_912557487" MODIFIED="1779148225972" TEXT="undurchsichtige Situation">
<node CREATED="1779148226905" ID="ID_135139479" MODIFIED="1779148244026" TEXT="hier wird offensichtlich der dtor des thread-local-Objekts aufgerufen"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1779148244982" ID="ID_1958707025" MODIFIED="1779149274605" TEXT="aber: der Thread gilt offiziell bereits als terminiert (d.h. main l&#xe4;uft ebenfalls weiter)">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1779149276142" ID="ID_1730131768" MODIFIED="1779149284875" TEXT="das ist ein Problem f&#xfc;r das Test-Setup"/>
<node CREATED="1779149288603" ID="ID_990204079" MODIFIED="1779149314066" TEXT="weil wir damit zu u.U zu fr&#xfc;h pr&#xfc;fen (befor die Allocs zur&#xfc;ckgesandt wurden)"/>
</node>
<node CREATED="1779148268344" ID="ID_1630618662" MODIFIED="1779148295972" TEXT="der clean-up-Hook bekommt eine Instanz mit einer leeren Block-Liste"/>
<node CREATED="1779148296997" ID="ID_418820207" MODIFIED="1779148309028" TEXT="aber er iteriert diese, aus unklarem Grund"/>
</node>
</node>
</node>
<node CREATED="1778948944946" ID="ID_399276838" MODIFIED="1778948954589" TEXT="verify_announce">
<node CREATED="1778948971327" ID="ID_655345868" MODIFIED="1778948993943" TEXT="mu&#xdf; hier die Pr&#xfc;fung bei laufendem Worker-Thread machen (barrier)"/>
<node CREATED="1778948997109" ID="ID_1331981737" MODIFIED="1778949018089" TEXT="Announcment mu&#xdf; im Engine-Pool sichtbar sein"/>
@ -147172,6 +147192,21 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1778692908762" ID="ID_707657939" MODIFIED="1778692926163" TEXT="dann mu&#xdf; das globale Processing diesen behandelt und zugestellt haben"/>
<node CREATED="1778692929071" ID="ID_467967428" MODIFIED="1778692935363" TEXT="und sei es, als leere Alloc"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1779144663108" ID="ID_16263096" MODIFIED="1779144677490" TEXT="in der Praxis ist es komplexer">
<icon BUILTIN="clanbomber"/>
<node CREATED="1779144679873" ID="ID_1548159331" MODIFIED="1779144739766" TEXT="...weil wir nicht wissen, ob ein Announcement stattgefunden hat">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
genauer, wir &#187;wissen&#171; da&#223; die Standard-Implementierung der Render-Invocation ein Announcement macht (Haha ... aktuell macht sie keines), aber wir k&#246;nnen uns niemals darauf verlassen.....
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1779144741521" ID="ID_1735692450" MODIFIED="1779144758025" TEXT="daher mu&#xdf; hier tats&#xe4;chlich auch das synchrone API aufgerufen werden"/>
<node CREATED="1779144758915" ID="ID_702343722" MODIFIED="1779144791995" TEXT="und erst danach werfen wir error::State (Allokaitonsfehler)"/>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#540f69" CREATED="1778693019244" ID="ID_1545731490" MODIFIED="1778693042226" TEXT="beachte: gar kein direkter Request">
<icon BUILTIN="forward"/>