From a4b83135a01c3869742efcc043a2de17c513013f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 18 May 2026 23:12:43 +0200 Subject: [PATCH] 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 --- src/vault/mem/buffer-provider.cpp | 4 -- src/vault/mem/local-buffer-store.hpp | 74 +++++++++++++++++---- src/vault/mem/local-mem-pool.hpp | 6 +- tests/vault/mem/local-buffer-store-test.cpp | 6 +- tests/vault/mem/local-mem-pool-test.cpp | 2 +- wiki/thinkPad.ichthyo.mm | 39 ++++++++++- 6 files changed, 107 insertions(+), 24 deletions(-) diff --git a/src/vault/mem/buffer-provider.cpp b/src/vault/mem/buffer-provider.cpp index 4fbd1bbf6..beae9241d 100644 --- a/src/vault/mem/buffer-provider.cpp +++ b/src/vault/mem/buffer-provider.cpp @@ -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) diff --git a/src/vault/mem/local-buffer-store.hpp b/src/vault/mem/local-buffer-store.hpp index 9c351ebea..5623afea0 100644 --- a/src/vault/mem/local-buffer-store.hpp +++ b/src/vault/mem/local-buffer-store.hpp @@ -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 } }; diff --git a/src/vault/mem/local-mem-pool.hpp b/src/vault/mem/local-mem-pool.hpp index 36dde72f8..a65fd7e05 100644 --- a/src/vault/mem/local-mem-pool.hpp +++ b/src/vault/mem/local-mem-pool.hpp @@ -370,7 +370,7 @@ namespace mem { * @warning there must not be any allocations in use when invoking this function */ template 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 }); } diff --git a/tests/vault/mem/local-buffer-store-test.cpp b/tests/vault/mem/local-buffer-store-test.cpp index 739b4e067..9de5b684f 100644 --- a/tests/vault/mem/local-buffer-store-test.cpp +++ b/tests/vault/mem/local-buffer-store-test.cpp @@ -26,6 +26,7 @@ #include "lib/thread.hpp" //#include "lib/error.hpp" #include "lib/util.hpp" +#include "test/diagnostic-output.hpp" #include @@ -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()); diff --git a/tests/vault/mem/local-mem-pool-test.cpp b/tests/vault/mem/local-mem-pool-test.cpp index 66faeebd6..e53d80a4e 100644 --- a/tests/vault/mem/local-mem-pool-test.cpp +++ b/tests/vault/mem/local-mem-pool-test.cpp @@ -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()); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 3936a6399..88bbd1427 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -144995,8 +144995,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -145231,6 +145231,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + @@ -145239,6 +145240,25 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + + + + + + + + + @@ -147172,6 +147192,21 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + +

+ 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..... +

+ +
+
+ + +