BufferProvider: implement and verify the second case for synchronisation

...which happens when a buffer type is referred for the first time
from within a new worker thread, while it is already stored in the
global registry; the full key-chain is down-synced then.
This commit is contained in:
Fischlurch 2026-04-20 19:48:20 +02:00
parent abecd2cbde
commit 3b7877204d
3 changed files with 97 additions and 33 deletions

View file

@ -62,7 +62,7 @@ namespace mem {
* defined anew in the worker, or when a worker refers for the first * defined anew in the worker, or when a worker refers for the first
* time to an type predefined from the Builder. In those (rare) cases * time to an type predefined from the Builder. In those (rare) cases
* however, a mutex synchronisation is necessary to protect the global * however, a mutex synchronisation is necessary to protect the global
* metadata table against corruption and ensure synchronous response: * metadata table against corruption and ensure synchronous response.
*/ */
class LocalBufferStage class LocalBufferStage
: public BufferProviderSetup::Stage : public BufferProviderSetup::Stage
@ -80,6 +80,9 @@ namespace mem {
ID ID
lookup (HashVal key) override lookup (HashVal key) override
{ {
if (not localReg_->isKnown(key))
globalReg_().propagateDown(key, *localReg_);
// try it again after sync with central registry
return localReg_->isKnown(key)? localReg_->get (key) return localReg_->isKnown(key)? localReg_->get (key)
: metadata::Key::INVALID; : metadata::Key::INVALID;
} }
@ -149,6 +152,7 @@ namespace mem {
{ } { }
HashVal getFamilyID() const override { return familyID_; } HashVal getFamilyID() const override { return familyID_; }
size_t cntEntries() const { return localReg_->cntEntries(); }
}; };

View file

@ -16,31 +16,15 @@
*/ */
#include "lib/error.hpp"
#include "test/run.hpp" #include "test/run.hpp"
//#include "test/test-frame.hpp"
#include "test/test-helper.hpp" #include "test/test-helper.hpp"
#include "vault/mem/buffhandle.hpp"
#include "vault/mem/engine-buffer-metadata.hpp" #include "vault/mem/engine-buffer-metadata.hpp"
#include "vault/mem/local-buffer-stage.hpp" #include "vault/mem/local-buffer-stage.hpp"
//#include "lib/uninitialised-storage.hpp"
#include "lib/depend-inject.hpp" #include "lib/depend-inject.hpp"
#include "lib/thread.hpp" #include "lib/thread.hpp"
//#include "lib/symbol.hpp" #include "lib/error.hpp"
#include "lib/util.hpp"
#include "test/diagnostic-output.hpp"////////////////TODO
#include <memory>
//using std::strncpy;
//using std::unique_ptr;
//using test::TestFrame;
//using test::testData;
//using util::isSameObject;
//using util::isnil;
//using lib::randStr;
//using lib::Literal;
using std::this_thread::yield; using std::this_thread::yield;
using lib::Thread; using lib::Thread;
@ -49,12 +33,10 @@ namespace mem {
namespace test { namespace test {
using LERR_(LOGIC); using LERR_(LOGIC);
// using LERR_(FATAL);
// using LERR_(INVALID);
using LERR_(LIFECYCLE); using LERR_(LIFECYCLE);
namespace { // Test fixture namespace { // Test helper
template<typename X> template<typename X>
Buff* Buff*
@ -63,7 +45,7 @@ namespace test {
return reinterpret_cast<Buff*> (std::addressof(something)); return reinterpret_cast<Buff*> (std::addressof(something));
} }
}//(End) Test fixture and helpers }
@ -83,6 +65,7 @@ namespace test {
run (Arg) run (Arg)
{ {
seedRand(); seedRand();
verify_DataTransfer(); verify_DataTransfer();
verify_publishNewKey(); verify_publishNewKey();
verify_fetchNewKeyOnUse(); verify_fetchNewKeyOnUse();
@ -174,7 +157,7 @@ namespace test {
* - notably register a totally new buffer type, from within the worker * - notably register a totally new buffer type, from within the worker
* - then also perform the steps necessary to lock and release a buffer * - then also perform the steps necessary to lock and release a buffer
* - check that the new type was migrated into the central metadata hub * - check that the new type was migrated into the central metadata hub
* - verify that also the complete paretn-chain was published * - verify that also the complete parent-chain was published
*/ */
void void
verify_publishNewKey() verify_publishNewKey()
@ -196,7 +179,7 @@ namespace test {
const auto HANDLER = TypeHandler::create<double>(RANDD); const auto HANDLER = TypeHandler::create<double>(RANDD);
const LocalTag TAG{defaultGen.u64()}; const LocalTag TAG{defaultGen.u64()};
HashVal metaID{0}; HashVal typeID{0};
// this is our »allocated buffer« // this is our »allocated buffer«
double testBuffer{0}; double testBuffer{0};
@ -205,7 +188,7 @@ namespace test {
Thread testWorker{[&] /* === Use Case : a worker defines a new buffer type === */ Thread testWorker{[&] /* === Use Case : a worker defines a new buffer type === */
{ {
auto& typeKey = stageAPI.defineBufferType (SIZ_D, HANDLER, TAG); // ◁─────────────────┨ data up-sync happens here auto& typeKey = stageAPI.defineBufferType (SIZ_D, HANDLER, TAG); // ◁─────────────────┨ data up-sync happens here
metaID = HashVal(typeKey); // sneak out to verify later typeID = HashVal(typeKey); // sneak out to verify later
// emulate allocation and usage of a buffer // emulate allocation and usage of a buffer
BuffAlloc allocRecord{alloc, typeKey.storageSize(),typeKey.localTag()}; BuffAlloc allocRecord{alloc, typeKey.storageSize(),typeKey.localTag()};
@ -222,13 +205,13 @@ namespace test {
yield(); // wait for worker to finish yield(); // wait for worker to finish
CHECK (0 != metaID); CHECK (0 != typeID);
CHECK (3 == metaHub->cntEntries()); CHECK (3 == metaHub->cntEntries());
CHECK (metaHub->isKnown (metaID)); CHECK (metaHub->isKnown (typeID));
// complete registration chain has been published to the central registry // complete registration chain has been published to the central registry
HashVal FAM_ID = localStage.getFamilyID(); HashVal FAM_ID = localStage.getFamilyID();
HashVal parent1 = metaHub->lookup(metaID).parentKey(); HashVal parent1 = metaHub->lookup(typeID).parentKey();
CHECK (metaHub->isKnown (parent1)); CHECK (metaHub->isKnown (parent1));
CHECK (parent1 == metaHub->defineBufferType (FAM_ID, SIZ_D, HANDLER)); CHECK (parent1 == metaHub->defineBufferType (FAM_ID, SIZ_D, HANDLER));
@ -240,9 +223,70 @@ namespace test {
} }
/** @test simulate another common buffer provider usage situation,
* where a local instance starts using a type that was previously
* defined in the central registry.
* - setup an empty new EngineBufferMetadata end inject it as mock
* - this time however a new type is defined into this central registry
* - in a new worker thread, the local registry is initially empty
* - yet when a lookup fails, a down-sync from the global registry happens
* - after that the type is also known locally and can be used for allocations.
* - nothing is published back and so any state records remain conined
* to the current worker thread
*/
void void
verify_fetchNewKeyOnUse() verify_fetchNewKeyOnUse()
{ {
lib::DependInject<EngineBufferMetadata>::Local<EngineBufferMetadata> metaHub;
// setup information used for buffer type registration
const size_t SIZ_D = sizeof(double);
const double RANDD = 1.0 + defaultGen.uni();
const auto HANDLER = TypeHandler::create<double>(RANDD);
LocalBufferStage localStage{"verify_fetchNewKeyOnUse"};
BufferProviderSetup::Stage& stageAPI = localStage;
HashVal FAM_ID = localStage.getFamilyID();
CHECK (FAM_ID == hash_value (Literal{"verify_fetchNewKeyOnUse"}));
metaHub.triggerCreate();
HashVal typeID = metaHub->defineBufferType (FAM_ID, SIZ_D, HANDLER);
CHECK (metaHub->isKnown(typeID));
CHECK (metaHub->lookup(typeID).storageSize() == SIZ_D);
CHECK (2 == metaHub->cntEntries());
Thread testWorker{[&] /* === Use Case : worker uses a buffer type defined "elsewhere" type === */
{
// initially the thread-local registry is empty
CHECK (0 == localStage.cntEntries());
auto& typeKey = stageAPI.lookup(typeID); // ◁─────────────────────────────────────────┨ data down-sync happens here
CHECK (typeKey != metadata::Key::INVALID);
CHECK (typeKey.storageSize() == SIZ_D);
CHECK (typeKey.localTag() == LocalTag::UNKNOWN);
CHECK (2 == localStage.cntEntries());
double testBuffer{0};
Buff* alloc = mark_as_Buffer (testBuffer);
// emulate allocation and usage of a buffer
BuffAlloc allocRecord{alloc, typeKey.storageSize(),typeKey.localTag()};
auto& stateKey = stageAPI.mark_locked (typeKey, allocRecord);
CHECK (3 == localStage.cntEntries()); // one additional (state)entry
// verify the buffer constructor was applied
CHECK (testBuffer = RANDD);
// release the buffer...
stageAPI.mark_released (stateKey);
stageAPI.discard (stateKey); // ◁──────────────────────────────────────────────────┨ buffer state entry never leaves the worker thread
CHECK (2 == localStage.cntEntries());
}};
while (testWorker)
yield(); // wait for worker to finish
// no new entries in central registry
CHECK (2 == metaHub->cntEntries());
} }
}; };

View file

@ -144486,9 +144486,9 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1776566302722" ID="ID_563548473" MODIFIED="1776566323627" TEXT="ein release / dtor-call kann nicht synchronisiert werden"/> <node CREATED="1776566302722" ID="ID_563548473" MODIFIED="1776566323627" TEXT="ein release / dtor-call kann nicht synchronisiert werden"/>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1776566422332" ID="ID_773286904" MODIFIED="1776604505367" TEXT="Verbindung zur zentral-Registry"> <node COLOR="#338800" CREATED="1776566422332" ID="ID_773286904" MODIFIED="1776707180399" TEXT="Verbindung zur zentral-Registry">
<icon BUILTIN="pencil"/> <icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776566441432" ID="ID_83703051" MODIFIED="1776604516229" TEXT="neue Typ-Registrierung im Thread &#x27f9; zur Zentrale publiziert"> <node COLOR="#435e98" CREATED="1776566441432" ID="ID_83703051" MODIFIED="1776707176040" TEXT="neue Typ-Registrierung im Thread &#x27f9; zur Zentrale publiziert">
<node CREATED="1776604591195" ID="ID_1133340482" MODIFIED="1776604688880" TEXT="brauche daf&#xfc;r die zentralen EngineMetadata"> <node CREATED="1776604591195" ID="ID_1133340482" MODIFIED="1776604688880" TEXT="brauche daf&#xfc;r die zentralen EngineMetadata">
<node COLOR="#5b280f" CREATED="1776604776169" ID="ID_1295263836" MODIFIED="1776604807258" TEXT="Aua &#x2014; jetzt gibt&apos;s doch eine statische Initialisierung"> <node COLOR="#5b280f" CREATED="1776604776169" ID="ID_1295263836" MODIFIED="1776604807258" TEXT="Aua &#x2014; jetzt gibt&apos;s doch eine statische Initialisierung">
<icon BUILTIN="button_cancel"/> <icon BUILTIN="button_cancel"/>
@ -144565,7 +144565,23 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node> </node>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776566459396" ID="ID_20651536" MODIFIED="1776604516229" TEXT="Verwendung im Thread &#x27f8; Typ aus Zentrale synchronisiert"/> <node COLOR="#435e98" CREATED="1776566459396" ID="ID_20651536" MODIFIED="1776707176041" TEXT="Verwendung im Thread &#x27f8; Typ aus Zentrale synchronisiert">
<node COLOR="#435e98" CREATED="1776704448745" ID="ID_1701721015" MODIFIED="1776707039908" TEXT="wo wird der down-sync eingeschleift?">
<icon BUILTIN="help"/>
<node CREATED="1776704463019" ID="ID_1647332020" MODIFIED="1776704470100" TEXT="mu&#xdf; in lookup() sitzen"/>
<node CREATED="1776706983787" ID="ID_1432209099" MODIFIED="1776707010485" TEXT="der wird n&#xe4;mlich auch bereits von BufferProvider::lockBuffer() verwendet"/>
<node CREATED="1776707011321" ID="ID_70753884" MODIFIED="1776707021020" TEXT="auch andere Informations-Funktionen machen einen lookup()"/>
</node>
<node COLOR="#338800" CREATED="1776707043517" ID="ID_1534309023" MODIFIED="1776707166222" TEXT="Test">
<icon BUILTIN="button_ok"/>
<node CREATED="1776707049394" ID="ID_1727759495" MODIFIED="1776707052989" TEXT="Aufbau ganz analog"/>
<node COLOR="#338800" CREATED="1776707072891" ID="ID_1256499400" MODIFIED="1776707169760" TEXT="mir fehlt ein Beleg da&#xdf; tats&#xe4;chlich eine Synchronisation stattfindet">
<icon BUILTIN="button_ok"/>
<node CREATED="1776707101847" ID="ID_1368500895" MODIFIED="1776707138447" TEXT="lege die cntEntries() auf die LocalBufferStage-Schnittstelle"/>
<node CREATED="1776707139396" ID="ID_1964413531" MODIFIED="1776707159816" TEXT="das ist nur f&#xfc;r den Test relevant (aber schadet nicht)"/>
</node>
</node>
</node>
</node> </node>
</node> </node>
<node CREATED="1776204575221" ID="ID_869340284" MODIFIED="1776204579271" TEXT="Store-Slice"/> <node CREATED="1776204575221" ID="ID_869340284" MODIFIED="1776204579271" TEXT="Store-Slice"/>