From f84517547bf0a3b33c22c475d8229dda5ed0e485 Mon Sep 17 00:00:00 2001
From: Ichthyostega
Date: Wed, 14 Jun 2023 18:12:41 +0200
Subject: [PATCH] Dispatcher-Pipeline: coordination of base tick and
prerequisite expansion
- had to fix a logical inconsistency in the underlying Expander implementation
in TreeExplorer: the source-pipeline was pulled in advance on expansion,
in order to "consume" the expanded element immediately; now we retain
this element (actually inaccessible) until all of the immediate
children are consumed; thus the (visible) state of the PipeFrameTick
stays at the frame number corresponding to the top-level frame Job,
while possibly expanding a complete tree of flexible prerequisites
This test now gives a nice visualisation of the interconnected states
in the Job-Planning pipeline. This can be quite complex, yet I still think
that this semi-functional approach with a stateful pipeline and expand functors
is the cleanest way to handle this while encapsulating all details
---
src/lib/iter-tree-explorer.hpp | 26 +-
.../steam/engine/job-planning-setup-test.cpp | 14 +-
tests/library/iter-tree-explorer-test.cpp | 18 +-
wiki/thinkPad.ichthyo.mm | 237 +++++++++++++++++-
4 files changed, 262 insertions(+), 33 deletions(-)
diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp
index f3da7df8f..35d818d54 100644
--- a/src/lib/iter-tree-explorer.hpp
+++ b/src/lib/iter-tree-explorer.hpp
@@ -644,7 +644,8 @@ namespace lib {
* of this Expander (decorator) is to integrate those "nested child iterators" seamlessly
* into the overall iteration process; once a child iterator is exhausted, it will be
* popped and iteration continues with the previous child iterator or finally with
- * the source iterator wrapped by this decorator.
+ * the source iterator wrapped by this decorator. The source pipeline is only pulled
+ * once the expanded children are exhausted.
* @remark since we allow a lot of leeway regarding the actual form and definition of the
* _expansion functor_, there is a lot of minute technical details, mostly confined
* within the _FunTraits traits. For the same reason, we need to prepare two different
@@ -689,14 +690,14 @@ namespace lib {
expandChildren()
{
REQUIRE (this->checkPoint(), "attempt to expand an empty explorer");
+ REQUIRE (invariant());
ResIter expanded{ hasChildren()? expandChild_(*expansions_)
: expandRoot_(*this)};
- incrementCurrent(); // consume current head element (but don't clean-up)
if (not isnil(expanded))
- expansions_.push (move(expanded));
+ expansions_.push (move(expanded)); // note: source of expansion retained
else
- dropExhaustedChildren(); // clean-up only here to preserve the logical depth
+ iterNext(); // expansion unsuccessful, thus consume source immediately
ENSURE (invariant());
}
@@ -773,6 +774,12 @@ namespace lib {
protected:
+ bool
+ hasChildren() const
+ {
+ return 0 < depth();
+ }
+
/** @internal accessor for downstream layers to allow close collaboration */
ResIter&
accessCurrentChildIter()
@@ -785,13 +792,10 @@ namespace lib {
dropExhaustedChildren()
{
while (not invariant())
- ++expansions_;
- }
-
- bool
- hasChildren() const
- {
- return 0 < depth();
+ {
+ ++expansions_; // pop expansion stack (to reinstate invariant)
+ incrementCurrent(); // consume source of innermost expansion
+ }
}
};
diff --git a/tests/core/steam/engine/job-planning-setup-test.cpp b/tests/core/steam/engine/job-planning-setup-test.cpp
index da6274084..b6e4e9c28 100644
--- a/tests/core/steam/engine/job-planning-setup-test.cpp
+++ b/tests/core/steam/engine/job-planning-setup-test.cpp
@@ -213,14 +213,24 @@ namespace test {
}
- /** @test build and verify the exploration function to discover job prerequisites */
+ /** @test build and verify the exploration function to discover job prerequisites
+ * - use a setup where the master ExitNode requires a prerequisite ExitNode to be pulled
+ * - mark the pipeline-IDs, so that both nodes can be distinguished in the resulting Jobs
+ * - the `expandPrerequisites()` builder function uses JobTicket::getPrerequisites()
+ * - and this »expander« function is unfolded recursively such that first the source
+ * appears in the iterator, and as next step the child prerequisites, possibly to
+ * be unfolded further recursively
+ * - by design of the iterator pipeline, it is always possible to access the `PipeFrameTick`
+ * - this corresponds to the top-level JobTicket, which will produce the final frame
+ * - putting all these information together, proper working can be visualised.
+ */
void
exploreJobTickets()
{
MockDispatcher dispatcher{MakeRec() // define a single segment for the complete time axis
.attrib("mark", 11) // the »master job« for each frame has pipeline-ID ≔ 11
.scope(MakeRec()
- .attrib("mark",22) // add a »prerequisite job« with pipeline-ID ≔ 22
+ .attrib("mark",22) // add a »prerequisite job« marked with pipeline-ID ≔ 22
.genNode())
.genNode()};
diff --git a/tests/library/iter-tree-explorer-test.cpp b/tests/library/iter-tree-explorer-test.cpp
index 65f95de16..28043bd81 100644
--- a/tests/library/iter-tree-explorer-test.cpp
+++ b/tests/library/iter-tree-explorer-test.cpp
@@ -1148,7 +1148,7 @@ namespace test{
/** @test ability to wrap and handle IterSource based iteration.
- * Contrary to the preceding test case, here the point is to _base the whole pipleine_
+ * Contrary to the preceding test case, here the point is to _base the whole pipeline_
* on a data source accessible through the IterSource (VTable based) interface. The notable
* point with this technique is the ability to use some _extended sub interface of IterSource_
* and to rely on this interface to implement some functor bound into the TreeExplorer pipeline.
@@ -1168,19 +1168,19 @@ namespace test{
virtual PrivateSource* expandChildren() const =0;
};
- class VerySpecivicIter
+ class VerySpecificIter
: public WrappedLumieraIter
{
public:
- VerySpecivicIter(uint start)
+ VerySpecificIter(uint start)
: WrappedLumieraIter(NumberSequence{start})
{ }
virtual PrivateSource*
expandChildren() const override
{
- return new VerySpecivicIter{*wrappedIter() - 2};
+ return new VerySpecificIter{*wrappedIter() - 2};
}
uint
@@ -1194,7 +1194,7 @@ namespace test{
// simple standard case: create a new heap allocated IterSource implementation.
// TreeExplorer will take ownership (by smart-ptr) and build a Lumiera Iterator front-End
CHECK ("7-6-5-4-3-2-1" == materialise (
- treeExplore(new VerySpecivicIter{7})));
+ treeExplore(new VerySpecificIter{7})));
// missing source detected
@@ -1203,7 +1203,7 @@ namespace test{
// attach to an IterSource living here in local scope...
- VerySpecivicIter vsit{5};
+ VerySpecificIter vsit{5};
// ...and build a child expansion on top, which calls through the PrivateSource-API
// Effectively this means we do not know the concrete type of the "expanded children" iterator,
@@ -1219,17 +1219,17 @@ namespace test{
CHECK (4 == vsit.currentVal());
CHECK (0 == ii.depth());
- ii.expandChildren(); // note: calls through source's VTable to invoke VerySpecificIter::expandChildren()
+ ii.expandChildren(); // note: calls through source's VTable to invoke VerySpecificIter::expandChildren()
CHECK (1 == ii.depth());
CHECK (2 == *ii);
++ii;
CHECK (1 == *ii);
- CHECK (3 == vsit.currentVal());
+ CHECK (4 == vsit.currentVal()); // note: as long as expanded children are alive, the source pipeline is not pulled further
CHECK (1 == ii.depth());
++ii;
- CHECK (0 == ii.depth());
+ CHECK (0 == ii.depth()); // ... but now the children were exhausted and thus also the source advanced
CHECK (3 == *ii);
CHECK (3 == vsit.currentVal());
++ii;
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 14c222c1a..71577b1e0 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -55602,6 +55602,205 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FAIL___expectation___________
+
+
+ expect:J(11|200ms)-J(22|200ms)-J(11|240ms)-J(22|240ms)-J(11|280ms)-J(22|280ms)
+
+
+ actual:J(11|200ms)-J(22|240ms)-J(11|240ms)-J(22|280ms)-J(11|280ms)-J(22|320ms)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Lösung: Quelle erst weiter iterieren,
+
+
+ wenn wirklich notwendig
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Bedeutung der Invariante
+
+
+
+
+
+
+
+
+
+
+
+
+
+ technisch:
+
+
+ -
+ entweder Expansions-Stack ist leer
+
+ -
+ oder der oberste Iterator auf dem Stack ist nichtleer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Und zwar wegen der Objektorientierung: die gesamte Logik ist verkapselt, und nur wenige Operationen sind für abgeleitete Klassen zugänglich; im Besonderen ist incrementCurrent() private und das ist der einzige Weg um an die Iteration des Quell-Iterators zu kommen, aber genau dort wird auch dropExhaustedChildren aufgerufen()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insofern kann das Verwerfen des Quell-Objekts verzögert werden,
+
+
+ solange bis die daraus resultierenden Kinder aufgebraucht sind;
+
+
+ da wir ohnehin einen Stack benötigen, läßt sich dieser Status
+
+
+ auch verschachtelt aufrechterhalten
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ Expansion: Quell wird noch nicht angefaßt, aber Kind-Iterator wird bereitgelegt und daraus ggfs. das erste Element exponiert
+
+ -
+ Wenn eine Kind-Ebene leer wird, wird sie verworfen — und erst an dieser Stelle wird das nächste Quell-Element wirklich benötigt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ bisher stand hier das dropExhaustedChildren(), aber das fällt in disem Fall leer durch, wenn wir uns bereits wieder auf Basis-Level befinden — was bisher kein Problem war, denn es war bereits das Quell-Element konsumiert worden. Da wir aber diesen Fall klar zu fassen bekommen direkt in expandChildren() (weil eben die Expansion sich leer ergibt), können wir explizit in diesem Fall das Konsumieren der Quelle direkt machen. Ist im Ergebnis sogar logischer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ...ich hatte es seinerzeit wohl bemerkt, aber für nicht relevant bzw sogar „eigentlich logisch“ gehalten... jedenfalls war es im Testfall verify_IterSource() dokumentiert, da dort auch die Source-Pipeline (hinter dem IterSource-Interface) sichtbar ist und mit beobachtet wird. Dieser Fall (Zeile 1229) hat nun angesprochen und wurde entsprechend korrigiert
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -64555,8 +64754,8 @@
-
+
@@ -75138,7 +75337,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -75231,14 +75430,14 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
-
+
@@ -75270,7 +75469,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -75281,11 +75480,11 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
-
+
@@ -75306,13 +75505,29 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+