From 206c67cc8a5bc9ca5ff9a44affd61274f90ef9fc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 13 Dec 2023 19:42:38 +0100 Subject: [PATCH] =?UTF-8?q?Scheduler-test:=20adapt=20=CE=BB-post=20to=20in?= =?UTF-8?q?clude=20deadline=20info?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...it seems impossible to solve this conundrum other than by opening a path to override a contextual deadline setting from within the core Activity-Language logic. This will be used in two cases - when processing a explicitly coded POST (using deadline from the POST) - after successfully opening a Gate by NOTIFY (using deadline from Gate) All other cases can now supply Time::NEVER, thereby indicating that the processing layer shall use contextual information (intersection of the time intervals) --- src/vault/gear/activity.hpp | 34 +++--- src/vault/gear/scheduler.hpp | 4 +- tests/vault/gear/activity-detector-test.cpp | 9 +- tests/vault/gear/activity-detector.hpp | 2 +- tests/vault/gear/scheduler-activity-test.cpp | 43 ++++--- wiki/thinkPad.ichthyo.mm | 115 +++++++------------ 6 files changed, 92 insertions(+), 115 deletions(-) diff --git a/src/vault/gear/activity.hpp b/src/vault/gear/activity.hpp index 7201404ca..813284221 100644 --- a/src/vault/gear/activity.hpp +++ b/src/vault/gear/activity.hpp @@ -211,7 +211,7 @@ namespace gear { constexpr void _verify_usable_as_ExecutionContext () { - ASSERT_MEMBER_FUNCTOR (&EXE::post, Proc(Time, Activity*, EXE&)); + ASSERT_MEMBER_FUNCTOR (&EXE::post, Proc(Time, Time, Activity*, EXE&)); ASSERT_MEMBER_FUNCTOR (&EXE::work, void(Time, size_t)); ASSERT_MEMBER_FUNCTOR (&EXE::done, void(Time, size_t)); ASSERT_MEMBER_FUNCTOR (&EXE::tick, Proc(Time)); @@ -292,6 +292,14 @@ namespace gear { bool isHold () const { return rest > 0; } bool isFree (Time now) const { return not (isHold() or isDead(now)); } void incDependencies() { ++rest; } + + Time + lockPermanently() + { + auto oldDeadline{dead}; + dead = Time::MIN; + return Time{oldDeadline}; + } }; /** Time window to define for activation */ @@ -557,33 +565,33 @@ namespace gear { // maybe the Gate has been opened by this notification? if (data_.condition.isFree(now)) {//yes => activate gated chain but lock redundant invocations - data_.condition.dead = Time::MIN; - return postChain (now, executionCtx); + Time dead = data_.condition.lockPermanently(); + return postChain (now,dead, executionCtx); } } return activity::PASS; } template activity::Proc - dispatchSelf (Time when, EXE& executionCtx) + dispatchSelf (Time when, Time dead, EXE& executionCtx) { - return executionCtx.post (when, this, executionCtx); + return executionCtx.post (when, dead, this, executionCtx); } template activity::Proc - dispatchSelfDelayed (Time now, EXE& executionCtx) + dispatchSelfDelayed (Time now, Time dead, EXE& executionCtx) { - dispatchSelf (now + executionCtx.getWaitDelay(), executionCtx); + dispatchSelf (now + executionCtx.getWaitDelay(), dead, executionCtx); return activity::SKIP; } template activity::Proc - postChain (Time when, EXE& executionCtx) + postChain (Time when, Time dead, EXE& executionCtx) { REQUIRE (next); - return executionCtx.post (when, next, executionCtx); + return executionCtx.post (when, dead, next, executionCtx); } template @@ -646,7 +654,7 @@ namespace gear { case GATE: return checkGate (now, executionCtx); case POST: - return dispatchSelf (Time{data_.timeWindow.life}, executionCtx); + return dispatchSelf (Time{data_.timeWindow.life},Time{data_.timeWindow.dead}, executionCtx); case FEED: return activity::PASS; case HOOK: @@ -713,10 +721,10 @@ namespace gear { case HOOK: return notifyHook (now, executionCtx); case POST: - case FEED: - return postChain (now, executionCtx); + case FEED: // ▽▽▽▽▽ implies to use a deadline from the context + return postChain (now,Time::NEVER, executionCtx); default: - return dispatchSelfDelayed (now, executionCtx); + return dispatchSelfDelayed (now,Time::NEVER, executionCtx); } // Fallback: self-re-dispatch for async execution (-> getWaitDelay()) } diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index 410f3d183..8e67f1728 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -505,13 +505,13 @@ namespace gear { * down here as a nested sub-context. */ activity::Proc - post (Time when, Activity* chain, ExecutionCtx& ctx) + post (Time when, Time dead, Activity* chain, ExecutionCtx& ctx) { REQUIRE (chain); ActivationEvent chainEvent = ctx.rootEvent; chainEvent.activity = chain; chainEvent.starting = _raw(chain->constrainedStart (when)); - chainEvent.deadline = _raw(chain->constrainedDeath (chainEvent.deathTime())); + chainEvent.deadline = _raw(chain->constrainedDeath (dead.isRegular()? dead:chainEvent.deathTime())); ExecutionCtx subCtx{scheduler_, chainEvent}; return scheduler_.layer2_.postDispatch (chainEvent, subCtx, scheduler_.layer1_); } diff --git a/tests/vault/gear/activity-detector-test.cpp b/tests/vault/gear/activity-detector-test.cpp index 6e19d6411..4dcd10996 100644 --- a/tests/vault/gear/activity-detector-test.cpp +++ b/tests/vault/gear/activity-detector-test.cpp @@ -218,6 +218,7 @@ namespace test { activity::_verify_usable_as_ExecutionContext(); Time t = randTime(); + Time td{t+Time(0,1)}; size_t x = rand(); Activity a; @@ -232,8 +233,8 @@ namespace test { ctx.done (t,x); CHECK (detector.verifyInvocation(CTX_DONE).arg(t,x)); - CHECK (activity::PASS == ctx.post (t, &a, ctx)); - CHECK (detector.verifyInvocation(CTX_POST).arg(t,&a,ctx)); + CHECK (activity::PASS == ctx.post (t,td, &a, ctx)); + CHECK (detector.verifyInvocation(CTX_POST).arg(t,td,&a,ctx)); CHECK (activity::PASS == ctx.tick(t)); CHECK (detector.verifyInvocation(CTX_TICK).arg(t)); @@ -362,7 +363,7 @@ namespace test { ActivityDetector detector; Activity chain; - Activity gate{1}; + Activity gate{1, Time{22,22}}; gate.next = &chain; Activity notification{&gate}; CHECK (gate.data_.condition.rest == 1); @@ -373,7 +374,7 @@ namespace test { notification.dispatch (tt, detector.executionCtx); CHECK (detector.verifyInvocation("tap-GATE").arg("11.011 --notify-↯> Act(GATE") - .beforeInvocation("CTX-post").arg("11.011", "Act(TICK", "≺test::CTX≻")); + .beforeInvocation("CTX-post").arg("11.011","22.022", "Act(TICK", "≺test::CTX≻")); CHECK (gate.data_.condition.rest == 0); } diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp index dbbaa4290..2b3dc59c5 100644 --- a/tests/vault/gear/activity-detector.hpp +++ b/tests/vault/gear/activity-detector.hpp @@ -572,7 +572,7 @@ namespace test { struct FakeExecutionCtx; - using SIG_post = activity::Proc(Time, Activity*, FakeExecutionCtx&); + using SIG_post = activity::Proc(Time, Time, Activity*, FakeExecutionCtx&); using SIG_work = void(Time, size_t); using SIG_done = void(Time, size_t); using SIG_tick = activity::Proc(Time); diff --git a/tests/vault/gear/scheduler-activity-test.cpp b/tests/vault/gear/scheduler-activity-test.cpp index 14010cb61..dfa04b835 100644 --- a/tests/vault/gear/scheduler-activity-test.cpp +++ b/tests/vault/gear/scheduler-activity-test.cpp @@ -125,7 +125,7 @@ namespace test { Time tt{5,5}; post.activate (tt, detector.executionCtx); - CHECK (detector.verifyInvocation("CTX-post").arg("11.000", "Act(POST", "≺test::CTX≻")); + CHECK (detector.verifyInvocation("CTX-post").arg("11.000","22.000", "Act(POST", "≺test::CTX≻")); } @@ -157,8 +157,13 @@ namespace test { /** @test behaviour of Activity::NOTIFY when _activated_ * - notification is dispatched as special message to an indicated target Activity - * - when activated, a `NOTIFY`-Activity _posts itself_ through the Execution Context hook - * - this way, further processing will happen in management mode (single threaded) + * - when activated, a `NOTIFY`-Activity _dispatches_ into the Activity::notify() + * of the target Activity + * - what happens then depends on the target + * - usually the target is a `GATE` (see below), but here for sake of simplicity, + * a `TICK`-Activity is used; on notification, this will _post_ itself as new task, + * with a delay retrieved from context (here configured to be +1sec), and without + * specifying a deadline (thus a deadline from the context will be used). */ void verifyActivity_Notify_activate() @@ -170,7 +175,7 @@ namespace test { Time tt{111,11}; notify.activate (tt, detector.executionCtx); - CHECK (detector.verifyInvocation("CTX-post").arg("11.111", "Act(NOTIFY", "≺test::CTX≻")); + CHECK (detector.verifyInvocation("CTX-post").arg("12.111", Time::NEVER, "Act(TICK", "≺test::CTX≻")); } @@ -286,20 +291,21 @@ namespace test { void verifyActivity_Gate_opened() { + Time tt{333,33}; + Time td{555,55}; + Activity chain; - Activity gate{1}; + Activity gate{1,td}; gate.next = &chain; // Conditionals in the gate block invocations CHECK (gate.data_.condition.isHold()); CHECK (gate.data_.condition.rest == 1); - CHECK (gate.data_.condition.dead == Time::NEVER); + CHECK (gate.data_.condition.dead == td); ActivityDetector detector; Activity& wiring = detector.buildGateWatcher (gate); - Time tt{333,33}; - - // an attempt to activate blocks (returing SKIP, nothing else happens) + // an attempt to activate blocks (returning SKIP, nothing else happens) CHECK (activity::SKIP == wiring.activate (tt, detector.executionCtx)); CHECK (1 == gate.data_.condition.rest); // unchanged (and locked)... CHECK (detector.verifyInvocation("tap-GATE").arg("33.333 ⧐ Act(GATE")); @@ -311,7 +317,7 @@ namespace test { CHECK (detector.verifyInvocation("tap-GATE").seq(0).arg("33.333 ⧐ Act(GATE") .beforeInvocation("tap-GATE").seq(1).arg("33.333 --notify-↯> Act(GATE") - .beforeInvocation("CTX-post").seq(1).arg(tt, "after-GATE", "≺test::CTX≻")); + .beforeInvocation("CTX-post").seq(1).arg(tt,td, "after-GATE", "≺test::CTX≻")); CHECK (gate.data_.condition.dead == Time::MIN); detector.incrementSeq(); @@ -392,8 +398,7 @@ namespace test { * - use a directly wired, arbitrary chain * - dispatch will activate all Activities * - however, when the Gate is configured to be blocked - * (waiting on prerequisites), then the rest of the chain is not activated, - * only a re-check of the Gate is scheduled for later (1.011 -> 2.011) + * (waiting on prerequisites), then the rest of the chain is not activated. * - the dispatch function also handles the notifications; * when a notification towards the Gate is dispatched, the Gate is * decremented and thereby opened; activation of the rest of the chain @@ -403,8 +408,9 @@ namespace test { dispatchChain() { Time tt{11,1}; + Time td{22,2}; Activity tick; - Activity gate{0}; + Activity gate{0,td}; gate.next = &tick; Activity post{tt, &gate}; // so now we have POST ⟶ GATE ⟶ TICK; @@ -433,7 +439,7 @@ namespace test { CHECK (activity::PASS == ActivityLang::dispatchChain (¬ify, detector.executionCtx)); // dispatch a notification (case/seq == 2) CHECK (detector.verifyInvocation("Gate") .seq(2).arg("1.011 --notify-↯> Act(GATE") // ...notification dispatched towards the Gate - .beforeInvocation("CTX-post").seq(2).arg("1.011","after-Gate","≺test::CTX≻")); // ...this opened the Gate and posted/requested activation of the rest of the chain + .beforeInvocation("CTX-post").seq(2).arg("1.011","2.022","after-Gate","≺test::CTX≻")); // ...this opened the Gate and posted follow-up chain CHECK (detector.ensureNoInvocation("after-Gate").seq(2) // verify that activation was not passed out directly .afterInvocation("CTX-post").seq(2)); CHECK (detector.ensureNoInvocation("CTX-tick").seq(2) // verify also the λ-tick was not invoked directly @@ -530,7 +536,7 @@ namespace test { // rig the λ-post to forward dispatch as expected in real usage detector.executionCtx.post.implementedAs( - [&](Time when, Activity* postedAct, auto& ctx) + [&](Time when, Time, Activity* postedAct, auto& ctx) { if (when == ctx.getSchedTime()) // only for POST to run „right now“ return ActivityLang::dispatchChain (postedAct, ctx); @@ -548,10 +554,9 @@ namespace test { ///// 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≻") // 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 + CHECK (detector.verifyInvocation("deBlock") .seq(1).arg("5.555 --notify-↯> Act(GATE") // immediately at begin, the internal self-notification is dispatched 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("CTX-post").seq(1).arg("5.555","10.0","after-theGate","≺test::CTX≻") //==> latch was decremented, Gate OPENS, posting the tail-chain with Deadline from Gate .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}) @@ -626,7 +631,7 @@ namespace test { detector.incrementSeq(); CHECK (activity::PASS == ActivityLang::dispatchChain (¬ify, detector.executionCtx)); CHECK (detector.verifyInvocation("CTX-done").seq(1).arg("5.555", "") // activation of WORKSTOP via callback - .beforeInvocation("CTX-post").seq(1).arg("5.555", "Act(NOTIFY", "≺test::CTX≻")); // the following NOTIFY is posted... + .beforeInvocation("CTX-post").seq(1).arg("5.555","10.0","WORKSTART","≺test::CTX≻"));// the attached chain is activated, POSTing the second Activity } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index a0af7fbb6..17a6d960f 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -81224,8 +81224,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -93490,16 +93490,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

wie im Beispiel im Test: seed=62 ⟹ sehr schnell ein Expand und dann mehrere massive Wellen die bis an den Anschlag gehen

- -
+
@@ -93517,32 +93514,26 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...denn normalerweise ist ein calibrate() ganz verträumt   (und sicher nicht zufällig) im ersten simpleUsage-Example versteckt

- -
+
- - - +

angeblich um zu demonstrieren, daß der Hash gleich ist...

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

2023-09-07 17:15:25 @@ -93641,9 +93630,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

es paßt nicht zum Stil im gesamten Kontext; wir sind hier in Implementierungs-Code, der grundsätzlich davon ausgeht, nur sinnvoll aufgerufen zu werden — es geht nicht darum, einen User „an die Hand zu nehmen“ @@ -93674,9 +93661,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

ist das angemessen, @@ -93694,9 +93679,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

so manches an diesen Tests hier funktioniert nur scheinbar „einfach“ @@ -103124,16 +103107,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

164.458 / 1.016*100µs - 1

- -
+
@@ -103165,16 +103145,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

in einem Fall sehe ich das sogar in einer Zeile als "HT" (head time)

- -
+
@@ -103188,16 +103165,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

man sieht sofort wo das Problem liegt: die Deadlines ☠

- -
+ @@ -103205,16 +103179,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

nebenbei bemerkt: jeder Dispatch geht auch nochmal durch ctx.post

- -
+ @@ -103229,16 +103200,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...und zwar liegt die Wurzel in der Offenheit der Activity-Language; ich wollte (und will) diese nicht auf eine Implementierungs-Logik des Schedulers reduzieren; dadurch sind Redundanzen entstanden, und aus logischen Gründen müßte eingentlich das Zeitfenster [start,dead] vom initialen POST am Anfang der Kette gelten, zumindes »sinngemäß«

- -
+
@@ -103251,29 +103219,23 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

wenn überhaupt, sollte die Deadline des Target gelten

- -
+
- - - +

Deadlines betreffen die Aktivierung. Es ist die Aufgabe des Job-Planning, das per Verkettung zurückzuführen auf die gewünschte Ankunftszeit. Die Activity-Language könnte das gar nicht tun, denn ihr fehlt dazu die Information über Erfahrungswerte die Ausführungszeit betreffend

- -
+
@@ -103286,22 +103248,22 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Der Code ist wirklich performance-kritisch, und bis jetzt hab ich richtig gute Werte erziehlt, durch genau diese Art Maßnahmen.

- -
+
+ + + @@ -103312,11 +103274,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + - - + + + @@ -103352,16 +103318,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

ich weiß nicht, wie gut die CPU-Lasterzeugung funktioniert; Wohl möglich, daß Cache-Effekte die tatsächliche Zeit in de Höhe treiben

- -
+