From 0567633bd9cb87e78520a597284472c99d092a22 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 13 May 2026 01:47:51 +0200 Subject: [PATCH] BufferProvider: add implementation for the asynchronous hand-over ...and define a test case, to perform the typical usage sequence within a lot of threads in parallel. --- src/vault/mem/engine-buffer-manager.hpp | 7 +- .../vault/mem/engine-buffer-manager-test.cpp | 98 ++++++++++++++++++- wiki/thinkPad.ichthyo.mm | 56 ++++++++++- 3 files changed, 151 insertions(+), 10 deletions(-) diff --git a/src/vault/mem/engine-buffer-manager.hpp b/src/vault/mem/engine-buffer-manager.hpp index 6f27e2531..08866ae0a 100644 --- a/src/vault/mem/engine-buffer-manager.hpp +++ b/src/vault/mem/engine-buffer-manager.hpp @@ -160,7 +160,7 @@ namespace mem { * @warning when the returned Alloc is [non-empty](\ref Alloc::empty), * the receiver is responsible for returning it after use. */ - void async_requestAllocation (AllocRequest const&); + void async_requestAllocation (AllocReceiver&, size_t sizRequest); /** * Handle all enqueued asynchronous requests @@ -181,9 +181,10 @@ namespace mem { inline void - EngineBufferManager::async_requestAllocation (AllocRequest const& req) + EngineBufferManager::async_requestAllocation (AllocReceiver& receiver, size_t sizRequest) { - + REQUIRE (sizRequest); + requestQueue_.push ({&receiver, sizRequest}); } diff --git a/tests/vault/mem/engine-buffer-manager-test.cpp b/tests/vault/mem/engine-buffer-manager-test.cpp index f06d495e2..e3f09925f 100644 --- a/tests/vault/mem/engine-buffer-manager-test.cpp +++ b/tests/vault/mem/engine-buffer-manager-test.cpp @@ -21,17 +21,26 @@ //#include "vault/mem/engine-buffer-metadata.hpp" #include "vault/mem/engine-buffer-manager.hpp" #include "vault/mem/engine-buffer-allocator.hpp" +#include "lib/scoped-collection.hpp" +#include "lib/iter-explorer.hpp" //#include "lib/depend-inject.hpp" -//#include "lib/thread.hpp" +#include "lib/thread.hpp" //#include "lib/error.hpp" #include "lib/symbol.hpp" #include "test/diagnostic-output.hpp"////////////TODO #include +//#include +#include -//using std::this_thread::yield; -//using lib::Thread; +using lib::Thread; using lib::Literal; +using lib::explore; +//using lib::ScopedCollection; +using std::this_thread::yield; +using std::this_thread::sleep_for; +using std::chrono::microseconds; +using std::chrono_literals::operator ""ms; namespace vault { namespace mem { @@ -43,6 +52,8 @@ namespace test { namespace { // Test helper + const size_t ALLOC_REQ = 1024*1024; + const size_t NUM_THREADS = 100; // template // Buff* // mark_as_Buffer(X& something) @@ -66,7 +77,7 @@ namespace test { virtual void run (Arg) { -// seedRand(); + seedRand(); demonstrate_AllocatorInterface(); verify_syncRequest(); @@ -164,10 +175,87 @@ namespace test { - /** @test show that an working allocation request across thread boundaries */ + /** @test show that an working allocation request passes thread boundaries */ void verify_asyncRequest() { + struct LocalTestPool + : AllocReceiver + { + Alloc + retrieveAlloc() + { + Alloc received{nullptr, 0}; + inQueue_.pop (received); // Note: no-op if queue is empty + return received; + } + + bool + empty() const + { + return inQueue_.empty(); + } + }; + + EngineBufferManager manager; + + using WorkBuff = std::array; + +std::atomic_int cnt{0}; + auto workSeq = [&, gen = makeRandGen()]// local random generator per thread + () mutable + {// sequence of actions within a worker thread +int i=++cnt; + LocalTestPool myPool; + CHECK (myPool.empty()); + + manager.async_requestAllocation (myPool, ALLOC_REQ); + + // meanwhile the worker does some other "work" + uint delay = 100 + gen.i(800); + sleep_for (microseconds(delay)); +SHOW_EXPR(delay) + + Alloc alloc = myPool.retrieveAlloc(); + if (alloc.empty()) + // they let us down ... + // insist harder to be serviced thusly + alloc = manager.requestAllocation (ALLOC_REQ); + + CHECK (not alloc.empty()); + CHECK (ALLOC_REQ <= alloc.siz); + // can use that allocation for some "serious work"... + auto content = new(alloc.mem) WorkBuff; + std::ranges::generate (*content, [&]{ return gen.u64(); }); + sleep_for (10ms); + + // pass allocation back to the central hub + manager.supply (alloc); +SHOW_EXPR(i) + }; + + using Threads = lib::ScopedCollection; + auto startThread = [&](auto& storage){ storage.template create (workSeq); }; + + // Start a collection of workers.... + Threads threads{NUM_THREADS, startThread}; + + // wait for all threads to finish... + while (explore(threads).has_any()) + yield(); + + // all the allocations sent back by the workers + // should sit in the EngineBufferManager's input queue +SHOW_EXPR(watch(manager).numAllocs()); +// CHECK (NUM_THREADS == watch(manager).numAllocs()); +SHOW_EXPR(watch(manager).bytesLeased()); +// CHECK (NUM_THREADS*ALLOC_REQ <= watch(manager).bytesLeased()); + + // process in-queue and retrieve all allocations.. + manager.processPendingRequests(); +SHOW_EXPR(watch(manager).numAllocs()); +SHOW_EXPR(watch(manager).bytesLeased()); +// CHECK (0 == watch(manager).bytesLeased()); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 5c981bc4b..df37ccc83 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -146914,8 +146914,60 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ hatte diese Technik vom incidence-count-test kopiert; der Code dort ist aber nicht ganz korrekt — was dort aber nicht auffällt, da das thread-Microbenchmark seine test-Funktion (das »subjekt«) nochmal kopiert, nämlich in die closure des jeweiligen Thread. Diese Kopie bewirkt dann einfach, daß der Generator nochmal kopiert wird ... bin mir aber trotzdem nicht sicher, so wirklich verschiedene Zufallszahlen gezogen werden. +

+

+ +

+

+ Jedenfalls in dem Setup, das ich jetzt hier für diesen neuen Test gebaut habe, verwenden alle Threads dasselbe Lamba für die Thread-Funktion gemeinsam. Das bedeutet, das makeRandomGen() wird tatsächlich nur einmal aufgerufen, und daher haben wir nur einen einzigen Seed (selbst wenn die Lambdas dann im Thread-Wrapper nochmal kopiert werden) +

+ + +
+ + + + + + + + +