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:
parent
abecd2cbde
commit
3b7877204d
3 changed files with 97 additions and 33 deletions
|
|
@ -62,7 +62,7 @@ namespace mem {
|
|||
* 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
|
||||
* 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
|
||||
: public BufferProviderSetup::Stage
|
||||
|
|
@ -76,10 +76,13 @@ namespace mem {
|
|||
|
||||
|
||||
/* === BufferStage interface === */
|
||||
|
||||
|
||||
ID
|
||||
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)
|
||||
: metadata::Key::INVALID;
|
||||
}
|
||||
|
|
@ -149,6 +152,7 @@ namespace mem {
|
|||
{ }
|
||||
|
||||
HashVal getFamilyID() const override { return familyID_; }
|
||||
size_t cntEntries() const { return localReg_->cntEntries(); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,31 +16,15 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "test/run.hpp"
|
||||
//#include "test/test-frame.hpp"
|
||||
#include "test/test-helper.hpp"
|
||||
#include "vault/mem/buffhandle.hpp"
|
||||
#include "vault/mem/engine-buffer-metadata.hpp"
|
||||
#include "vault/mem/local-buffer-stage.hpp"
|
||||
//#include "lib/uninitialised-storage.hpp"
|
||||
#include "lib/depend-inject.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
//#include "lib/symbol.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "test/diagnostic-output.hpp"////////////////TODO
|
||||
|
||||
#include <memory>
|
||||
#include "lib/error.hpp"
|
||||
|
||||
|
||||
//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 lib::Thread;
|
||||
|
||||
|
|
@ -49,12 +33,10 @@ namespace mem {
|
|||
namespace test {
|
||||
|
||||
using LERR_(LOGIC);
|
||||
// using LERR_(FATAL);
|
||||
// using LERR_(INVALID);
|
||||
using LERR_(LIFECYCLE);
|
||||
|
||||
|
||||
namespace { // Test fixture
|
||||
namespace { // Test helper
|
||||
|
||||
template<typename X>
|
||||
Buff*
|
||||
|
|
@ -63,7 +45,7 @@ namespace test {
|
|||
return reinterpret_cast<Buff*> (std::addressof(something));
|
||||
}
|
||||
|
||||
}//(End) Test fixture and helpers
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -83,6 +65,7 @@ namespace test {
|
|||
run (Arg)
|
||||
{
|
||||
seedRand();
|
||||
|
||||
verify_DataTransfer();
|
||||
verify_publishNewKey();
|
||||
verify_fetchNewKeyOnUse();
|
||||
|
|
@ -174,7 +157,7 @@ namespace test {
|
|||
* - notably register a totally new buffer type, from within the worker
|
||||
* - then also perform the steps necessary to lock and release a buffer
|
||||
* - 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
|
||||
verify_publishNewKey()
|
||||
|
|
@ -196,7 +179,7 @@ namespace test {
|
|||
const auto HANDLER = TypeHandler::create<double>(RANDD);
|
||||
const LocalTag TAG{defaultGen.u64()};
|
||||
|
||||
HashVal metaID{0};
|
||||
HashVal typeID{0};
|
||||
|
||||
// this is our »allocated buffer«
|
||||
double testBuffer{0};
|
||||
|
|
@ -205,7 +188,7 @@ namespace test {
|
|||
Thread testWorker{[&] /* === Use Case : a worker defines a new buffer type === */
|
||||
{
|
||||
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
|
||||
BuffAlloc allocRecord{alloc, typeKey.storageSize(),typeKey.localTag()};
|
||||
|
|
@ -222,13 +205,13 @@ namespace test {
|
|||
yield(); // wait for worker to finish
|
||||
|
||||
|
||||
CHECK (0 != metaID);
|
||||
CHECK (0 != typeID);
|
||||
CHECK (3 == metaHub->cntEntries());
|
||||
CHECK (metaHub->isKnown (metaID));
|
||||
CHECK (metaHub->isKnown (typeID));
|
||||
|
||||
// complete registration chain has been published to the central registry
|
||||
HashVal FAM_ID = localStage.getFamilyID();
|
||||
HashVal parent1 = metaHub->lookup(metaID).parentKey();
|
||||
HashVal parent1 = metaHub->lookup(typeID).parentKey();
|
||||
CHECK (metaHub->isKnown (parent1));
|
||||
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
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -144486,9 +144486,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1776566302722" ID="ID_563548473" MODIFIED="1776566323627" TEXT="ein release / dtor-call kann nicht synchronisiert werden"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1776566422332" ID="ID_773286904" MODIFIED="1776604505367" TEXT="Verbindung zur zentral-Registry">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776566441432" ID="ID_83703051" MODIFIED="1776604516229" TEXT="neue Typ-Registrierung im Thread ⟹ zur Zentrale publiziert">
|
||||
<node COLOR="#338800" CREATED="1776566422332" ID="ID_773286904" MODIFIED="1776707180399" TEXT="Verbindung zur zentral-Registry">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1776566441432" ID="ID_83703051" MODIFIED="1776707176040" TEXT="neue Typ-Registrierung im Thread ⟹ zur Zentrale publiziert">
|
||||
<node CREATED="1776604591195" ID="ID_1133340482" MODIFIED="1776604688880" TEXT="brauche dafür die zentralen EngineMetadata">
|
||||
<node COLOR="#5b280f" CREATED="1776604776169" ID="ID_1295263836" MODIFIED="1776604807258" TEXT="Aua — jetzt gibt's doch eine statische Initialisierung">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
|
|
@ -144565,7 +144565,23 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776566459396" ID="ID_20651536" MODIFIED="1776604516229" TEXT="Verwendung im Thread ⟸ Typ aus Zentrale synchronisiert"/>
|
||||
<node COLOR="#435e98" CREATED="1776566459396" ID="ID_20651536" MODIFIED="1776707176041" TEXT="Verwendung im Thread ⟸ 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ß in lookup() sitzen"/>
|
||||
<node CREATED="1776706983787" ID="ID_1432209099" MODIFIED="1776707010485" TEXT="der wird nä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ß tatsä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ür den Test relevant (aber schadet nicht)"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1776204575221" ID="ID_869340284" MODIFIED="1776204579271" TEXT="Store-Slice"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue