From e6d233def238441f6c71f0cebad4d372d56e0a27 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 1 Sep 2023 23:51:15 +0200 Subject: [PATCH] Activity-Lang: instrumentation to make the complete call sequence visible No new functionality, and implementation works as expected. This test case covers an especially tricky setup, where a calculation shall be triggered from an external event, while ensuring that the actual processing can start only after also the regular time-bound scheduling has taken place (this might be used to prevent an unexpectedly early external signal to cause writing into an output buffer before the defined window of data delivery) --- tests/vault/gear/scheduler-activity-test.cpp | 75 +++--- wiki/thinkPad.ichthyo.mm | 251 +++++++++++-------- 2 files changed, 194 insertions(+), 132 deletions(-) diff --git a/tests/vault/gear/scheduler-activity-test.cpp b/tests/vault/gear/scheduler-activity-test.cpp index 22974b5c0..276585c81 100644 --- a/tests/vault/gear/scheduler-activity-test.cpp +++ b/tests/vault/gear/scheduler-activity-test.cpp @@ -472,7 +472,7 @@ namespace test { Job testJob{detector.buildMockJob("testJob", nominal, 12345)}; TimeVar now = Time{5,5}; - detector.executionCtx.getSchedTime = [&]{ // increase "current" time on each access + detector.executionCtx.getSchedTime = [&]{ // increase "current" time on each access now += FSecs(1,20); return now; }; @@ -491,18 +491,17 @@ namespace test { .beforeInvocation("CTX-work").arg("5.155","") .beforeInvocation("testJob") .arg("7.007",12345) .beforeInvocation("CTX-done").arg("5.355","")); - cout << detector.showLog()<is (Activity::NOTIFY)); + CHECK (anchor.next->next->is (Activity::GATE)); + CHECK (anchor.next->next->next->is (Activity::WORKSTART)); + CHECK (anchor.next->next->next->next->is (Activity::INVOKE)); + CHECK (anchor.next->next->next->next->next->is (Activity::FEED)); + CHECK (anchor.next->next->next->next->next->next->is (Activity::FEED)); + CHECK (anchor.next->next->next->next->next->next->next->is (Activity::WORKSTOP)); + CHECK (anchor.next->next->next->next->next->next->next->next == nullptr); + // insert test-instrumentation to trace activation + detector.watchGate (anchor.next->next, "theGate"); detector.insertActivationTap(trigger.data_.notification.target, "trigger"); + detector.insertActivationTap(anchor.next->data_.notification.target, "deBlock"); + + // rig the λ-post to forward dispatch as expected in real usage detector.executionCtx.post.implementedAs( [&](Time when, Activity& postedAct, auto& ctx) { - if (when == ctx.getSchedTime()) + if (when == ctx.getSchedTime()) // only for POST to run „right now“ return activityLang.dispatchChain (postedAct, ctx); else return activity::PASS; }); + ///// Case-1 : send a notification from prerequisite, but prior to activating primary-chain CHECK (activity::PASS == ActivityLang::dispatchChain (trigger, detector.executionCtx)); - CHECK (detector.verifyInvocation("trigger") .seq(0).arg("5.555 --notify-↯> Act(GATE") - .arg("<2, until 0:00:10.000")); + CHECK (detector.verifyInvocation("trigger") .seq(0).arg("5.555 --notify-↯> Act(GATE") // notification dispatched to the Gate + .arg("<2, until 0:00:10.000")); // Note: the Gate-latch expects 2 notifications + CHECK (detector.ensureNoInvocation("testJob") // ==> the latch was decremented but no invocation yet + .afterInvocation("trigger")); + ///// Case-2 : now activate the primary-chain detector.incrementSeq(); CHECK (activity::PASS == ActivityLang::dispatchChain (anchor, detector.executionCtx)); - CHECK (detector.verifyInvocation("CTX-post").seq(1).arg("5.555","Act(NOTIFY","≺test::CTX≻") - .beforeInvocation("after-theGate").seq(1).arg("5.555 ⧐ Act(WORKSTART") - .beforeInvocation("CTX-work").arg("5.555","") - .beforeInvocation("testJob") .arg("7.007",12345) - .beforeInvocation("CTX-done").arg("5.555","") - .beforeInvocation("theGate").seq(1).arg("<0, until -85401592:56:01.825")); + CHECK (detector.verifyInvocation("CTX-post").seq(1).arg("5.555","Act(NOTIFY","≺test::CTX≻") // immediately at begin, the internal self-notification is posted + .beforeInvocation("deBlock") .seq(1).arg("5.555 --notify-↯> Act(GATE") // ...and then dispatched recursively via ActivityLang::dispatchChain() towards the Gate + .arg("<1, until 0:00:10.000") // Note: at this point, the Gate-latch expects 1 notifications + .beforeInvocation("CTX-post").seq(1).arg("5.555","after-theGate","≺test::CTX≻") // ==> the latch was decremented and the Gate OPENS, and thus post() the tail-chain + .beforeInvocation("after-theGate") .arg("5.555 ⧐ Act(WORKSTART") // ...causing the activation to pass behind the Gate + .beforeInvocation("CTX-work").seq(1).arg("5.555","") // ...through WORKSTART + .beforeInvocation("testJob") .seq(1).arg("7.007",12345) // ...then invoke the JobFunctor itself (with the nominal Time{7,7}) + .beforeInvocation("CTX-done").seq(1).arg("5.555","") // ...and finally the WORKSTOP + .beforeInvocation("theGate") .seq(1).arg("5.555 ⧐ Act(GATE") // RETURN to the primary-chain activation (after the internal self-notification) + .arg("<0, until -85401592:56:01.825")); // -- however, after opening the Gate, the notification has blocked it permanently + CHECK (detector.ensureNoInvocation("testJob") // ==> thus no further (redundant) activation of the JobFunctor + .afterInvocation("CTX-done").seq(1)); detector.incrementSeq(); - CHECK (activity::PASS == ActivityLang::dispatchChain (trigger, detector.executionCtx)); - - CHECK (detector.verifyInvocation("trigger") .seq(2).arg("5.555 --notify-↯> Act(GATE") - .arg("<0, until -85401592:56:01.825")); - cout << detector.showLog()< Act(GATE") // ... it reaches the Gate + .arg("<0, until -85401592:56:01.825")); // ... but the Gate has been closed permanently (by setting the deadline to Time::MIN) + CHECK (detector.ensureNoInvocation("testJob") // ==> no further invocation + .afterInvocation("trigger").seq(2)); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index ae994a02e..e75da39c0 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -65136,9 +65136,7 @@ - - - +

Frühjahr 2018 komplett überarbeitet. @@ -65193,9 +65191,7 @@ - - - +

war im Einsatz seit Beginn der Lumiera-Projektes. @@ -65221,9 +65217,7 @@ - - - +

Im Einsatz seit der Behebung des CLang-Problems bis heute (3/2018). @@ -65270,9 +65264,7 @@ - - - +

Bei diesem Wunsch-Profil bleibt nur eine Variante von Lösung-2 @@ -65280,9 +65272,7 @@ - - - +

...denn nur eine dynamische Laufzeit-Factory ermöglicht, jederzeit  den Konstruktionsmodus zu wechseln @@ -65293,9 +65283,7 @@ - - - +

...denn nur ein Instanz-Pointer kann umgebogen oder auf NULL zurückgesetzt werden. @@ -65337,9 +65325,7 @@ - - - +

...denn er muß komplett generisch sein, und lib/depend.hpp darf keinerlei Kenntnis @@ -65355,9 +65341,7 @@ - - - +

...denn ein Lambda kann in dieser Lage grundsätzlich keinen optimierungs-Vorteil bringen, @@ -65370,9 +65354,7 @@ - - - +

...denn es läuft darauf hinaus, daß die Nutzung eines Dienstes zwar on demand erfolgt, @@ -65428,9 +65410,7 @@ - - - +

scheidet für unser Nutzungsmuster aus @@ -65438,9 +65418,7 @@ - - - +

...denn es bedutet effektiv, daß viele Instanz-Zeiger "herumfliegen", @@ -65504,9 +65482,7 @@ - - - +

...d.h. Service-Zugang wird automatisch geschlossen, @@ -65528,9 +65504,7 @@ - - - +

sie werden ja sofort ausgewertet, da die Service-Instanz unmittelbar gebaut wird @@ -65598,9 +65572,7 @@ - - - +

aber Lebenszyklus ist an die Factory gebunden @@ -65669,9 +65641,7 @@ - - - +

Folgendes Szenario ignorieren wir: @@ -65759,9 +65729,7 @@ - - - +

statische Storage ist irgendwie cool @@ -65778,9 +65746,7 @@ - - - +

das gilt im Besonderen für eine default-Storage als Singleton. @@ -65815,9 +65781,7 @@ - - - +

...aber dann eben nicht mehr elegant. @@ -65858,9 +65822,7 @@ - - - +

Und das hängt nur von den Umständen ab. @@ -65915,9 +65877,7 @@ - - - +

daher muß sein dtor vor dem dtor von DependencyFactory laufen @@ -65940,9 +65900,7 @@ - - - +

denn während dem dtor existiert das Lock nicht mehr. @@ -66004,9 +65962,7 @@ - - - +

denn nun wird das "singleton" schon ziemlich gehaltlos, @@ -77778,9 +77734,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Falls es eine zeitliche Grenze gibt, vor der die Berechnungen grundsätzlich nicht starten sollen — beispielsweise weil die Ergebnis-Daten in einen Puffer geschrieben werden, der erst ab einem bestimmten Zeitpunkt verfügbar ist; dies wird dann indirekt codiert durch den Start-Zeitpunkt der Haupt-Berechnung, und das Einschleifen einer Notification auf das direkt nachfolgende Gate, was dazu führt, daß eine extern empfangene Notification von abgeschlossenen Vorgänger-Berechnungen erst nach diesem Zeitpunkt das Gate öffnen können. Diese interne Freischaltungs-Notification wird direkt hinter dem Ankerpunkt (dem POST) eingehängt, verwendet aber ansonsten die vorhandenen Mechanismen (indirekte Aktivierung über den Dispatch-hook) @@ -78169,9 +78123,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

....denn die Implementierung des Notification-Dispatch erkennt, wenn eine Notification das Gate öffnet, und dispatched in diesem Fall den Chain sofort zur Ausführung und sperrt dann auch das Gate endgültig; das bedeutet: wenn die Aktivierung über diesen Mechanismus erfolgt, wird ein geplanter re-Trigger niemals zum Zug kommen (sondern dann stets ein bereits gesperrtes Gate vorfinden @@ -78182,9 +78134,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

entscheidendes Kriterium: was ist der Regelfall? @@ -78252,9 +78202,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

nur wenn dadruch das Latch ⟼ 0 geht @@ -78268,9 +78216,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

indem die deadline ≔ Time::MIN @@ -79079,14 +79025,75 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +

+ kann ja normalerweise leer bleiben (es ist ja nur ein Test) +

+ +
+
- + + + + + + + + + +

+ ...weil es eine eigens aufgerufene Funktion ist, die ich auch für alle drei Spezialisierungen sinngemäß anders implementieren kann +

+ +
+
+ + + + +

+ Ein Problemfall wäre, wenn eine Funktion einen Rückgabetyp hat, der nicht default-konstruierbar ist. Ja, sowas kommt vor — aber ich betrachte es hier nun wirklich als die Ausnahme, und es gibt immer noch den Workaround, in einem solchen Fall eben den gewünschten Rückgabewert explizit zu setzen (oder gleich die λ-Impl zu verwenden). +

+ +
+ +
+
+
+ + - + + + - + + + + + +

+ bisher gab es nur einen Testfall, und der betraf den ActivityDetector selber +

+ +
+ +
+ + + + + + +
@@ -79689,9 +79696,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Es ist noch nicht klar, wie die Callbacks von async-IO aufgeschaltet werden @@ -80160,8 +80165,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -80176,22 +80181,20 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - + + + + - - + + - - + + - - - +

Im ActivityDetector sind normalerweise die ctx-λ als reine Logging-Funktionen implementiert; aber die nun konstruierte Ausführungs-Logik setzt fest darauf, daß ein Aufruf von ctx.post() sofort in einen ActivityLang::dispatchChain() umgesetzt wird (allerdings nicht wirklich immer, sondern nur in dem Fall, daß wir das GroomingToken halten und die im post() gegebene Zeit die aktuelle Zeit ist) @@ -80202,13 +80205,25 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + +

+ gemeint ist: den Aufruf von λ-post könnte man weitergeben an die ActivityLang-Instanz, die im Test-Kontext konstruiert wurde; damit würde also die Prüfung/Steuerung mit dem GroomingToken ausgelassen, aber ansonsten würde dann die Aktivierung direkt an ActivityLang::dispatchChain() weitergegeben. +

+

+ +

+

+ Wichtige Einschränkung: dieses »Durchreichen« darf man nur machen, wenn der post für „jetzt gleich“ ist, d.h. die im post() gegebene Zeit muß man checken (sonst würde z.B. ein geblocktes Gate, das sich in die Zukunft schedulen möchte, sofort in eine Endlosschleife laufen) +

+ +
- - - +

FakeExecutionCtx bzw. die dort verwendete DiagnosticFun, auf die man ja tatsächlich zugreifen können sollte (um den Rückgabewert zu manipulieren) — und das wäre bei einer std::function  nicht mehr möglich... @@ -80226,6 +80241,34 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + +

+ Testfall-1: +

+

+ notification ⟼ decr(Gate) ⟼ ✗ +

+

+ Testfall-2: +

+

+ primary-chain ⟼ notify an Gate ⟼ λ-post ⟼ dispatch ⟼ decr(gate) ⟼ PASS ⟼ Rest der Kette +

+ +
+
+