BufferProvider: draft mechanism for buffer metadata synchronisation
Since the intention is to run several thread-local instances of `BufferMetadata` in a concurrent setup, we need a way to publish and exchange chains of registered type-keys and metadata entries. The first step is to develop a mechanic for data exchange, and to define the rules when such synchronisation is (not) allowed.
This commit is contained in:
parent
9bd3e550dc
commit
c05a73d68a
5 changed files with 519 additions and 19 deletions
|
|
@ -58,12 +58,12 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/hash-value.h"
|
||||
#include "lib/util-foreach.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "vault/mem/buffhandle.hpp"
|
||||
#include "vault/mem/type-handler.hpp"
|
||||
#include "vault/mem/buffer-local-tag.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ namespace vault{
|
|||
namespace mem {
|
||||
|
||||
using lib::HashVal;
|
||||
using util::for_each;
|
||||
using util::unConst;
|
||||
|
||||
namespace err = lumiera::error;
|
||||
|
||||
|
|
@ -98,6 +98,18 @@ namespace mem {
|
|||
BLOCKED ///< allocated buffer blocked by protocol failure
|
||||
};
|
||||
|
||||
inline bool
|
||||
isValidBufferTransition (BufferState oldState, BufferState newState)
|
||||
{
|
||||
return ( (oldState == FREE and newState == LOCKED)
|
||||
or (oldState == LOCKED and newState == EMITTED)
|
||||
or (oldState == LOCKED and newState == BLOCKED)
|
||||
or (oldState == LOCKED and newState == FREE)
|
||||
or (oldState == EMITTED and newState == BLOCKED)
|
||||
or (oldState == EMITTED and newState == FREE)
|
||||
or (oldState == BLOCKED and newState == FREE)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -357,15 +369,8 @@ namespace mem {
|
|||
{
|
||||
__must_not_be_NIL();
|
||||
|
||||
if ( (state_ == FREE and newState == LOCKED)
|
||||
or (state_ == LOCKED and newState == EMITTED)
|
||||
or (state_ == LOCKED and newState == BLOCKED)
|
||||
or (state_ == LOCKED and newState == FREE)
|
||||
or (state_ == EMITTED and newState == BLOCKED)
|
||||
or (state_ == EMITTED and newState == FREE)
|
||||
or (state_ == BLOCKED and newState == FREE))
|
||||
{
|
||||
// allowed transition
|
||||
if (isValidBufferTransition (state_, newState))
|
||||
{ // perform transition
|
||||
if (newState == FREE)
|
||||
invokeEmbeddedDtor_and_clear();
|
||||
if (newState == LOCKED)
|
||||
|
|
@ -534,7 +539,8 @@ namespace mem {
|
|||
verify_all_buffers_freed()
|
||||
try
|
||||
{
|
||||
for_each (entries_, verify_is_free);
|
||||
for (auto const& entry : entries_)
|
||||
verify_is_free(entry);
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (engine,"Shutdown of BufferProvider metadata store")
|
||||
|
||||
|
|
@ -671,6 +677,12 @@ namespace mem {
|
|||
return *entry;
|
||||
}
|
||||
|
||||
Entry const&
|
||||
get (HashVal hashID) const
|
||||
{
|
||||
return unConst(this)->get (hashID);
|
||||
}
|
||||
|
||||
bool
|
||||
isKnown (HashVal key) const
|
||||
{
|
||||
|
|
@ -766,6 +778,22 @@ namespace mem {
|
|||
}
|
||||
|
||||
|
||||
/** Synchronise metadata, including the key path up to root
|
||||
* @param key start point of data exchange
|
||||
* @param srcReg another BufferMetadata registry from which
|
||||
* metadata shall be retrieved and imported
|
||||
*/
|
||||
void
|
||||
import (HashVal key, BufferMetadata const& srcReg)
|
||||
{
|
||||
if (not srcReg.isKnown (key))
|
||||
return;
|
||||
Entry const& entry{srcReg.get(key)};
|
||||
doSynchronise (entry);
|
||||
import (entry.parentKey(), srcReg);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/** Core low-level operation to access or create a buffer metadata entry.
|
||||
* The hashID of the entry in question is built, based on the parentKey,
|
||||
|
|
@ -861,6 +889,35 @@ namespace mem {
|
|||
}
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
|
||||
/** import data from another metadata entry,
|
||||
* without violating sanity rules
|
||||
* @remark usually this is used to import new type-keys;
|
||||
* an active buffer entry can only be handled if it is new,
|
||||
* or implies a valid state transition.
|
||||
* @note if the buffer is not NULL, it must be the same address,
|
||||
* otherwise the keys would differ, since they are generated
|
||||
* with \ref Key::forEntry()
|
||||
*/
|
||||
void
|
||||
doSynchronise (Entry const& otherEntry)
|
||||
{
|
||||
Entry* entry = table_.fetch (otherEntry);
|
||||
if (not entry)
|
||||
table_.store (otherEntry);
|
||||
else
|
||||
if (not otherEntry.isTypeKey()
|
||||
and otherEntry.state() != entry->state())
|
||||
{
|
||||
ASSERT (not entry->isTypeKey(), "Metadata State-Engine corrupted");
|
||||
if ( otherEntry.state() == FREE
|
||||
or otherEntry.state() == LOCKED)
|
||||
throw err::Logic ("Metadata synchronisation can not imply allocation/deallocation.");
|
||||
else
|
||||
entry->mark(otherEntry.state());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
141
src/vault/mem/local-buffer-stage.hpp
Normal file
141
src/vault/mem/local-buffer-stage.hpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
SIMPLE-BUFFER-STATE-REGISTRY.hpp - buffer state management with thread-local sub-services
|
||||
|
||||
Copyright (C)
|
||||
2026 Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
**Lumiera** is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See the file COPYING for further details.
|
||||
|
||||
*/
|
||||
|
||||
/** @file local-buffer-stage.hpp
|
||||
** Production-grade implementation of engine::BufferProvider state coordination.
|
||||
** Requests to the BufferProvider will be issued from all threads concurrently,
|
||||
** which requires to distinguish between known common state and particular state
|
||||
** relevant only for the current worker. Whenever a _buffer type_ is registered,
|
||||
** this information could become relevant later, when actually a buffer is locked
|
||||
** with this type, and such a request for an actual buffer can be encountered
|
||||
** in any thread. While, on the other hand, the state transitions for actual
|
||||
** buffer usage will happen within a single job invocation, and can thus be
|
||||
** handled locally within the corresponding worker thread. An allocated buffer
|
||||
** that is passed into the frame cache is yet another story, and will be handled
|
||||
** by a dedicated _cache provider_.
|
||||
**
|
||||
** Together these observations lead to a structure with locally replicated copies
|
||||
** of the buffer metadata store, where additional type registration information
|
||||
** is requested from the central metadata hub on-demand. Yet the front-end for
|
||||
** accessing the \ref BufferProvider is a common service of the Render Engine,
|
||||
** and thus the switch to the thread-local metadata management has to happen
|
||||
** somewhere in-between. This can be achieved by connecting the BufferMetadata
|
||||
** as a \ref lib::LocalSlice as part of the \ref BufferProvider::BufferStage
|
||||
** implementation — as provided by this class, LocalBufferStage.
|
||||
**
|
||||
** @see buffer-provider.hpp
|
||||
** @see buffer-provider-protocol-test.cpp
|
||||
** @see simple-buffer-state-registry.hpp
|
||||
*/
|
||||
|
||||
#ifndef VAULT_MEM_LOCAL_BUFFER_STAGE_H
|
||||
#define VAULT_MEM_LOCAL_BUFFER_STAGE_H
|
||||
|
||||
|
||||
#include "vault/mem/buffer-metadata.hpp"
|
||||
#include "vault/mem/buffer-provider-setup.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace vault {
|
||||
namespace mem {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple Buffer type and state tracking registry, for test and demonstration.
|
||||
* Relies on a central hashtable, without considering any concurrency concerns.
|
||||
*/
|
||||
class LocalBufferStage
|
||||
: public BufferProviderSetup::Stage
|
||||
{
|
||||
HashVal familyID_;
|
||||
BufferMetadata metadata_;
|
||||
|
||||
/* === BufferStage interface === */
|
||||
|
||||
ID
|
||||
lookup (HashVal key) override
|
||||
{
|
||||
return metadata_.isKnown(key)? metadata_.get (key)
|
||||
: metadata::Key::INVALID;
|
||||
}
|
||||
|
||||
bool
|
||||
isAllotted (HashVal stateKey) const override
|
||||
{
|
||||
return metadata_.isLocked (stateKey);
|
||||
}
|
||||
|
||||
bool
|
||||
isAccessible (HashVal stateKey) const override
|
||||
{
|
||||
return metadata_.isAccessible (stateKey);
|
||||
}
|
||||
|
||||
ID
|
||||
defineBufferType (size_t buffSiz, TypeHandler handlerFunctions, LocalTag localTag)
|
||||
{
|
||||
return lookup (metadata_.key (familyID_, buffSiz, move (handlerFunctions), localTag));
|
||||
} // deliberately: maybe create storage, and return reference to it
|
||||
|
||||
ID
|
||||
mark_locked (ID typeKey, BuffAlloc alloc) override
|
||||
{
|
||||
auto& [storage,size,implMark] = alloc;
|
||||
return metadata_.markLocked (typeKey, storage, size, implMark);
|
||||
}
|
||||
|
||||
ID
|
||||
mark_emitted (HashVal stateKey) override
|
||||
{
|
||||
metadata::Entry& metaEntry = metadata_.get (stateKey);
|
||||
metaEntry.mark(EMITTED);
|
||||
return metaEntry; // contains also the key
|
||||
}
|
||||
|
||||
ID
|
||||
mark_released (HashVal stateKey) override
|
||||
{
|
||||
metadata::Entry& metaEntry = metadata_.get (stateKey);
|
||||
metaEntry.mark(FREE); // might invoke embedded dtor function
|
||||
return metaEntry;
|
||||
}
|
||||
|
||||
ID
|
||||
abandon (HashVal stateKey, bool invokeDtor) override
|
||||
{
|
||||
metadata::Entry& metaEntry = metadata_.get (stateKey);
|
||||
metaEntry.invalidate (invokeDtor);
|
||||
return metaEntry;
|
||||
}
|
||||
|
||||
void
|
||||
discard (HashVal stateKey) override
|
||||
{
|
||||
metadata_.release (stateKey);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
LocalBufferStage (Literal implementationID)
|
||||
: familyID_{hash_value (implementationID)}
|
||||
, metadata_{}
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
|
||||
}} // namespace vault::mem
|
||||
#endif /*VAULT_MEM_LOCAL_BUFFER_STAGE_H*/
|
||||
|
|
@ -72,6 +72,11 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "Local buffer state management" LocalBufferStage_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Mock support for render job planning" MockSupport_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
|
|
|||
195
tests/vault/mem/local-buffer-stage-test.cpp
Normal file
195
tests/vault/mem/local-buffer-stage-test.cpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
LocalBufferStage(Test) - verify thread-local buffer state management
|
||||
|
||||
Copyright (C)
|
||||
2026, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
**Lumiera** is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version. See the file COPYING for further details.
|
||||
|
||||
* *****************************************************************/
|
||||
|
||||
/** @file local-buffer-stage-test.cpp
|
||||
** unit test \ref LocalBufferStage_test
|
||||
*/
|
||||
|
||||
|
||||
#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/buffer-metadata.hpp"
|
||||
#include "vault/mem/local-buffer-stage.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#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;
|
||||
|
||||
namespace vault {
|
||||
namespace mem {
|
||||
namespace test {
|
||||
|
||||
using LERR_(LOGIC);
|
||||
// using LERR_(FATAL);
|
||||
// using LERR_(INVALID);
|
||||
using LERR_(LIFECYCLE);
|
||||
|
||||
|
||||
namespace { // Test fixture
|
||||
|
||||
template<typename TY>
|
||||
TY&
|
||||
accessAs (metadata::Entry& entry)
|
||||
{
|
||||
TY* ptr = reinterpret_cast<TY*> (entry.access());
|
||||
ASSERT (ptr);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
Buff*
|
||||
mark_as_Buffer(X& something)
|
||||
{
|
||||
return reinterpret_cast<Buff*> (std::addressof(something));
|
||||
}
|
||||
|
||||
|
||||
const size_t TEST_MAX_SIZE = 1024 * 1024;
|
||||
|
||||
HashVal JUST_SOMETHING = 123;
|
||||
auto SOME_POINTER = mark_as_Buffer(JUST_SOMETHING);
|
||||
|
||||
}//(End) Test fixture and helpers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************//**
|
||||
* @test verify mechanism for a thread-local BufferStage implementation
|
||||
* - verify data synchronisation between several BufferMetadata registries
|
||||
* - verify typical usage situations, where a thread-local registry
|
||||
* has to synchronise itself with a central metadata hub.
|
||||
*/
|
||||
class LocalBufferStage_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
seedRand();
|
||||
verify_DataTransfer();
|
||||
verify_publishNewKey();
|
||||
verify_fetchNewKeyOnUse();
|
||||
}
|
||||
|
||||
|
||||
/** @test key-chains can be exported and imported */
|
||||
void
|
||||
verify_DataTransfer()
|
||||
{
|
||||
BufferMetadata meta1, meta2;
|
||||
|
||||
const HashVal FAM = ranHash();
|
||||
const size_t SIZ_D = sizeof(double);
|
||||
const double RAND1 = 1.0 + defaultGen.uni();
|
||||
|
||||
// create one type-key in Registry-1
|
||||
metadata::Key keyD = meta1.key (FAM,SIZ_D);
|
||||
CHECK (meta1.isKnown (keyD));
|
||||
CHECK (not meta2.isKnown (keyD));
|
||||
CHECK (meta1.get(keyD).isTypeKey());
|
||||
|
||||
// create another type-key in Registry-2, this time with a constructor
|
||||
metadata::Key keyDD = meta2.key (FAM,SIZ_D, TypeHandler::create<double> (RAND1));
|
||||
CHECK (meta2.isKnown (keyDD));
|
||||
CHECK (not meta1.isKnown (keyDD));
|
||||
CHECK (meta2.get(keyDD).isTypeKey());
|
||||
|
||||
// Keys are systematic => KeyD happens to be parent of KeyDD
|
||||
CHECK (keyD.storageSize() == keyDD.storageSize());
|
||||
CHECK (keyDD.parentKey() == HashVal(keyD));
|
||||
|
||||
// synchronise a sub-type
|
||||
meta1.import (keyDD, meta2);
|
||||
CHECK (meta1.isKnown (keyDD));
|
||||
|
||||
// now Registry-1 is able to use this subtype as well
|
||||
double testBuffer{0};
|
||||
metadata::Entry& eDD = meta1.markLocked (keyDD, mark_as_Buffer(testBuffer));
|
||||
CHECK (eDD.state() == LOCKED);
|
||||
CHECK (eDD.parentKey() == HashVal(keyDD));
|
||||
// and the »constructor« was indeed invoked...
|
||||
CHECK (testBuffer == RAND1);
|
||||
testBuffer += 1.1; // add marker...
|
||||
|
||||
// within the confines of allowed state transitions,
|
||||
// it is even possible to sync the state of active buffer entries
|
||||
CHECK (meta1.isKnown (eDD));
|
||||
CHECK (not meta2.isKnown (eDD));
|
||||
meta2.import (eDD, meta1);
|
||||
CHECK (meta2.isKnown (eDD));
|
||||
CHECK (meta2.get(eDD).state() == LOCKED);
|
||||
|
||||
// importing the state record did not re-invoke the constructor
|
||||
CHECK (testBuffer == RAND1 + 1.1);
|
||||
|
||||
meta2.get(eDD).mark(EMITTED);
|
||||
CHECK (meta2.get(eDD).state() == EMITTED);
|
||||
CHECK (meta1.get(eDD).state() == LOCKED);
|
||||
meta1.import (eDD, meta2);
|
||||
CHECK (meta1.get(eDD).state() == EMITTED);
|
||||
|
||||
// now mark the buffer as released in Registry-1
|
||||
CHECK (eDD.state() == EMITTED);
|
||||
eDD.mark(FREE);
|
||||
CHECK (eDD.state() == FREE);
|
||||
CHECK (meta2.get(eDD).state() == EMITTED);
|
||||
|
||||
// it is not allowed to mark de-allocated through synchronisation
|
||||
VERIFY_ERROR (LOGIC, meta2.import (eDD, meta1) );
|
||||
|
||||
VERIFY_ERROR (LIFECYCLE, meta2.release(HashVal(eDD)) );
|
||||
CHECK (meta2.get(eDD).state() == EMITTED);
|
||||
meta2.get(eDD).invalidate (false);
|
||||
CHECK (meta2.get(eDD).state() == FREE);
|
||||
meta2.release(eDD);
|
||||
meta1.release(eDD);
|
||||
}
|
||||
|
||||
|
||||
/** @test simulate a standard buffer provider usage cycle
|
||||
*/
|
||||
void
|
||||
verify_publishNewKey()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_fetchNewKeyOnUse()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (LocalBufferStage_test, "unit engine");
|
||||
|
||||
|
||||
|
||||
}}} // namespace vault::mem::test
|
||||
|
|
@ -144223,13 +144223,35 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1776204526672" ID="ID_961403309" MODIFIED="1776477618192" TEXT="Prototyping (testgetrieben)">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1776204565654" ID="ID_45614742" MODIFIED="1776204569140" TEXT="generischer Slice">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1776293021744" ID="ID_290625266" MODIFIED="1776522855937" TEXT="Problem: dieses Setup ist effektiv statisch/global">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das erfordert Vorsicht, weil man dadurch die Eigenschaft einer geschlossenen Service-Instanz verliert; es ist also leider kein drop-in-replacement, mit dem man einfach eine tief eingebettete Koponente thread-local macht; theoretisch würde letzteres zwar gehen, aber dann auf einen zweistufigen Dispatch hinauslaufen, d.h. nach dem thread-local-Zugriff müßte noch ein Dispatch über eine Map per umschließender Service-Instanz erfolgen.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
In dem Fall beschränke ich mich aber letztlich auf unsere Anforderungen hier, und da brauchen wir tatsähchlich nur eine globale Instanz — ich hatte mir ja sogar schon überlegt, ob man nicht sogar die BufferMetadata koplett als separaten Service herauslösen müßte; das bekommen wir hier nun als Nebeneffekt geschenkt
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#a61032" DESTINATION="ID_808171568" ENDARROW="Default" ENDINCLINATION="11;-94;" ID="Arrow_ID_66477268" STARTARROW="None" STARTINCLINATION="259;15;"/>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1776204565654" ID="ID_45614742" MODIFIED="1776522493293" TEXT="generischer Slice">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1776292208608" ID="ID_1141218614" MODIFIED="1776477482889" TEXT="Zweck: Anbinden eines Slice in einem normalen Objekt-Kontext">
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1776292987880" ID="ID_808037956" MODIFIED="1776477504910" TEXT="Zugriff über Pointer-artige Syntax">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1776293021744" ID="ID_808171568" MODIFIED="1776477489785" TEXT="Problem: thread_local ist implizit statisch">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1776293021744" ID="ID_808171568" MODIFIED="1776522852160" TEXT="Problem: thread_local ist implizit statisch">
|
||||
<linktarget COLOR="#a61032" DESTINATION="ID_808171568" ENDARROW="Default" ENDINCLINATION="11;-94;" ID="Arrow_ID_66477268" SOURCE="ID_290625266" STARTARROW="None" STARTINCLINATION="259;15;"/>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1776293040334" ID="ID_555811092" MODIFIED="1776293060463" TEXT="das heißt es ist eine »versteckte globale Variable«"/>
|
||||
<node CREATED="1776295872712" ID="ID_906429004" MODIFIED="1776295889501" TEXT="stellt das für den konkteten Use-Case überhaupt eine Beschränkung dar?">
|
||||
|
|
@ -144324,8 +144346,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
man muß den aktuellen Namespace schließen, in lib gehen, dort die explizite Spezialisierung definieren...
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="smiley-angry"/>
|
||||
</node>
|
||||
<node CREATED="1776521934417" ID="ID_130169122" MODIFIED="1776522032176" TEXT="Lösung: 2.Template-Parameter mit Default-Init">
|
||||
|
|
@ -144341,8 +144362,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
direkt bei LocalSlice definiert; erzeugt eine default-konstruierte Instanz vom Service
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1776522006689" ID="ID_1991943573" MODIFIED="1776522019517" TEXT="getested: Spezialisierung ist möglich">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -144391,7 +144411,89 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1776204569932" ID="ID_1285006287" MODIFIED="1776204574453" TEXT="Stage-Slice"/>
|
||||
<node CREATED="1776204569932" ID="ID_1285006287" MODIFIED="1776204574453" TEXT="Stage-Slice">
|
||||
<node COLOR="#338800" CREATED="1776522893322" ID="ID_1694212347" MODIFIED="1776566332195" TEXT="BufferMetadata um Migration erweitern">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1776556044669" ID="ID_857328588" MODIFIED="1776556055571" TEXT="Begriff: Key-chain">
|
||||
<node CREATED="1776556089572" ID="ID_565019641" MODIFIED="1776556103328" TEXT="ein Entry mit allen seinen Parent-Entries"/>
|
||||
</node>
|
||||
<node CREATED="1776556171704" ID="ID_1788136226" MODIFIED="1776556247063" TEXT="Zweck: Abgleich zwischen zwei Metadata-Tabellen">
|
||||
<node CREATED="1776556712396" ID="ID_346498254" MODIFIED="1776556720533" TEXT="Ergänzen fehlender Informationen"/>
|
||||
<node CREATED="1776556725461" ID="ID_154374768" MODIFIED="1776556776087" TEXT="beschränkter Abgleich bestimmter Daten"/>
|
||||
<node CREATED="1776556777026" ID="ID_441313668" MODIFIED="1776556800521" TEXT="Exception falls inkonsistenter Abgleich verlangt wird">
|
||||
<node CREATED="1776556809443" ID="ID_480351503" MODIFIED="1776556822013" TEXT="ein Buffer darf nicht NULL werden"/>
|
||||
<node CREATED="1776556823233" ID="ID_964120138" MODIFIED="1776556838587" TEXT="State darf nur erlaubten Transitionen folgen"/>
|
||||
<node CREATED="1776556866299" ID="ID_1595480658" MODIFIED="1776557157499" TEXT="setzt implizit einen logisch kohärenten Aufruf der Synchronisation vorraus">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das bedeutet, die hier aufgestellten Regeln/Verbote sind so konstruiert, daß sie bei einem sinnvollen Abgleich niemals auftreten können.  Der Sinn ergibt sich dabei aus den vorgesehenen Zustandsübergängen; es sollten nur Typ-Informationen angereichert werden, und konkrete Einträge sollten eigentlich gar nicht synchronisiert werden; hilfsweise sehe ich die Möglichkeit vor, daß konkrete Einträge sich gemäß State-Machine vorwärts bewegen (falls wir tatsächlich konkrete Einträge zentral ablegen, wenn sie in den Cache wandern — ich kann diese Möglichkeit derzeit nicht ausschließen)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1776557583845" ID="ID_1234349568" MODIFIED="1776566359211" TEXT="Schnittstelle / Mechanismus">
|
||||
<icon BUILTIN="list"/>
|
||||
<node CREATED="1776557598572" ID="ID_930030080" MODIFIED="1776557685683" TEXT="hier tritt das Problem der Storage auf">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
es sollte nur alloziert werden, wenn das auch notwendig ist; man könnte entweder einen Buffer vorsehen, oder direkt von Quelle zu Ziel arbeiten.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1776557687442" ID="ID_1438523066" MODIFIED="1776557735700" TEXT="Einsicht: das hier ist Implementierung / es geht nur um Komplexität">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
es müssen keine Komponenten gegeneinander isoliert werden; die Schnittstelle dient nur dazu, den vorgang leicht beherrschbar zu machen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1776557770647" ID="ID_1041865636" MODIFIED="1776557793535" TEXT="⟹ Operation sitzt auf dem Empfänger und bekommt die Quelle als const"/>
|
||||
<node CREATED="1776557806144" ID="ID_1595165986" MODIFIED="1776557808303" TEXT="Logik">
|
||||
<node CREATED="1776557909652" ID="ID_1185060059" MODIFIED="1776557938771" TEXT="wenn Quelle den Key nicht hat ⟹ fertig">
|
||||
<linktarget COLOR="#a9b4c1" DESTINATION="ID_1185060059" ENDARROW="Default" ENDINCLINATION="102;0;" ID="Arrow_ID_680660049" SOURCE="ID_294261855" STARTARROW="None" STARTINCLINATION="102;0;"/>
|
||||
</node>
|
||||
<node CREATED="1776557809545" ID="ID_1932336352" MODIFIED="1776557831449" TEXT="wenn ich den Key nicht habe ⟹ Daten kopieren"/>
|
||||
<node CREATED="1776557832463" ID="ID_727929224" MODIFIED="1776557843944" TEXT="wenn ich den Key habe ⟹ Abgleich mit Konsistenzcheck"/>
|
||||
<node CREATED="1776557887075" ID="ID_294261855" MODIFIED="1776557938772" TEXT="rekursiv den Abgleich für den parent-Hash auslösen">
|
||||
<arrowlink DESTINATION="ID_1185060059" ENDARROW="Default" ENDINCLINATION="102;0;" ID="Arrow_ID_680660049" STARTARROW="None" STARTINCLINATION="102;0;"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1776566204691" ID="ID_1993707384" MODIFIED="1776566327373" TEXT="Test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1776566208368" ID="ID_318163591" MODIFIED="1776566219745" TEXT="verwende zwei lokal instantiierte BufferMetadata-Registries"/>
|
||||
<node CREATED="1776566220517" ID="ID_268834894" MODIFIED="1776566230815" TEXT="befülle sie schrittweise mit Typ-Keys"/>
|
||||
<node CREATED="1776566231540" ID="ID_368942183" MODIFIED="1776566246638" TEXT="synchronsiere jeweils hin- und her"/>
|
||||
<node CREATED="1776566247466" ID="ID_268191870" MODIFIED="1776566267353" TEXT="zeige daß die Metadaten auch in der enderen Registry sind"/>
|
||||
<node CREATED="1776566268399" ID="ID_1584729655" MODIFIED="1776566286040" TEXT="lege sogar einen aktiven Buffer-Entry an..."/>
|
||||
<node CREATED="1776566286951" ID="ID_1237749199" MODIFIED="1776566301833" TEXT="kann auch hier synchronisieren, solange die State-Transitions valide sind"/>
|
||||
<node CREATED="1776566302722" ID="ID_563548473" MODIFIED="1776566323627" TEXT="ein release / dtor-call kann nicht synchronisiert werden"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1776566422332" ID="ID_773286904" MODIFIED="1776566436334" TEXT="Verbindung zur zentral-Registry">
|
||||
<node CREATED="1776566441432" ID="ID_83703051" MODIFIED="1776566458537" TEXT="neue Typ-Registrierung im Thread ⟹ zur Zentrale publiziert"/>
|
||||
<node CREATED="1776566459396" ID="ID_20651536" MODIFIED="1776566492396" TEXT="Verwendung im Thread ⟸ Typ aus Zentrale synchronisiert"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1776204575221" ID="ID_869340284" MODIFIED="1776204579271" TEXT="Store-Slice"/>
|
||||
<node CREATED="1776204694205" ID="ID_1617552510" MODIFIED="1776204701641" TEXT="WorkerBufferStage">
|
||||
<node CREATED="1776436907819" ID="ID_1105234662" MODIFIED="1776439761005" TEXT="Rolle der RootID klären">
|
||||
|
|
|
|||
Loading…
Reference in a new issue