From 02ba977c61b96c1a41631fd75e7c60d63b3ddf9f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 22 Feb 2026 23:39:10 +0100 Subject: [PATCH] BufferProvider: experiment for callback configuration Now I have a working setup for a `BufferProxyAdaptor`. Unfortunately this is still confusingly complex. This is due to my intention to build a generic template, that can be configured with a policy to invoke arbitrary callbacks, whenever one of the passed-through buffers transitions through its lifecycle. * we need to configure the `BufferProviderSetup` to use our specific implementation classes * and the second one of these, our `BufferStore` implementation needs to invoke the callbacks --- src/steam/engine/buffer-metadata.hpp | 3 +- src/steam/engine/buffer-proxy-adaptor.hpp | 37 ++- .../engine/output-proxy-provider-test.cpp | 20 +- wiki/thinkPad.ichthyo.mm | 237 +++++++++++------- 4 files changed, 186 insertions(+), 111 deletions(-) diff --git a/src/steam/engine/buffer-metadata.hpp b/src/steam/engine/buffer-metadata.hpp index 3967b2e38..03490f992 100644 --- a/src/steam/engine/buffer-metadata.hpp +++ b/src/steam/engine/buffer-metadata.hpp @@ -225,7 +225,8 @@ namespace engine { Key newKey{parent}; // copy of parent as baseline if (nontrivial(localTag)) { - if (nontrivial(parent.specifics_)) + if (nontrivial(parent.specifics_) + and localTag != parent.specifics_) throw error::Logic{"Implementation defined local key should not be overridden. " "Underlying buffer type already defines a nontrivial LocalTag"}; newKey.parent_ = HashVal(parent); diff --git a/src/steam/engine/buffer-proxy-adaptor.hpp b/src/steam/engine/buffer-proxy-adaptor.hpp index 91bad8bfb..d7c129172 100644 --- a/src/steam/engine/buffer-proxy-adaptor.hpp +++ b/src/steam/engine/buffer-proxy-adaptor.hpp @@ -48,6 +48,7 @@ namespace engine { using lib::Literal; // using std::unique_ptr; // using std::forward; + using std::move; @@ -65,6 +66,7 @@ namespace engine { * @todo WIP-WIP 2/2026 used as prototype to forge a path ahead to resolve * the structural problems with OutputSlot vs. BufferProvider ///////////////////////////////////////TICKET #1415 : prototyping to resolve structural mismatch between OutputSlot / DataSink / BufferProvider */ + template class BufferProxyAdaptor : util::NonCopyable { @@ -81,6 +83,8 @@ namespace engine { class ProxyBufferStore : public BufferProviderSetup::Store { + CONF& callback_; + Buff* asBuffer (LocalTag targetMarker) { @@ -99,36 +103,38 @@ namespace engine { Slot provideBuffer (size_t,HashVal, LocalTag targetMarker) override { - TODO ("invoke LOCK callback"); + callback_.on_lock(); return {asBuffer(targetMarker), targetMarker}; } void mark_emitted (size_t,HashVal, LocalTag const&) override { - TODO ("invoke EMIT callback"); + callback_.on_emit(); } void detachBuffer (size_t,HashVal,Slot) override { - TODO ("invoke RELEASE callback"); + callback_.on_release(); } + public: + ProxyBufferStore (CONF& policy) + : callback_{policy} + { } }; class PassThroughBufferProvider : public BufferProviderSetup { - + public: - PassThroughBufferProvider () - : BufferProviderSetup{*this} + template + PassThroughBufferProvider (SETUP& setup) + : BufferProviderSetup{setup} { } - auto buildStage() { return std::make_unique(); } - auto buildStore() { return std::make_unique(); } - BuffDescr registerBuffer (void* buff, size_t siz) { @@ -139,12 +145,21 @@ namespace engine { } }; + struct Setup + : CONF + { + auto buildStage() { return std::make_unique(); } + auto buildStore() { return std::make_unique(*this); } + }; + Setup setup_; + PassThroughBufferProvider proxyProvider_; public: - BufferProxyAdaptor() - : proxyProvider_{} + BufferProxyAdaptor(CONF policy) + : setup_{move(policy)} + , proxyProvider_{setup_} { } template diff --git a/tests/core/steam/engine/output-proxy-provider-test.cpp b/tests/core/steam/engine/output-proxy-provider-test.cpp index 14cef882e..857d02049 100644 --- a/tests/core/steam/engine/output-proxy-provider-test.cpp +++ b/tests/core/steam/engine/output-proxy-provider-test.cpp @@ -21,7 +21,7 @@ //#include "steam/play/diagnostic-output-slot.hpp" #include "steam/engine/buffer-proxy-adaptor.hpp" #include "steam/engine/test-rand-ontology.hpp" - +#include "lib/test/diagnostic-output.hpp" namespace steam { @@ -35,11 +35,6 @@ namespace test { * @test verify the design of OutputSlot and BufferProvider by * implementing a delegating BufferProvider to expose * output data buffers provided from _some implementation._ - * @todo WIP-WIP 12/2024 this turned out to be impossible, - * due to inconsistencies in the default implementation. /////////////////////////////////////////////TICKET #1387 : need to consolidate BufferProvider default implementation - * @todo 2/2026 broken by refactoring of BufferProvider //////////////////////////////////////////////TICKET #1410 : refactoring to separate external and implementation API - * @todo however, something along this lines *must* - * be made possible, somehow, for OutputSlot... * @todo 2/2026 now using this setup for prototyping /////////////////////////////////////////////////TICKET #1415 : explore this design idea through prototyping */ class OutputProxyProvider_test : public Test @@ -47,10 +42,15 @@ namespace test { virtual void run (Arg) { - size_t seenID{0}; - BufferState lastState{NIL}; + struct Callback + { + void on_lock() { cout << "LOCK" << endl; } + void on_emit() { cout << "EMIT" << endl; } + void on_release() { cout << "RELEASE" << endl; } + }; + // setup with notification callback - BufferProxyAdaptor proxPro{}; + BufferProxyAdaptor proxPro{Callback()}; // Assuming some data block is »given« seedRand(); @@ -58,7 +58,7 @@ namespace test { TestFrame dataBlock (frameNr); CHECK ( dataBlock.isPristine()); - BuffHandle handle = proxPro.lockBuffer (dataBlock); ///////////////////////////////////////////////TICKET #1387 : unable to implement this + BuffHandle handle = proxPro.lockBuffer (dataBlock); // Now a »client« can do awful things to the buffer... CHECK (handle.isValid()); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index a82906033..d5392f253 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -61678,41 +61678,32 @@ - - - +

Es wird in Zukunft immer wieder Einrichtungen geben, die neu re-seeded werden müssen, um deterministisch zu sein. Dieses re-Seeding muß zuverlässig und reproduzierbar erfolgen, nachdem der globale PRNG neu initialisiert wurde.

- -
+ - - - +

...jede Art von Einrichtung, die in komplexer Weise vom Random-Seed abhängt, und bei korrektem Setup deterministisch gemacht werden kann; also immer dann, wenn dieses Setup »schwierig« wird! Als Kontrast: für einen banalen Test zieht man sich einfach eine Zufallszahl und gut is; inwiefern das Test-Ergabnis von dieser Zufallszahl abhängt, ist in einem solchen Fall komplett offensichtlich.

- -
+
- - - +

TestFrame definiert nicht nur einen Mechanismus, einen Datenblock von Zufallszahlen zu erzeugen, sondern er definiert auch deterministische Sequenzen und Familien solcher Sequenzen. Diese werden aus einem »Diskriminator« gebildet. Per TestFrame::reseed() verwirft man alle bestehenden Testframes und zieht einen neuen Diskriminator. Sofern ein Test mit Test-Frames einzeln reproduzierbar sein soll, muß dieres reseed() aufgerufen werden. Das übersieht man natürlich leicht, denn normalerweise will man solche Tests nicht deterministisch wiederholen (was solange gilt, bis es mal ein ernstes Problem gibt)

- -
+ @@ -61725,29 +61716,23 @@ - - - +

Wenn die Lumiera-Applikation mal etwas Vergleichbares braucht, muß es anderweitig an den Application-Lifecycle gebunden werden

- -
+
- - - +

soll aber ohne Weiteres stets für jeden Test verfügbar sein

- -
+
@@ -61759,9 +61744,7 @@ - - - +

Explizit ausformuliert: @@ -61775,70 +61758,55 @@ - - + - - - +

Jede Einrichtung, die am re-Seeding teilnehmen muß, hat sich da selbst in den Code zu integrieren. Sobald sich diese Integrierung ändert, ändert sich der Seed für alle Beteiligten. Aufgrund der Layer-Ordnung können nur Library-Einrichtungen an einem solchen Setup teilnehmen; alles oberhalb muß dann auf weitere, verteilte Random-Generatoren aufbauen

- -
+
- - - +

irgend ein Koordiniertungs-System, entweder compile-time oder run-time. Es muß sicherstellen, daß Partizipatoren leicht hinzukonfiguriert werden können, idealerweise automatisch, indem man einen Header includiert. Weiterhin muß aber sichergestellt werden, daß die Ordnung der Zuteilung stabil bleibt. Konsequenz: das wird aufwendiger als es auf den ersten Blick aussieht; dafür ist die Aufgabe für die Konsumenten des Random-Seed sehr einfach

- -
+
- - - +

Auch bei dieser Variante gibt es einen Registrierungs-Hook, der sehr bequem automatisiert wird. Allerdings liefert er nur einen einzigen Seed-Wert identisch an alle Konsumenten. Jeder Konsument muß daher zwingend seinen eigenen, lokalen Random-Generator verwalten und darf nirgends lib::defaultGen verwenden. Wenn mehr als ein einziger Client diese Regel mißachtet, bricht das System zusammen

- -
+
- - - +

Dieser Ansatz ist mit den beiden zuvor genannten Varianten kombinierbar. Es erlaubt dann beliebigen Komponenten in verschiedensten Setups, automatisch am re-Seeding teilzunehmen. Effektiv läuft das aber darauf hinaus, auch getrennte Generatoren für jeden Konsumenten zu verlangen, also keine bequeme Library-Funktion mehr!

- -
+
- - - +

Vorteil: in den meisten Tests ist die Situation so einfach, daß sich all die schwierigeren Fragen überhaupt nicht stellen @@ -61847,17 +61815,14 @@ Nachteil: das Thema ist nicht offensichtlich und wird deshalb leicht übersehen, so daß man dann im Ernstfall vor einem nicht-reproduzierbaren Problem steht

- -
+
- - - +

Vorteil: man kümmert sich nur um das, was wirklich ein Problem darstellt, und läßt alles andere laufen @@ -61866,8 +61831,7 @@ Nachteil: das Problembewußtsein muß da sein, sonst funktioniert der Ansatz nicht

- -
+
@@ -61888,57 +61852,45 @@ - - - +

tja ... alles schön gesültzt bis jetzt — geht aber am Problem vorbei

- -
+ - - - +

und der paßt nicht ins Schema: der sitzt nämlich NICHT in der lib-Lumiera

- -
+
- - - +

wir haben auch schon ein App-Event-System — das ist aber leider zu knapp definiert

- -
+ - - - +

...es kann nur einfachste void(void)-Funktionen aufrufen, und auch nicht in definierter Reihenfolge. Zudem gibt es keine Scopes, d.h. wir müßten ein globales Lifecycle-Event definieren ON_RESEED. Das ist genau das was ich definitiv nicht will — weil wir damit in ein viel anspruchsvolleres Feature einsteigen

- -
+
@@ -61979,9 +61931,7 @@ - - - +

Explizit nicht sinnvoll hierfür einen Unit-Test zu schreiben! @@ -62007,8 +61957,7 @@ - - + @@ -114336,7 +114285,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -114424,6 +114374,65 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + + + + + + + + + +

+ das macht die sache nun extrem ekelhaft und verworren +

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

+ in der einfachsten Form muß der Aufrufer herumtricksen, +

+

+ weil die Policy mit drei Lambdas konfiguriert wird... +

+ + +
+ +
+
+ + + +
+
+
@@ -114660,8 +114669,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) ...und kann aber keinen rechten Grund finden, es zu rechtfertigen....

- - + @@ -114707,9 +114715,60 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + + + + + +

+ ...der hier nicht zugrifft, denn kein anderes LocalTag +

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

+ weiß nicht recht warum ich so überhaupt keinen Antrieb hab, meine eigentliche Aufgabe voranzutreiben ... vielleicht weil ich zehn andere Verpflichtungen habe, die ich nur wegen Lumiera zurückstelle, obwohl sie einfacher zu erledigen wären? +

+ +
+
+ + + + +

+ ...bevor sich ein Workaround festsetzt, den man dann immer wieder vergißt, oder das ganze Thema wird insgesamt vergessen und führt dann zu Bugs. +

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