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:
parent
0204438147
commit
a4b83135a0
6 changed files with 107 additions and 24 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -144995,8 +144995,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> 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<R1> b1, Extension<R1,R2> 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ü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<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1778948939771" ID="ID_1407930950" MODIFIED="1778948943134" TEXT="Thread beenden"/>
|
||||
<node CREATED="1778979132916" ID="ID_393345688" MODIFIED="1778979147235" TEXT="Anzahl tatsächlicher Allokationen prü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 »reserved« 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äuft ebenfalls weiter)">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1779149276142" ID="ID_1730131768" MODIFIED="1779149284875" TEXT="das ist ein Problem für das Test-Setup"/>
|
||||
<node CREATED="1779149288603" ID="ID_990204079" MODIFIED="1779149314066" TEXT="weil wir damit zu u.U zu früh prüfen (befor die Allocs zurü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ß hier die Prüfung bei laufendem Worker-Thread machen (barrier)"/>
|
||||
<node CREATED="1778948997109" ID="ID_1331981737" MODIFIED="1778949018089" TEXT="Announcment muß im Engine-Pool sichtbar sein"/>
|
||||
|
|
@ -147172,6 +147192,21 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1778692908762" ID="ID_707657939" MODIFIED="1778692926163" TEXT="dann muß 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 »wissen« daß die Standard-Implementierung der Render-Invocation ein Announcement macht (Haha ... aktuell macht sie keines), aber wir können uns niemals darauf verlassen.....
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1779144741521" ID="ID_1735692450" MODIFIED="1779144758025" TEXT="daher muß hier tatsä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"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue