Invocation: analyse usage of buffer metadata entries

Code clean-up: mark all buffers with a dedicated tagging type


The point in question is: if we work the LocalTag into the type-hash,
could it be possible to miss an existing entry in the metadata registry?
This could cause two entries to be locked for a single buffer address,
leading to data corruption.

As far as I can see, in the current usage this would not happen,
but unfortunately this problem can not be ruled out, since the BufferProvider
API and protocol is designed to be open for various usage patterns.

However, the same potentially disastrous pattern could also materialise
when registering two different buffer types, and then locking each
for the same buffer location.
This commit is contained in:
Fischlurch 2024-07-28 19:29:27 +02:00
parent 72c7386435
commit 9a23aa773b
8 changed files with 170 additions and 81 deletions

View file

@ -59,6 +59,7 @@
#include "lib/hash-value.h"
#include "lib/util-foreach.hpp"
#include "include/logging.h"
#include "steam/streamtype.hpp"
#include "steam/engine/type-handler.hpp"
#include "steam/engine/buffer-local-key.hpp"
#include "lib/nocopy.hpp"
@ -76,6 +77,8 @@ namespace engine {
namespace error = lumiera::error;
namespace metadata {
using Buff = StreamType::ImplFacade::DataBuffer;
class Key;
class Entry;
}
@ -214,7 +217,7 @@ namespace engine {
* For NULL buffer a copy of the parent is returned.
*/
static Key
forEntry (Key const& parent, const void* bufferAddr, LocalTag const& localTag =LocalTag::UNKNOWN)
forEntry (Key const& parent, const Buff* bufferAddr, LocalTag const& localTag =LocalTag::UNKNOWN)
{
Key newKey{parent}; // copy of parent as baseline
if (nontrivial(localTag))
@ -270,11 +273,11 @@ namespace engine {
: public Key
{
BufferState state_;
void* buffer_;
Buff* buffer_;
protected:
Entry (Key const& parent
,void* bufferPtr =nullptr
,Buff* bufferPtr =nullptr
,LocalTag const& specialTag =LocalTag::UNKNOWN
)
: Key{Key::forEntry (parent, bufferPtr, specialTag)}
@ -313,7 +316,7 @@ namespace engine {
return state_;
}
void*
Buff*
access()
{
__must_not_be_NIL();
@ -350,7 +353,7 @@ namespace engine {
}
Entry&
lock (void* newBuffer)
lock (Buff* newBuffer)
{
__must_be_FREE();
buffer_ = newBuffer;
@ -362,7 +365,7 @@ namespace engine {
{
if (buffer_ and invokeDtor)
invokeEmbeddedDtor_and_clear();
buffer_ = 0;
buffer_ = nullptr;
state_ = FREE;
return *this;
}
@ -610,7 +613,7 @@ namespace engine {
* @note might create/register a new Entry as a side-effect
*/
Key const&
key (Key const& parentKey, void* concreteBuffer, LocalTag const& specifics =LocalTag::UNKNOWN)
key (Key const& parentKey, metadata::Buff* concreteBuffer, LocalTag const& specifics =LocalTag::UNKNOWN)
{
Key derivedKey = Key::forEntry (parentKey, concreteBuffer, specifics);
Entry* existing = table_.fetch (derivedKey);
@ -645,7 +648,7 @@ namespace engine {
*/
Entry&
lock (Key const& parentKey
,void* concreteBuffer
,metadata::Buff* concreteBuffer
,LocalTag const& specifics =LocalTag::UNKNOWN
,bool onlyNew =false)
{
@ -717,7 +720,7 @@ namespace engine {
* created, but is marked as FREE
*/
Entry&
markLocked (Key const& parentKey, void* buffer, LocalTag const& specifics =LocalTag::UNKNOWN)
markLocked (Key const& parentKey, metadata::Buff* buffer, LocalTag const& specifics =LocalTag::UNKNOWN)
{
if (!buffer)
throw error::Fatal{"Attempt to lock for a NULL buffer. Allocation floundered?"

View file

@ -102,7 +102,7 @@ namespace engine {
* actual buffer, which is locked for exclusive use by one client.
*/
BuffHandle
BufferProvider::buildHandle (HashVal typeID, void* storage, LocalTag const& localTag)
BufferProvider::buildHandle (HashVal typeID, Buff* storage, LocalTag const& localTag)
{
metadata::Key& typeKey = meta_->get (typeID);
metadata::Entry& entry = meta_->markLocked(typeKey, storage, localTag);
@ -189,7 +189,7 @@ namespace engine {
try {
metadata::Entry& metaEntry = meta_->get (handle.entryID());
metaEntry.mark(FREE); // might invoke embedded dtor function
detachBuffer (metaEntry.parentKey(), metaEntry.localTag());
detachBuffer (metaEntry.parentKey(), metaEntry.localTag(), *handle);
meta_->release (metaEntry);
}
ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider")
@ -233,7 +233,7 @@ namespace engine {
try {
metadata::Entry& metaEntry = meta_->get (target.entryID());
metaEntry.invalidate (invokeDtor);
detachBuffer (metaEntry.parentKey(), metaEntry.localTag());
detachBuffer (metaEntry.parentKey(), metaEntry.localTag(), *target);
meta_->release (metaEntry);
}
ERROR_LOG_AND_IGNORE (engine, "cleanup of buffer metadata while handling an error")

View file

@ -88,13 +88,16 @@ namespace engine {
protected: /* === for Implementation by concrete providers === */
/// placeholder marker type for an actual data buffer
using Buff = StreamType::ImplFacade::DataBuffer;
BufferProvider (Literal implementationID);
virtual uint prepareBuffers (uint count, HashVal typeID) =0;
virtual BuffHandle provideLockedBuffer (HashVal typeID) =0;
virtual void mark_emitted (HashVal typeID, LocalTag const&) =0;
virtual void detachBuffer (HashVal typeID, LocalTag const&) =0;
virtual void mark_emitted (HashVal, LocalTag const&) =0;
virtual void detachBuffer (HashVal, LocalTag const&, Buff&) =0;
public:
@ -131,7 +134,7 @@ namespace engine {
size_t getBufferSize (HashVal typeID) const;
protected:
BuffHandle buildHandle (HashVal typeID, void* storage, LocalTag const& =LocalTag::UNKNOWN);
BuffHandle buildHandle (HashVal typeID, Buff* storage, LocalTag const& =LocalTag::UNKNOWN);
bool was_created_by_this_provider (BuffDescr const&) const;
};

View file

@ -119,7 +119,7 @@ namespace engine {
*/
class BuffHandle
{
typedef StreamType::ImplFacade::DataBuffer Buff;
using Buff = StreamType::ImplFacade::DataBuffer; ///< marker type for an actual data buffer
BuffDescr descriptor_;
Buff* pBuffer_;

View file

@ -49,9 +49,18 @@ namespace engine {
namespace error = lumiera::error;
using Buff = StreamType::ImplFacade::DataBuffer;
namespace { // implementation helpers...
inline Buff*
asBuffer(void* mem)
{// type tag to mark memory address as Buffer
return static_cast<Buff*> (mem);
}
using diagn::Block;
/** helper to find Block entries
@ -247,7 +256,7 @@ namespace engine {
{
diagn::BlockPool& blocks = getBlockPoolFor (typeID);
diagn::Block& newBlock = blocks.createBlock();
return buildHandle (typeID, newBlock.accessMemory(), &newBlock);
return buildHandle (typeID, asBuffer(newBlock.accessMemory()), &newBlock);
}
@ -265,10 +274,11 @@ namespace engine {
/** mark a buffer as officially discarded */
void
TrackingHeapBlockProvider::detachBuffer (HashVal typeID, LocalTag const& specifics)
TrackingHeapBlockProvider::detachBuffer (HashVal typeID, LocalTag const& specifics, Buff& storage)
{
diagn::Block* block4buffer = locateBlock (typeID, specifics);
REQUIRE (block4buffer, "releasing a buffer not allocated through this provider");
REQUIRE (util::isSameObject(storage, block4buffer->accessMemory()));
block4buffer->markReleased();
}

View file

@ -134,10 +134,10 @@ namespace engine {
public:
/* === BufferProvider interface === */
virtual uint prepareBuffers (uint count, HashVal typeID);
virtual BuffHandle provideLockedBuffer (HashVal typeID);
virtual void mark_emitted (HashVal entryID, LocalTag const&);
virtual void detachBuffer (HashVal entryID, LocalTag const&);
virtual uint prepareBuffers (uint count, HashVal typeID) override;
virtual BuffHandle provideLockedBuffer (HashVal typeID) override;
virtual void mark_emitted (HashVal, LocalTag const&) override;
virtual void detachBuffer (HashVal, LocalTag const&, Buff&) override;
public:
TrackingHeapBlockProvider();

View file

@ -28,12 +28,10 @@
#include "lib/error.hpp"
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include "steam/engine/buffer-metadata.hpp"
#include "steam/engine/testframe.hpp"
#include "lib/util.hpp"
#include <cstdlib>
#include <cstring>
#include <memory>
using std::strncpy;
@ -54,15 +52,6 @@ namespace test {
namespace { // Test fixture
const size_t TEST_MAX_SIZE = 1024 * 1024;
const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
HashVal JUST_SOMETHING = 123;
void* const SOME_POINTER = &JUST_SOMETHING;
template<typename TY>
TY&
accessAs (metadata::Entry& entry)
@ -71,6 +60,23 @@ namespace test {
ASSERT (ptr);
return *ptr;
}
template<typename X>
metadata::Buff*
mark_as_Buffer(X& something)
{
return reinterpret_cast<metadata::Buff*> (std::addressof(something));
}
const size_t TEST_MAX_SIZE = 1024 * 1024;
const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
HashVal JUST_SOMETHING = 123;
auto SOME_POINTER = mark_as_Buffer(JUST_SOMETHING);
}//(End) Test fixture and helpers
@ -214,7 +220,7 @@ namespace test {
// embedded into a Buffer Descriptor...
// later, when it comes to actually *locking* those buffers...
typedef char RawBuffer[SIZE_B];
using RawBuffer = std::byte;
void* storage = malloc (2*SIZE_B);
// do the necessary memory allocations behind the scenes...
@ -222,12 +228,12 @@ namespace test {
TestFrame* frames = new TestFrame[3]; // a real-world BufferProvider would use some kind of allocator
// track individual buffers by metadata entries
metadata::Entry& f0 = meta_->markLocked(bufferType1, &frames[0]);
metadata::Entry& f1 = meta_->markLocked(bufferType1, &frames[1]);
metadata::Entry& f2 = meta_->markLocked(bufferType1, &frames[2]);
metadata::Entry& f0 = meta_->markLocked(bufferType1, mark_as_Buffer(frames[0]));
metadata::Entry& f1 = meta_->markLocked(bufferType1, mark_as_Buffer(frames[1]));
metadata::Entry& f2 = meta_->markLocked(bufferType1, mark_as_Buffer(frames[2]));
metadata::Entry& r0 = meta_->markLocked(rawBuffType, &rawbuf[0]);
metadata::Entry& r1 = meta_->markLocked(rawBuffType, &rawbuf[1]);
metadata::Entry& r0 = meta_->markLocked(rawBuffType, mark_as_Buffer(rawbuf[ 0]));
metadata::Entry& r1 = meta_->markLocked(rawBuffType, mark_as_Buffer(rawbuf[SIZE_B]));
CHECK (LOCKED == f0.state());
CHECK (LOCKED == f1.state());
@ -241,12 +247,13 @@ namespace test {
CHECK (transaction2 == r0.localTag());
CHECK (transaction2 == r1.localTag());
auto adr =[](auto* x){ return reinterpret_cast<size_t>(x); };
CHECK (f0.access() == frames+0);
CHECK (f1.access() == frames+1);
CHECK (f2.access() == frames+2);
CHECK (r0.access() == rawbuf+0);
CHECK (r1.access() == rawbuf+1);
CHECK (adr(f0.access()) == adr(frames+0));
CHECK (adr(f1.access()) == adr(frames+1));
CHECK (adr(f2.access()) == adr(frames+2));
CHECK (adr(r0.access()) == adr(rawbuf+0 ));
CHECK (adr(r1.access()) == adr(rawbuf+SIZE_B));
TestFrame defaultFrame;
CHECK (defaultFrame == f0.access());
@ -359,8 +366,8 @@ namespace test {
VERIFY_ERROR (FATAL, entry.mark(NIL) );
// re-use buffer slot, start new lifecycle
void* OTHER_LOCATION = this;
entry.lock (OTHER_LOCATION);
auto* SOME_OTHER_LOCATION = mark_as_Buffer(*this);
entry.lock (SOME_OTHER_LOCATION);
CHECK (LOCKED == entry.state());
CHECK (entry.isLocked());
@ -373,7 +380,7 @@ namespace test {
VERIFY_ERROR (FATAL, entry.mark(BLOCKED) );
VERIFY_ERROR (FATAL, entry.mark(NIL) );
CHECK (OTHER_LOCATION == entry.access());
CHECK (SOME_OTHER_LOCATION == entry.access());
entry.mark (FREE);
CHECK (!entry.isLocked());

View file

@ -4882,9 +4882,7 @@
<node CREATED="1482464758148" ID="ID_545671857" MODIFIED="1518487921059" TEXT="triggert sofort"/>
<node CREATED="1492205571235" ID="ID_166892291" MODIFIED="1576282358143" TEXT="Zukunft: partiell schlie&#xdf;en">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
es w&#228;re denkbar, an dieser Stelle
@ -4923,9 +4921,7 @@
</node>
<node CREATED="1482466509953" ID="ID_565604124" MODIFIED="1576282358143" TEXT="...dient der Sicherheit">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
denn es erzwingt,
@ -4965,9 +4961,7 @@
</node>
<node CREATED="1482466587807" ID="ID_135147405" MODIFIED="1518487921059">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
reine ID-Wirtschaft <i>w&#228;re m&#246;glich</i>
@ -5502,9 +5496,7 @@
</node>
<node CREATED="1665801312049" ID="ID_904420529" MODIFIED="1665801548562" TEXT="Styles kann man bisweilen vereinfachen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und Inkscape wird das erhalten, solange man die betr. Features nicht wieder aktiviert. Im Besonderen kann man
@ -6406,9 +6398,7 @@
</node>
<node CREATED="1563020429008" ID="ID_873763889" MODIFIED="1678461929265" TEXT="Delegate herausgel&#xf6;st: UIStyle">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<ul>
<li>
@ -8003,9 +7993,7 @@
<icon BUILTIN="yes"/>
<node CREATED="1508540093004" ID="ID_277252077" MODIFIED="1576282358124" TEXT="Kinder-IDs sind eindeutig">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Innerhalb einer Kinder-Folge gibt es keine duplikaten IDs.
@ -10273,9 +10261,7 @@
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...das war nur ein unn&#246;tiger Fix nach dem Gie&#223;kannen-Prinzip.
@ -13652,9 +13638,7 @@
<icon BUILTIN="hourglass"/>
<node CREATED="1518830375356" ID="ID_580870700" MODIFIED="1518830460134">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<i>nur eingeschr&#228;nkt</i>&#160;auf die TypID?
@ -52461,9 +52445,7 @@
<node CREATED="1491005078409" ID="ID_1515823316" MODIFIED="1492391288793" TEXT="...mu&#xdf; ich wissen, wie newInstance verwendet wird"/>
<node CREATED="1491005491226" ID="ID_1986906306" MODIFIED="1576282357983" TEXT="und das h&#xe4;ngt von InvocationTrail ab">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
invocationTral wurde aufgegeben.
@ -52483,9 +52465,7 @@
<node CREATED="1492391227720" ID="ID_475537616" MODIFIED="1492391288793" TEXT="d.h. eine reine Command-ID wird stillschweigend geklont"/>
<node CREATED="1492391240086" ID="ID_687060412" MODIFIED="1492391288793">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
allerdings, wenn man eine <b>explizite</b>&#160;Instanz-ID angibt,
@ -52503,9 +52483,7 @@
<icon BUILTIN="help"/>
<node CREATED="1491494779796" ID="ID_1437580350" MODIFIED="1576282357982" TEXT="oder nur Prototypen?">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
das hei&#223;t, es geht um die Haupt-Registry f&#252;r Commands.
@ -88789,9 +88767,97 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
...und zwar je nachdem, in welcher Reihenfolge das LocalTag und die Buffer-Adresse gegeben waren: war das LocalTag zuerst da, dann w&#252;rde das bereits Teil des Hash; wenn dann sp&#228;ter jemand mit der gleichen Buffer-Adresse ankommt, aber ohne das LocalTag, dann wird der bestehende (schlimmstenfalls bereits gelockte) Eintrag nicht gefunden &#10233; es wird ein neuer Eintrag erzeugt und somit <b>zweimal gelockt.</b>
</p>
</body>
</html></richcontent>
<icon BUILTIN="broken-line"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1722174380744" ID="ID_267908880" MODIFIED="1722174427410" TEXT="Zielkonflikt im Rahmen des bestehenden Systems">
<icon BUILTIN="clanbomber"/>
<node CREATED="1722174447615" ID="ID_1522763793" MODIFIED="1722174491095" TEXT="Anforderung-1 : f&#xfc;r eine Buffer-Addresse darf es h&#xf6;chstens einen Eintrag geben"/>
<node CREATED="1722174500520" ID="ID_1587953083" MODIFIED="1722174647262" TEXT="Anforderung-2 : (abstrakter)Typ-Eintrag wird durch LocalTag differenziert"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1722183960753" ID="ID_1728122357" MODIFIED="1722187113886" TEXT="m&#xf6;gliche L&#xf6;sung: LocalTag nur f&#xfc;r abstrakte Eintr&#xe4;ge in den Hash aufnehmen">
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1722174881847" ID="ID_1281056403" MODIFIED="1722174898085" TEXT="die Bedeutung der abstrakten Typ-Eintr&#xe4;ge ist unklar">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1722174903945" ID="ID_244030292" MODIFIED="1722174919751" TEXT="derzeit zeichnet sich ab, sie f&#xfc;r Buffer-Descriptoren zu verwenden"/>
<node CREATED="1722174958776" ID="ID_1387037031" MODIFIED="1722175397721" TEXT="sp&#xe4;ter einmal k&#xf6;nnte auf dieser Basis auch ein Memory-Block-Pool verwaltet werden">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...da mit jedem Buffer-Typ auch eine bestimmte Memory-Block-Gr&#246;&#223;e verbunden ist, k&#246;nnte man dahinter mehrere kachelnde Basis-Allokatoren anordnen &#8212; und in einer solchen Struktur w&#228;re es sinnvoll, zu erwartende Buffer-Allokationen <b>im Voraus anzuk&#252;ndigen</b>. Die Steuerung der Pool-Gr&#246;&#223;e k&#246;nnte dann im Hintergrund erfolgen, was <i>m&#246;glicherweise eine relevante Optimierung auf Durchsatz</i>&#160;erm&#246;glicht
</p>
</body>
</html>
</richcontent>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1722175400457" ID="ID_299028104" MODIFIED="1722175484518" TEXT="hierzu w&#xe4;re eine Betrachtung des Buffer-Lebenszyklus notwengig">
<icon BUILTIN="hourglass"/>
<node CREATED="1722175526112" ID="ID_80353444" MODIFIED="1722178333993" TEXT="was passiert nach dem Ende einer Buffer-Verwendung?">
<icon BUILTIN="help"/>
<node CREATED="1722178721896" ID="ID_1494898122" MODIFIED="1722178748459" TEXT="BufferProvider::releaseBuffer(BuffHandle)">
<node CREATED="1722178755399" ID="ID_46030515" MODIFIED="1722178782104" TEXT="findet den Entry">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
anhand der Hash-ID, die zum Entry geh&#246;rt, welcher beim Erzeugen des Handles gespeichert wurde
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1722178787002" ID="ID_1918650261" MODIFIED="1722178803804" TEXT="&#xfc;berf&#xfc;hrt ihn &#x27fc; FREE">
<node CREATED="1722178807616" ID="ID_66134104" MODIFIED="1722178819618" TEXT="attached-dtor">
<font NAME="SansSerif" SIZE="11"/>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node CREATED="1722178828605" ID="ID_866216305" MODIFIED="1722178839388" TEXT="virtual : detachBuffer"/>
<node CREATED="1722178863904" ID="ID_420990968" MODIFIED="1722178895514">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
der Entry wird aus der Metadaten-Tabelle <b>gel&#246;scht</b>
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1722175512970" ID="ID_771752357" MODIFIED="1722183934557" TEXT="welche Eintr&#xe4;ge durchlaufen welche Lebenszyklus-Phasen?">
<icon BUILTIN="help"/>
<node CREATED="1722183802974" ID="ID_558175102" MODIFIED="1722183818351" TEXT="abstrakte Typ-Eintr&#xe4;ge bleiben im Status FREE"/>
<node CREATED="1722183840449" ID="ID_817136084" MODIFIED="1722183914707" TEXT="Eintr&#xe4;ge mit Speicheradresse laufen LOCKED &#x27fc; EMITTED | BLOCKED &#x27fc; FREE &#x27fc; (gel&#xf6;scht)"/>
</node>
<node CREATED="1722183936308" ID="ID_1490912654" MODIFIED="1722183942726" TEXT="vorl&#xe4;ufige Analyse">
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#fed399" COLOR="#fa002a" CREATED="1722187122172" ID="ID_1081153017" MODIFIED="1722187151227" TEXT="AUA: das ist ein grund&#xe4;tzliches Problem">
<icon BUILTIN="broken-line"/>
<node CREATED="1722187174269" ID="ID_1527056830" MODIFIED="1722187190070" TEXT="man k&#xf6;nnte n&#xe4;mlich auch mit zwei verschiedenen BufferTypes einsteigen"/>
<node CREATED="1722187190971" ID="ID_1915656776" MODIFIED="1722187217883" TEXT="damit bek&#xe4;me man jeweils einen separaten Hash und Entry"/>
<node CREATED="1722187254401" ID="ID_1600797508" MODIFIED="1722187297320" TEXT="man k&#xf6;nnte nun jedem die jeweils gleiche Adresse zuordnen"/>
<node CREATED="1722187298244" ID="ID_1405182769" MODIFIED="1722187322085" TEXT="und dann jeden Eintrag locken, ohne da&#xdf; die Metadatenverlwaltung Alarm schl&#xe4;gt"/>
<node CREATED="1722187373789" ID="ID_1380501144" MODIFIED="1722187434046" TEXT="wenn sich aber die Implementierung an das implizierte Protokoll h&#xe4;lt &#x27f9; keine Gefahr">
<node CREATED="1722187437073" ID="ID_967644037" MODIFIED="1722187460993" TEXT="die Impl. kann den State zu einer Adresse nicht wissen"/>
<node CREATED="1722187463676" ID="ID_1266347109" MODIFIED="1722187482519" TEXT="erwartet wird, da&#xdf; sie f&#xfc;r jede Anfrage eine neue Adresse belegt"/>
<node CREATED="1722187483627" ID="ID_1498778966" MODIFIED="1722187504963" TEXT="und nur Adressen wiederverwendet, die per detachBuffer() zur&#xfc;ckgegeben wurden"/>
</node>
</node>
<node COLOR="#435e98" CREATED="1722187531132" ID="ID_849426679" MODIFIED="1722187575744" TEXT="Fazit">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="forward"/>
<node CREATED="1722187535395" ID="ID_9598554" MODIFIED="1722187568052" TEXT="Gefahr besteht &#x2014; ist aber aktuell nicht virulent"/>
<node CREATED="1722187540798" ID="ID_120157543" MODIFIED="1722187552833" TEXT="es gibt aber ein implizites Implementierungs-Protokoll"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1722187577286" ID="ID_439948385" MODIFIED="1722187586617" TEXT="Ticket aufmachen">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
</node>