From ce5f7d5e06f6375e5aadada3edfd78d47327d76d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 12 Apr 2026 22:15:26 +0200 Subject: [PATCH] BufferProvider: new reworked implementation validated and complete (closes: #1410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After an extended round of analysis and cascading reworks, the long-standing discrepancy between `OutputSlot` and the ''Buffer Provider Protocol'' (#1395) has finally been resolved. The foundation for this success was laid by means of a restructured implementation within `BufferProvider` – which has now been successfully completed and validated. --- src/vault/mem/diagnostic-buffer-provider.cpp | 35 +- src/vault/mem/diagnostic-buffer-provider.hpp | 25 +- src/vault/mem/heap-mem-buffer-store.cpp | 5 +- wiki/thinkPad.ichthyo.mm | 350 ++++++++++++++----- 4 files changed, 309 insertions(+), 106 deletions(-) diff --git a/src/vault/mem/diagnostic-buffer-provider.cpp b/src/vault/mem/diagnostic-buffer-provider.cpp index 0a31955f7..bb51f90b2 100644 --- a/src/vault/mem/diagnostic-buffer-provider.cpp +++ b/src/vault/mem/diagnostic-buffer-provider.cpp @@ -22,7 +22,6 @@ #include "lib/error.hpp" #include "vault/mem/diagnostic-buffer-provider.hpp" -#include "vault/mem/heap-mem-buffer-store.hpp" #include "lib/format-string.hpp" #include "lib/util-foreach.hpp" #include "lib/util.hpp" @@ -37,7 +36,6 @@ namespace mem { namespace err = lumiera::error; using util::_Fmt; - using util::unConst; using diagn::Block; using diagn::StateReg; using std::unique_ptr; @@ -81,10 +79,20 @@ namespace mem { , LERR_(BOTTOM_VALUE)); } + + namespace { + inline auto + findHash (auto& registry, HashVal handle) + { + return find_if (registry + ,[handle](auto& entry) { return handle == HashVal(entry); }); + } + } + StateReg::Result StateReg::byHandle (HashVal handle) const { - if (auto pos = find_if (reg_, [&](auto& entry){ return handle == HashVal(entry); }) + if (auto pos = findHash (reg_, handle) ;pos != reg_.end() ) return *pos; @@ -96,7 +104,8 @@ namespace mem { bool StateReg::contains (HashVal handle) const { - return bool(byHandle (handle)); + auto pos = findHash (reg_, handle); + return pos != reg_.end(); } @@ -194,9 +203,11 @@ namespace mem { } ID - abandon (HashVal stateKey, bool invokeDtor) override + abandon (HashVal id, bool invokeDtor) override { - return stage_->abandon (stateKey, invokeDtor); + ID stateKey = stage_->abandon (id, invokeDtor); + tracker_.record_released (stateKey); + return stateKey; } void @@ -213,6 +224,10 @@ namespace mem { { } }; + //(End) internal diagnostics helper + + + DiagnosticBufferProvider::DiagnosticBufferProvider() @@ -224,7 +239,13 @@ namespace mem { DiagnosticBufferProvider::~DiagnosticBufferProvider() { -// INFO (proc_mem, "discarding %zu diagnostic buffer entries", outSeq_.size()); ////////////OOO implement based on dedicated tracking information in-object + ENSURE (tracker_); + auto created = [this]{ return tracker_->created.cnt(); }; + auto released = [this]{ return tracker_->released.cnt(); }; + INFO (proc_mem, "discarding %zu diagnostic buffer entries", created() ); + REQUIRE (released() <= created(), "Buffer handling broken: %zu excess released", released() - created()); + WARN_IF (created() > released() + ,proc_mem, "Allocation Logic mishap: %zu buffers not released", created() - released()); } BufferDiagnostic::BufferDiagnostic (DiagnosticBufferProvider& thePro) diff --git a/src/vault/mem/diagnostic-buffer-provider.hpp b/src/vault/mem/diagnostic-buffer-provider.hpp index 3d6525e99..80b53d52c 100644 --- a/src/vault/mem/diagnostic-buffer-provider.hpp +++ b/src/vault/mem/diagnostic-buffer-provider.hpp @@ -12,8 +12,18 @@ */ /** @file diagnostic-buffer-provider.hpp - ** A facility for writing unit-tests targeting the BufferProvider interface. - ** + ** A facility for writing unit-tests that target the BufferProvider interface. + ** A DiagnosticBufferProvider is a complete self-contained implementation of the + ** BufferProvider interface, with added diagnostic tracking of protocol steps. + ** The observed behaviour data can be retrieved and inspected using a special + ** "diagnostic backdoor", accessible through the call \ref watch(BufferProvider&). + ** Since by default the \ref HeapMemBufferStore is used, all allocated blocks are + ** retained and not released, as long as the BufferProvider front-end object + ** is alive; this allows to access the buffer memory, even from buffers + ** formally marked as released. + ** @todo 4/2026 the BufferStore API has also an `abandon()` call, that is invoked + ** in case of an _emergency clean-up_. This relevance of this feature is not + ** yet clear; thus _abandoned_ buffers are simply recorded as _released_. ** @see buffer-provider-protocol-test.cpp */ @@ -22,8 +32,6 @@ #include "lib/error.hpp" -#include "lib/util.hpp" -#include "vault/mem/type-handler.hpp" ///////////////OOO warum? #include "vault/mem/naive-buffer-setup.hpp" #include "lib/iter-adapter-stl.hpp" #include "lib/result.hpp" @@ -37,9 +45,6 @@ namespace vault { namespace mem { namespace err = lumiera::error; - class HeapMemBufferStore; //////////////////////////////OOO fällt dann weg nach dem Umbau - class BufferDiagnostic; - namespace diagn {// state descriptors for diagnostics.... /** represent the status of one allocation */ @@ -99,6 +104,9 @@ namespace mem { + class BufferDiagnostic; + + /********************************************************************//** * Helper for unit tests: Buffer provider that tracks state transitions. * This is a variant of the _naive_ heap based BufferProvider implementation, @@ -115,10 +123,11 @@ namespace mem { class DiagnosticBufferProvider : public NaiveBufferSetup { - class InstrumentedStageProxy; struct BlockTracker; std::unique_ptr tracker_; + class InstrumentedStageProxy; + public: ~DiagnosticBufferProvider(); DiagnosticBufferProvider(); diff --git a/src/vault/mem/heap-mem-buffer-store.cpp b/src/vault/mem/heap-mem-buffer-store.cpp index 62551fa6d..887ecf56b 100644 --- a/src/vault/mem/heap-mem-buffer-store.cpp +++ b/src/vault/mem/heap-mem-buffer-store.cpp @@ -68,7 +68,7 @@ namespace mem { inline void - zeroFill (void* buf, size_t cnt) + zeroWordsFill (void* buf, size_t cnt) { auto begin = static_cast (buf); std::fill (begin, begin+cnt, StorageWord(0)); @@ -111,8 +111,9 @@ namespace mem { REQUIRE (cnt, "Attempt to create a zero-sized buffer"); siz_ = cnt; size_t words = wordCnt(cnt); - mem_ = static_cast (std::aligned_alloc (std::alignment_of(), words )); + mem_ = static_cast (std::aligned_alloc (std::alignment_of(), words*WORD_SIZ )); ENSURE (mem_); + zeroWordsFill (mem_, words); state_ = LOCKED; } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 5f8163c9a..a3c33dc9a 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -57723,8 +57723,8 @@ - + @@ -82674,8 +82674,8 @@ - + @@ -82683,8 +82683,8 @@ - + @@ -105458,7 +105458,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -105469,6 +105469,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+
@@ -105854,8 +105855,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -110899,7 +110900,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -111081,6 +111082,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + @@ -114095,8 +114097,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -116388,6 +116390,17 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + +

+ Gefühlsmäßig sind jetzt die größten Probleme ausgeräumt. Der Lebenszyklus wird nun über ein eingebettetes, ref-counting-Handle gesteuert (und zwar gemeinsam für OutputSlot und alle Sinks / Connections). Die direkten API-Aufrufe werden nun über einen OutputBufferProxy direkt an die jeweilige Connection weitergeleitet (ermöglicht durch Einbetten der Connection-ID in das LocalTag) +

+ +
+ +
@@ -116853,8 +116866,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -117187,9 +117200,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + @@ -117513,7 +117526,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -117606,7 +117620,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -117693,8 +117707,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + + @@ -117706,7 +117722,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -117771,7 +117787,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -117839,7 +117855,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -117848,8 +117864,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- - + + @@ -118046,9 +118062,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + - + @@ -118159,7 +118175,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -118268,8 +118285,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -118393,7 +118410,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- +
@@ -118513,7 +118530,15 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + + + + +

+ Resultat: jetzt ist er eine abgeleitete Implementierung im neuen Schema +

+ +
@@ -118529,9 +118554,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + - + @@ -118920,12 +118945,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + - + @@ -118933,12 +118958,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + + @@ -118947,8 +118973,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + - + + + @@ -118957,7 +118987,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119044,7 +119074,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119058,14 +119088,15 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + - + + - + @@ -119088,7 +119119,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119222,7 +119253,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119241,7 +119272,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119269,7 +119300,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119281,7 +119312,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119329,7 +119360,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119341,7 +119372,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119375,7 +119406,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119507,7 +119538,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119562,8 +119593,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + + @@ -119607,7 +119640,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119677,7 +119710,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -119911,9 +119944,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - + + + + @@ -121648,7 +121682,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122018,7 +122052,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122346,7 +122380,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122380,14 +122414,14 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + @@ -122402,13 +122436,117 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + +

+ Bei dieser Art Tests war ich immer wieder in die Falle gegangen, daß uninitialised storage nun überraschenderweise tatsächlich uninitialised ist, und damit die Daten eines validen TestFrame enthalten kann, der von einem vorhergehenden Test an dieser Stelle abgelegt wurde. Ganz generell macht es das Testen leichter, wenn der Speicher vorher gesäubert wurde. +

+

+ Im Production-Code macht man typischerweise keine solchen speziellen Tricks, sich einen de-allozierten Speicher nachher anzuschauen... +

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

+ ⟹ brauche hier wordCnt(siz) * WORD_SIZE +

+ +
+
+
+ + + +
+ + + + + + + + + + + + +
+
+
+ @@ -122417,7 +122555,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- + @@ -122428,7 +122566,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122536,7 +122674,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122584,7 +122722,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122592,7 +122730,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -122602,7 +122741,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -122712,8 +122852,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + @@ -122776,7 +122917,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -122820,7 +122962,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122894,7 +123036,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122905,7 +123047,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -122945,8 +123087,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -123051,7 +123193,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -123062,7 +123204,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- + @@ -143613,7 +143755,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- + @@ -143775,7 +143917,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -143806,7 +143948,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -143872,7 +144014,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -143895,6 +144037,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + @@ -143905,7 +144048,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -143929,6 +144072,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + @@ -144325,7 +144471,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -144339,6 +144485,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + @@ -145161,8 +145308,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -145188,6 +145335,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + @@ -160357,8 +160507,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -166376,6 +166526,28 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + +

+ Ermöglicht einer BufferStore-Implementierung eine übergreifende Koordinierung +

+ +
+ + + +
+
+ + + + +
@@ -173424,8 +173596,8 @@ std::cout << tmpl.render({"what", "World"}) << s
- +