diff --git a/src/vault/mem/buffer-metadata.hpp b/src/vault/mem/buffer-metadata.hpp index 521be7fb2..b66196d23 100644 --- a/src/vault/mem/buffer-metadata.hpp +++ b/src/vault/mem/buffer-metadata.hpp @@ -643,57 +643,6 @@ namespace mem { : markLocked (parentKey,concreteBuffer,specifics); } - /** core operation to access or create a concrete buffer metadata entry. - * The hashID of the entry in question is built, based on the parentKey, - * which denotes a buffer type, optionally a implementation defined LocalTag, - * and the concrete buffer address. If yet unknown, a new concrete buffer - * metadata Entry is created and initialised to LOCKED state. Otherwise - * just the existing Entry is fetched and locked. - * @note this function really \em activates the buffer. - * In case the type (Key) involves a TypeHandler (functor), - * its constructor function will be invoked, if actually a new - * entry gets created. Typically this mechanism will be used - * to placement-create an object into the buffer. - * @param parentKey a key describing the _type_ of the buffer - * @param concreteBuffer storage pointer, must not be NULL - * @param specifics an implementation defined tag - * @param onlyNew disallow fetching an existing entry - * @throw error::Logic when #onlyNew is set, but an equivalent entry - * was registered previously. This indicates a serious error - * in buffer lifecycle management. - * @throw error::Invalid when invoked with NULL buffer. Use the #key - * functions instead to register and track type keys. - * @return reference to the entry stored in the metadata table. - * @warning the exposed reference might become invalid when the - * buffer is released or re-used later. - */ - Entry& - lock (Key const& parentKey - ,Buff* concreteBuffer - ,LocalTag const& specifics =LocalTag::UNKNOWN - ,bool onlyNew =false) - { - if (!concreteBuffer) - throw err::Invalid{"Attempt to lock a slot for a NULL buffer" - , LERR_(BOTTOM_VALUE)}; - - Entry newEntry{parentKey, concreteBuffer, specifics}; - Entry* existing = table_.fetch (newEntry); - - if (existing and onlyNew) - throw err::Logic{"Attempt to lock a slot for a new buffer, " - "while actually the old buffer is still locked" - , LERR_(LIFECYCLE)}; - if (existing and existing->isLocked()) - throw err::Logic{"Attempt to re-lock a buffer still in use" - , LERR_(LIFECYCLE)}; - - if (not existing) - return store_as_locked (newEntry); // actual creation - else - return existing->lock (concreteBuffer); - } - /** access the metadata record registered with the given hash key. * This might be a pseudo entry in case of a Key describing a buffer type. * Otherwise, the entry associated with a concrete buffer pointer is returned @@ -782,6 +731,58 @@ namespace mem { } + /** 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, + * which designates a buffer type, optionally an implementation defined + * LocalTag, together with the concrete buffer address and storage size. + * If yet unknown, a new concrete buffer metadata Entry is created and + * initialised immediately to LOCKED state. Otherwise just the matching + * Entry found in the metadata registry is fetched and locked. + * @note This function does the actual work to _activate_ the buffer. + * In case the type (Key) involves a TypeHandler (functor), + * its constructor function will be invoked, if actually a new + * entry gets created. Typically this mechanism will be used + * to placement-create an object into the buffer. + * @param parentKey a key describing the _type_ of the buffer + * @param concreteBuffer storage pointer, must not be NULL + * @param specifics an implementation defined tag + * @param onlyNew disallow fetching an existing entry + * @throw error::Logic when #onlyNew is set, but an equivalent entry + * was registered previously. This indicates a serious error + * in buffer lifecycle management. + * @throw error::Invalid when invoked with NULL buffer. Use the #key + * functions instead to register and track type keys. + * @return reference to the entry stored in the metadata table. + * @warning the exposed reference might become invalid when the + * buffer is released or re-used later. + */ + Entry& + lock (Key const& parentKey + ,Buff* concreteBuffer + ,LocalTag const& specifics =LocalTag::UNKNOWN + ,bool onlyNew =false) + { + if (!concreteBuffer) + throw err::Invalid{"Attempt to lock a slot for a NULL buffer" + , LERR_(BOTTOM_VALUE)}; + + Entry newEntry{parentKey, concreteBuffer, specifics}; + Entry* existing = table_.fetch (newEntry); + + if (existing and onlyNew) + throw err::Logic{"Attempt to lock a slot for a new buffer, " + "while actually the old buffer is still locked" + , LERR_(LIFECYCLE)}; + if (existing and existing->isLocked()) + throw err::Logic{"Attempt to re-lock a buffer still in use" + , LERR_(LIFECYCLE)}; + + if (not existing) + return store_as_locked (newEntry); // actual creation + else + return existing->lock (concreteBuffer); + } + private: @@ -802,6 +803,7 @@ namespace mem { } /** store a fully populated entry immediately starting with locked state + * @return a persisted copy that will be used to track the state henceforth. * @remark the (optional) constructor function for a type embedded into the * buffer is invoked when a _persistent_ entry transitions to _locked_ state; * since a new buffer created with storage location is already marked as _locked,_ diff --git a/src/vault/mem/buffer-provider.cpp b/src/vault/mem/buffer-provider.cpp index aef065d32..bdd5eaa17 100644 --- a/src/vault/mem/buffer-provider.cpp +++ b/src/vault/mem/buffer-provider.cpp @@ -115,7 +115,7 @@ namespace mem { BufferProvider::announce (uint cnt, BuffDescr const& type) { size_t buffSiz = getBufferSize (type); - uint actually_possible = bufferStore_->prepareBuffers (cnt, buffSiz, type); + uint actually_possible = bufferStore_->prepareBuffers (type, cnt, buffSiz); if (!actually_possible) throw err::State ("unable to fulfil request for buffers" ,LUMIERA_ERROR_BUFFER_MANAGEMENT); @@ -150,14 +150,14 @@ namespace mem { { REQUIRE (was_created_by_this_provider (type)); auto& typeKey = bufferStage_->lookup (type); - auto [storage, localTag] = bufferStore_->provideBuffer (typeKey.storageSize(), type, typeKey.localTag()); + auto [storage, actualSize, localTag] = bufferStore_->provideBuffer (type,typeKey.storageSize(),typeKey.localTag()); auto& stateKey = bufferStage_->mark_locked (typeKey, storage, localTag); return BuffHandle (buildDescriptor(stateKey), storage); } // NOTE: not the underlying parent descriptor! - /** BufferProvider API: state transition to \em emitted state. + /** BufferProvider API: state transition to _emitted state_. * Client code may signal a state transition through this optional operation. * The actual meaning of an "emitted" buffer is implementation defined; similarly, * some back-ends may actually do something when emitting a buffer (e.g. commit data @@ -173,7 +173,10 @@ namespace mem { BufferProvider::emitBuffer (BuffHandle const& handle) { auto& stateKey = bufferStage_->mark_emitted (handle); - bufferStore_->mark_emitted (stateKey.storageSize(), stateKey.parentKey(), stateKey.localTag()); + bufferStore_->mark_emitted (stateKey.parentKey() + ,BuffAlloc{handle.rawStorage() + ,stateKey.storageSize() + ,stateKey.localTag()}); } @@ -189,8 +192,10 @@ namespace mem { BufferProvider::releaseBuffer (BuffHandle& handle) try { // might invoke embedded dtor function auto& stateKey = bufferStage_->mark_released (handle); - bufferStore_->detachBuffer (stateKey.storageSize(), stateKey.parentKey() - ,std::make_tuple (handle.rawStorage(), stateKey.localTag())); + bufferStore_->detachBuffer (stateKey.parentKey() + ,BuffAlloc{handle.rawStorage() + ,stateKey.storageSize() + ,stateKey.localTag()}); bufferStage_->discard (handle); } ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider") @@ -207,8 +212,10 @@ namespace mem { BufferProvider::emergencyCleanup (BuffHandle& handle, bool invokeDtor) try { auto& stateKey = bufferStage_->abandon (handle, invokeDtor); - bufferStore_->detachBuffer (stateKey.storageSize(), stateKey.parentKey() - ,std::make_tuple (handle.rawStorage(), stateKey.localTag())); + bufferStore_->detachBuffer (stateKey.parentKey() + ,BuffAlloc{handle.rawStorage() + ,stateKey.storageSize() + ,stateKey.localTag()}); bufferStage_->discard (handle); } ERROR_LOG_AND_IGNORE (engine, "cleanup of buffer metadata while handling an error") diff --git a/src/vault/mem/buffer-provider.hpp b/src/vault/mem/buffer-provider.hpp index 5234f8862..fd644db57 100644 --- a/src/vault/mem/buffer-provider.hpp +++ b/src/vault/mem/buffer-provider.hpp @@ -138,6 +138,11 @@ namespace mem { class Key; } + /** Information to represent an actual buffer allocation. + * @remark LocalTag can be used by the storage-impl. */ + using BuffAlloc = std::tuple; + + LUMIERA_ERROR_DECLARE (BUFFER_MANAGEMENT); ///< Problem providing working buffers @@ -220,10 +225,10 @@ namespace mem { public: virtual ~BufferStore() { } ///< this is an interface - virtual uint prepareBuffers (uint cnt, size_t,HashVal typeID) =0; - virtual Slot provideBuffer (size_t,HashVal typeID, LocalTag) =0; - virtual void mark_emitted (size_t,HashVal, LocalTag const&) =0; - virtual void detachBuffer (size_t,HashVal, Slot alloc) =0; + virtual uint prepareBuffers (HashVal typeID, uint cnt, size_t) =0; + virtual BuffAlloc provideBuffer (HashVal typeID, size_t, LocalTag) =0; + virtual void mark_emitted (HashVal typeID, BuffAlloc) =0; + virtual void detachBuffer (HashVal typeID, BuffAlloc) =0; }; unique_ptr bufferStage_; diff --git a/src/vault/mem/buffer-proxy-adaptor.hpp b/src/vault/mem/buffer-proxy-adaptor.hpp index adc4b41f5..e2321c967 100644 --- a/src/vault/mem/buffer-proxy-adaptor.hpp +++ b/src/vault/mem/buffer-proxy-adaptor.hpp @@ -76,28 +76,28 @@ namespace mem { } uint - prepareBuffers (uint,size_t,HashVal) override + prepareBuffers (HashVal,uint,size_t) override { WARN (engine, "Announce invoked on a Proxy Buffer Provider; " "Implies misuse as generic BufferProvider"); return 1; } - Slot - provideBuffer (size_t,HashVal, LocalTag targetMarker) override + BuffAlloc + provideBuffer (HashVal,size_t siz,LocalTag targetMarker) override { CONF::on_lock(); - return {asBuffer(targetMarker), targetMarker}; + return {asBuffer(targetMarker), siz, targetMarker}; } void - mark_emitted (size_t,HashVal, LocalTag const&) override + mark_emitted (HashVal, BuffAlloc storageSlot) override { CONF::on_emit(); } void - detachBuffer (size_t,HashVal,Slot) override + detachBuffer (HashVal, BuffAlloc storageSlot) override { CONF::on_release(); } diff --git a/src/vault/mem/heap-mem-buffer-store.cpp b/src/vault/mem/heap-mem-buffer-store.cpp index aeb5280f5..8db5c8d8c 100644 --- a/src/vault/mem/heap-mem-buffer-store.cpp +++ b/src/vault/mem/heap-mem-buffer-store.cpp @@ -281,26 +281,27 @@ namespace mem { /* ==== Implementation of the BufferProvider interface ==== */ uint - HeapMemBufferStore::prepareBuffers (uint numBuffers, size_t buffSiz, HashVal typeID) + HeapMemBufferStore::prepareBuffers (HashVal typeID, uint numBuffers, size_t buffSiz) { BlockPool& responsiblePool = getBlockPoolFor (buffSiz, typeID); return responsiblePool.prepare_for (numBuffers); } - BufferProviderSetup::Store::Slot - HeapMemBufferStore::provideBuffer (size_t buffSiz, HashVal typeID, LocalTag) + BuffAlloc + HeapMemBufferStore::provideBuffer (HashVal typeID, size_t buffSiz, LocalTag) { BlockPool& blocks = getBlockPoolFor (buffSiz, typeID); Block& newBlock = blocks.createBlock(); LocalTag specifics{&newBlock}; // used by this implementation to find the storage to release later - return std::make_tuple (asBuffer(newBlock.accessMemory()), specifics); + return std::make_tuple (asBuffer(newBlock.accessMemory()), buffSiz, specifics); } void - HeapMemBufferStore::mark_emitted (size_t buffSiz, HashVal typeID, LocalTag const& specifics) + HeapMemBufferStore::mark_emitted (HashVal typeID, BuffAlloc storageSlot) { + auto& [_,buffSiz,specifics] = storageSlot; Block* block4buffer = locateBlock (buffSiz, typeID, specifics); if (!block4buffer) throw err::Logic ("Attempt to emit a buffer not known to this BufferProvider" @@ -320,9 +321,9 @@ namespace mem { /** mark a buffer as officially discarded */ void - HeapMemBufferStore::detachBuffer (size_t buffSiz, HashVal typeID, Slot alloc) + HeapMemBufferStore::detachBuffer (HashVal typeID, BuffAlloc storageSlot) { - auto& [storage, specifics] = alloc; + auto& [storage,buffSiz,specifics] = storageSlot; Block* block4buffer = locateBlock (buffSiz, typeID, specifics); REQUIRE (block4buffer, "releasing a buffer not allocated through this provider"); REQUIRE (util::isSameAdr (storage, block4buffer->accessMemory())); diff --git a/src/vault/mem/heap-mem-buffer-store.hpp b/src/vault/mem/heap-mem-buffer-store.hpp index 6015bd03d..cffac8b44 100644 --- a/src/vault/mem/heap-mem-buffer-store.hpp +++ b/src/vault/mem/heap-mem-buffer-store.hpp @@ -66,10 +66,10 @@ namespace mem { public: /* === BufferStore interface === */ - virtual uint prepareBuffers (uint cnt, size_t,HashVal typeID) override; - virtual Slot provideBuffer (size_t,HashVal, LocalTag) override; - virtual void mark_emitted (size_t,HashVal, LocalTag const&) override; - virtual void detachBuffer (size_t,HashVal, Slot alloc) override; + virtual uint prepareBuffers (HashVal typeID, uint cnt, size_t) override; + virtual BuffAlloc provideBuffer (HashVal,size_t,LocalTag) override; + virtual void mark_emitted (HashVal typeID, BuffAlloc) override; + virtual void detachBuffer (HashVal typeID, BuffAlloc) override; public: ~HeapMemBufferStore(); diff --git a/tests/vault/mem/buffer-metadata-test.cpp b/tests/vault/mem/buffer-metadata-test.cpp index eb81accf3..c5b399fa1 100644 --- a/tests/vault/mem/buffer-metadata-test.cpp +++ b/tests/vault/mem/buffer-metadata-test.cpp @@ -324,7 +324,7 @@ namespace test { metadata::Key key = meta_->key(SIZE_A); CHECK (NIL == meta_->get(key).state()); CHECK (meta_->get(key).isTypeKey()); - CHECK (!meta_->isLocked(key)); + CHECK (not meta_->isLocked(key)); VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(LOCKED) ); VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(EMITTED)); @@ -335,7 +335,7 @@ namespace test { // now build a concrete buffer entry metadata::Entry& entry = meta_->markLocked(key, SOME_POINTER); CHECK (LOCKED == entry.state()); - CHECK (!entry.isTypeKey()); + CHECK (not entry.isTypeKey()); CHECK (SOME_POINTER == entry.access()); @@ -353,8 +353,8 @@ namespace test { entry.mark (FREE); CHECK (FREE == entry.state()); - CHECK (!entry.isLocked()); - CHECK (!entry.isTypeKey()); + CHECK (not entry.isLocked()); + CHECK (not entry.isTypeKey()); VERIFY_ERROR (LIFECYCLE, entry.access() ); VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); @@ -381,7 +381,7 @@ namespace test { CHECK (SOME_OTHER_LOCATION == entry.access()); entry.mark (FREE); - CHECK (!entry.isLocked()); + CHECK (not entry.isLocked()); VERIFY_ERROR (LIFECYCLE, entry.access() ); meta_->lock(key, SOME_POINTER); @@ -399,8 +399,8 @@ namespace test { entry.mark (FREE); meta_->release(entry); - CHECK (!meta_->isKnown(entry)); - CHECK ( meta_->isKnown(key)); + CHECK (not meta_->isKnown(entry)); + CHECK ( meta_->isKnown(key)); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index fa42c6c07..25a008b52 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -16322,7 +16322,7 @@ - + @@ -20250,7 +20250,7 @@ - + @@ -72506,6 +72506,111 @@ + + + + + + + + + + +

+ wobei »Versagen« hier bedeutet: Kontrollverlust +

+
    +
  • + GAU : eine wichtige Komponente bricht weg, aber das Subsystem bekommt es noch mit +
  • +
  • + SuperGAU : es wird nur noch das Brechen einer fundamentalen Annahme erkannt +
  • +
+ +
+
+ + + + + + +

+ Das war eine bewußte Entscheidung. Sie gründet in der Beobachtung, daß Unterbrechnungs-Mechanismen eine erhebliche »Strahlkraft« entwickeln, da sich an ihnen letztlich die Tiefe der Kontrolle und Beherrschung ausdrückt. Dieser Belang ist — vor allem in einem neu aufzubauenden System — um Vielfaches konkreter und greifbarer als all das, was erst noch entwickelt werden soll. Daher besteht die Gefahr, daß sich dieser einzelne Belang zum Vorherrschenden Prinzip aufschwingt und alles andere dominiert. +

+ +
+
+ + + + +

+ Und das ist auch gut so, denn sonst besteht die Gefahr, unversehens, »durch die Hintertür« wieder in einen Unterbrechungs-Mechanismus abzugleiten +

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

+ das wäre sogar am schönsten +

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

+ denn selbst das Signalisieren eines Lifecycle-Event könnte irgendwo blocken +

+ +
+
+
+
+
+
@@ -113416,6 +113521,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + @@ -114473,12 +114579,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - +

- Seit 2024 bin ich nun mehrfach durch den Kern der Implementierung durchgegangen, und konnte keine offensichtliche Lücke finden (nur ein paar lokale Inkonsistenzen, wie z.B. daß wir uns den aktuellen Buffer-Pointer im Entry speichern, obwohl es genügen würde, ihn nur in den Hash einzurechnen). Auch der Zugangs-Funktionen, BufferMetadata::key(...) sind konsistent und korrekt strukturiert: es gibe eine einzige Funktion, mit der man »aus dem Nichts« einsteigen kann (und in dieser Funktion hatte ich 2024 die Reihenfolge der Qualifizierungen in ein verbindliches Schema geordnet). Außerdem gibt es ein paar weitere Funktionen, mit denen man einen bereits bestehenden Key verfeinern kann, und eine Funktion, die verwendet wird, um daraus einen Entry zu machen. + Seit 2024 bin ich nun mehrfach durch den Kern der Implementierung durchgegangen, und konnte keine offensichtliche Lücke finden; nur ein paar lokale Inkonsistenzen, wie z.B. daß wir uns den aktuellen Buffer-Pointer im Entry speichern, obwohl es genügen würde, ihn nur in den Hash einzurechnen (3/26 das war zu kurz gedacht. BufferMetadata sind die Metadaten eines Storage-Managers. Also müssen wir aber Hallo dort auch den tatsächlichen Pointer speichern, und die Size. Wo denn sonst?!!). Auch der Zugangs-Funktionen, BufferMetadata::key(...) sind konsistent und korrekt strukturiert: es gibe eine einzige Funktion, mit der man »aus dem Nichts« einsteigen kann (und in dieser Funktion hatte ich 2024 die Reihenfolge der Qualifizierungen in ein verbindliches Schema geordnet). Außerdem gibt es ein paar weitere Funktionen, mit denen man einen bereits bestehenden Key verfeinern kann, und eine Funktion, die verwendet wird, um daraus einen Entry zu machen.

Solange ich mich also nicht aus dem zugrundeliegenden gedanklichen Schema löse, werde ich hier nichts Relevantes in Bewegung bringen können @@ -114487,6 +114593,19 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + +

+ Was die Sache vor allem verwirrend macht, ist die Anordnung im Source-File. Das sind eigentlich mehrere Layer — der normale Zugangspfad tritt in der Mitte des Files ein, durch die Klasse BufferMetadata, geht dort durch die high-level und low-level-Funktionen und verwendet dann die Metadaten-Tabelle, sowie Key und Entry +

+ +
+
+
@@ -114499,12 +114618,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - +

- BuffHandle sit nun definitiv kein smart-Ptr mehr; damit fällt alles nachträgliche »andocken« und »verfeinern« weg..... + BuffHandle ist nun definitiv kein smart-Ptr mehr; damit fällt alles nachträgliche »andocken« und »verfeinern« weg.....

@@ -114882,8 +115001,32 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + + + + + + +

+ der Typ Buff wäre jetzt in buffhandle.hpp definiert +

+ +
+ +
+ + + + +

+ Namensvorschlag: BuffAlloc +

+ +
+ +
+
@@ -114892,6 +115035,130 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Einsicht: konkrete buffer-size darf nicht in den stateKey +

+ +
+ + + + + +

+ ...wenn der konkrete Size eingerechnet wird, führt das zu einem abweichenden StateKey, und daher wird die schon bestehende Sperre in den Metadaten nicht bemerkt (weil für den neuen Key auch einfach ein neuer Entry erzeugt wird) +

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

+ Wenn man oben zu lesen anfängt, kommen erst die Grund-Bausteine. Soweit logisch. Doch dann kommt man zu BufferMetadata, und das fängt mit den public-high-level-Funktionen an, und steigt in die eigene Implementierung ab. Das deutet darauf hin, daß man den Quellcode ggfs. mal in zwei Layer zerlegen sollte.... +

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

+ ...auch das deutet auf zwei Code-Layer hin; nachdem ich nun etliche male aufgeräumt habe, sieht man deutlich eine prallel-Struktur +

+
    +
  • + die Key-Konstruktoren erzeugen ein in-sich konsitente Typ-Repräsentation +
  • +
  • + die entsprechenden BufferMetadata::key()-Funktionen speichern und verwalten diese Typen persistent +
  • +
+ +
+
+ + + + +

+ BufferMetadate::lock()  ⟻ das ist die low-level worker-Function +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
@@ -116535,7 +116802,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + @@ -116790,6 +117057,20 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + +

+ Anmerkung 1 Monat später: diese Möglichkeit war mir damals schon nebenbei aufgefallen, allerdings nur getrieben vom Bestreben, die API-Funktionen besser in eine Zeile packen zu können. Weshalb ich die Idee erst mal als »Spinnerei« abgetan habe. Dann aber, im Zusammenhang mit dem OutputSlot stellte sich erneut das Problem, daß man hier Zustandsübergänge über ein API »durchreichen« muß. +

+

+ Konkret ist ja nun die Anforderung, daß wir den OutputSlot  komplett hinter einem  BuffHandle verbergen müssen, da die überarbeitete Implementierung der Render-Engine nur mit BuffHandles (und Deskriptoren) umgehen kann (das war eine Entscheidung auf  theoretischer Basis, denn alle notwendigen Operationen lassen sich so darstellen). Damit wäre es dann vielleicht doch sinnvoll, die latent vorhandene Symmetrie in diesem Implementierungs-API stärker herauszuheben? +

+ +
+ +
@@ -137000,7 +137281,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -137581,13 +137862,37 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + + + + +

+ Hier gibt es einen inhärenten Widerspruch, der auf eine grundlegende Entscheidung zurückgeht: wir wollen nämlich bewußt nicht 100%ig den Shutdown durchdringen und im Besonderen keine Unterbrechungs-Mechanismen implementieren. Daher kann man dieses Problem eigentlich nicht richtig lösen. Dementsprechend geht es hier um eine Heuristik: man muß zusehen, daß noch laufende Prozesse möglichst keinen Schaden mehr anrichten können +

+ +
+ +
+
+
- - + + + @@ -137600,16 +137905,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

es ist 1 KATASTROFE

- -
+
@@ -158527,6 +158829,7 @@ std::cout << tmpl.render({"what", "World"}) << s + @@ -158631,7 +158934,22 @@ std::cout << tmpl.render({"what", "World"}) << s - + + + + +
    +
  • + UML überarbeitet und aktualisiert +
  • +
  • + Text durchgegangen und die wenigen relevanten Änderungen eingearbeitet +
  • +
+ +
+ +
@@ -167743,9 +168061,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

ja leider ... deshalb muß man nun den halben KDE installieren @@ -167775,16 +168091,13 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

es verschiebt Assoziationen und sogar benachbarte Klassen, oft in komplett irrsinniger Weise

- -
+
@@ -167808,41 +168121,32 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - - - +

...dann wandelt sich der Typ auch in Multiline; danach sollte man zunächst die inneren Punkte platzieren, erst zuletzt den Start/Endpunkt. Vorsicht, sobald man eines der verbundenen Objekte weiter editiert oder skaliert, greift wieder das automatische Layout ein

- -
+
- - - +

als SVG exportieren und im Inkscape reparieren

- -
+ - - - +

das ist besonders ärgerlich, denn das führt zu unscharfen Linien

- -
+
@@ -170698,7 +171002,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- + @@ -175567,7 +175871,7 @@ that situation will improve in forseeable future. - + @@ -177533,7 +177837,7 @@ that situation will improve in forseeable future. - + @@ -177544,6 +177848,26 @@ that situation will improve in forseeable future. + + + + + + + + + + + + + + + + + + + +