From b61ca94ee530633e34058a0dcf83a8ffff8e5c74 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 23 Oct 2023 01:48:46 +0200 Subject: [PATCH] =?UTF-8?q?Scheduler:=20rectify=20=CE=BB-post=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...to bring it more in line with all the other calls dealing with Activity* ...allows also to harmonise the ActivityLang::dispatchChain() ...and to compose the calls in Scheduler directly NOTE: there is a twist: our string-formatting helper did not render custom string conversions for objects passed as pointer. This was a long standing problem, caused by ambiguous templates overloads; now I've attempted to solve it one level more down, in util::StringConv. This solution may turn out brittle, since we need to exclude any direct string conversion, most notably the ones for C-Strings (const char*) In case this solution turns out unsustainable, please feel free to revert this API change, and return to passing Activity& in λ-post, because in the end this is cosmetics. --- src/lib/format-obj.hpp | 7 +- src/lib/meta/util.hpp | 42 ++++++++---- src/vault/gear/activity-lang.hpp | 7 +- src/vault/gear/activity.hpp | 8 +-- src/vault/gear/scheduler-commutator.hpp | 2 +- src/vault/gear/scheduler.hpp | 8 +-- tests/32scheduler.tests | 2 +- tests/library/format-helper-test.cpp | 18 +++--- .../library/meta/late-bind-instance-test.cpp | 10 +-- tests/vault/gear/activity-detector-test.cpp | 4 +- tests/vault/gear/activity-detector.hpp | 2 +- tests/vault/gear/scheduler-activity-test.cpp | 22 +++---- tests/vault/gear/work-force-test.cpp | 2 +- wiki/thinkPad.ichthyo.mm | 64 ++++++++++++++++++- 14 files changed, 142 insertions(+), 56 deletions(-) diff --git a/src/lib/format-obj.hpp b/src/lib/format-obj.hpp index aeb45ee77..8bf9bb778 100644 --- a/src/lib/format-obj.hpp +++ b/src/lib/format-obj.hpp @@ -172,8 +172,11 @@ namespace util { * cv-qualifiactions and references are stripped from the type * @note Deliberately there is no magic detection/support for pointers. * This function _must not be overloaded_ (to avoid ambiguities - * in more elaborate template instantiations). If you want pointers - * to be indicated (with address), please use util::showPtr explicitly. + * in more elaborate template instantiations). + * @remark Since 10/2023 an attempt was made to solve this problem down + * in `StringConf`, yet this solution might be brittle. If you + * want pointers to be indicated (with address), consider using + * util::showPtr explicitly. */ template inline std::string diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 72d64cc4e..52c112eb5 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -385,16 +385,47 @@ namespace util { catch(...) { return FAILURE_INDICATOR; } }; + /** specialisation to allow rendering pointers to string-convertible types. + * @note different than #showPtr(), the address is not rendered here. + * @warning could be brittle, due to overlap with lib::meta::enable_CustomStringConversion; + * whenever there is an explicit string conversion, it must be excluded here. + */ + template + struct StringConv, char> + ,std::is_same, void> + > >> + { + static std::string + invoke (X const* ptr) noexcept + try { return ptr? "↗" + StringConv::invoke(*ptr) + : BOTTOM_INDICATOR + "«"+typeStr(ptr)+"»"; + } + catch(...) { return FAILURE_INDICATOR; } + }; + // NOTE: this is meant to be extensible; // more specialisations are e.g. in format-obj.hpp + /** human readable display of boolean values + * @return "`true`" or "`false`" + */ + inline std::string + showBool (bool yes) noexcept + { + return yes? lib::meta::BOOL_TRUE_STR + : lib::meta::BOOL_FALSE_STR; + } + /** pretty-print a double in fixed-point format */ std::string showDouble (double) noexcept; std::string showFloat (float) noexcept; + + + /** pretty-print an address as hex-suffix */ std::string showAddr (void const* addr) noexcept; @@ -425,17 +456,6 @@ namespace util { : BOTTOM_INDICATOR + " «" + typeStr(smPtr) + "»"; } - /** human readable display of boolean values - * @return "`true`" or "`false`" - */ - inline std::string - showBool (bool yes) noexcept - { - return yes? lib::meta::BOOL_TRUE_STR - : lib::meta::BOOL_FALSE_STR; - } - - }// namespace util #endif /*LIB_META_UTIL_H*/ diff --git a/src/vault/gear/activity-lang.hpp b/src/vault/gear/activity-lang.hpp index 3a6ffcf0e..cda2f0bb1 100644 --- a/src/vault/gear/activity-lang.hpp +++ b/src/vault/gear/activity-lang.hpp @@ -121,11 +121,12 @@ namespace gear { */ template static activity::Proc - dispatchChain (Activity& chain, EXE& executionCtx) + dispatchChain (Activity* chain, EXE& executionCtx) { - activity::Proc res = chain.dispatch (executionCtx.getSchedTime(), executionCtx); + if (!chain) return activity::PASS; + activity::Proc res = chain->dispatch (executionCtx.getSchedTime(), executionCtx); if (activity::PASS == res) - res = activateChain (chain.next, executionCtx); + res = activateChain (chain->next, executionCtx); else if (activity::SKIP == res) res = activity::PASS; return res; diff --git a/src/vault/gear/activity.hpp b/src/vault/gear/activity.hpp index 54cc6a81b..07aa6bd06 100644 --- a/src/vault/gear/activity.hpp +++ b/src/vault/gear/activity.hpp @@ -182,7 +182,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, 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)); @@ -485,7 +485,7 @@ namespace gear { activity::Proc dispatchNotify (Time now, EXE& executionCtx) { - return executionCtx.post (now, *this, executionCtx); + return executionCtx.post (now, this, executionCtx); } template @@ -531,7 +531,7 @@ namespace gear { activity::Proc dispatchSelf (Time when, EXE& executionCtx) { - return executionCtx.post (when, *this, executionCtx); + return executionCtx.post (when, this, executionCtx); } template @@ -547,7 +547,7 @@ namespace gear { postChain (Time when, EXE& executionCtx) { REQUIRE (next); - return executionCtx.post (when, *next, executionCtx); + return executionCtx.post (when, next, executionCtx); } template diff --git a/src/vault/gear/scheduler-commutator.hpp b/src/vault/gear/scheduler-commutator.hpp index 960d28985..915e765ed 100644 --- a/src/vault/gear/scheduler-commutator.hpp +++ b/src/vault/gear/scheduler-commutator.hpp @@ -216,7 +216,7 @@ namespace gear { Time now = executionCtx.getSchedTime(); if (decideDispatchNow (when, now)) - return ActivityLang::dispatchChain (*chain, executionCtx); + return ActivityLang::dispatchChain (chain, executionCtx); else if (holdsGroomingToken (thisThread())) layer1.feedPrioritisation (*chain, when); diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index 2c4cdb336..490103d6d 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -246,9 +246,9 @@ namespace gear { * @remark the \a ctx argument is redundant (helpful for test/mocking) */ activity::Proc - post (Time when, Activity& chain, ExecutionCtx& ctx) + post (Time when, Activity* chain, ExecutionCtx& ctx) { - return layer2_.postDispatch (&chain, when, ctx, layer1_); + return layer2_.postDispatch (chain, when, ctx, layer1_); } void @@ -307,8 +307,8 @@ namespace gear { loadControl_.incomingCapacity (head,now)); }) .performStep([&]{ - Activity* act = layer2_.findWork(layer1_,now); - return layer2_.postDispatch (act, now, ctx, layer1_); + Activity* act = layer2_.findWork (layer1_,now); + return ctx.post (now, act, ctx); }) .performStep([&]{ return scatteredDelay( loadControl_.outgoingCapacity (head,now)); diff --git a/tests/32scheduler.tests b/tests/32scheduler.tests index 3619dab13..34bb59d26 100644 --- a/tests/32scheduler.tests +++ b/tests/32scheduler.tests @@ -32,7 +32,7 @@ END -TEST "Scheduler Load-Control" SchedulerLoadControl_test < type display - CHECK (toString (nullptr) == "↯" ); // runtime exception, caught + CHECK (toString (&chatterer) == "↗hey Joe!"_expect); // pointer indicated + CHECK (toString (nullptr) == "↯"_expect); // runtime exception, caught - CHECK (toString (true) == "true" ); // special handling for bool - CHECK (toString (2+2 == 5) == "false" ); - CHECK (toString (12.34e55) == "1.234e+56" ); + CHECK (toString (true) == "true"_expect); // special handling for bool + CHECK (toString (2+2 == 5) == "false"_expect); + CHECK (toString (12.34e55) == "1.234e+56"_expect); CHECK (toString (short(12)) +toString (345L) +toString ("67") - +toString ('8') == "12345678" ); // these go through lexical_cast + +toString ('8') == "12345678"_expect); // these go through lexical_cast } @@ -151,7 +151,7 @@ namespace test { for (auto s : ss) res += s; - CHECK (res == "..2.113.114.115.116.117.118.119.1110.11"); + CHECK (res == "..2.113.114.115.116.117.118.119.1110.11"_expect); using VecS = vector; diff --git a/tests/library/meta/late-bind-instance-test.cpp b/tests/library/meta/late-bind-instance-test.cpp index 384684ae5..afbd496dd 100644 --- a/tests/library/meta/late-bind-instance-test.cpp +++ b/tests/library/meta/late-bind-instance-test.cpp @@ -173,16 +173,16 @@ namespace test { CHECK (dump(lateBindInstance (dummy, tuple<>{})) == "()"_expect); CHECK (dump(lateBindInstance (dummy, tuple{42})) == "(42)"_expect); CHECK (dump(lateBindInstance (dummy, tuple{1,2,3})) == "(1,2,3)"_expect); - CHECK (dump(lateBindInstance (dummy, tuple{marker,2,3})) == "(«long»,2,3)"_expect); - CHECK (dump(lateBindInstance (dummy, tuple{1,marker,3})) == "(1,«long»,3)"_expect); - CHECK (dump(lateBindInstance (dummy, tuple{1,2,marker})) == "(1,2,«long»)"_expect); - CHECK (dump(lateBindInstance (dummy, tuple{marker})) == "(«long»)"_expect); + CHECK (dump(lateBindInstance (dummy, tuple{marker,2,3})) == "(↗555,2,3)"_expect); + CHECK (dump(lateBindInstance (dummy, tuple{1,marker,3})) == "(1,↗555,3)"_expect); + CHECK (dump(lateBindInstance (dummy, tuple{1,2,marker})) == "(1,2,↗555)"_expect); + CHECK (dump(lateBindInstance (dummy, tuple{marker})) == "(↗555)"_expect); CHECK (dump(lateBindInstance (dummy, tuple{string{"1"} ,"2" ,marker ,tuple(2,3) , 5.5 - })) == "(1,2,«long»,«tuple»──(2,3),5.5)"_expect); + })) == "(1,2,↗555,«tuple»──(2,3),5.5)"_expect); } }; diff --git a/tests/vault/gear/activity-detector-test.cpp b/tests/vault/gear/activity-detector-test.cpp index 4d9a68faa..f74d42d35 100644 --- a/tests/vault/gear/activity-detector-test.cpp +++ b/tests/vault/gear/activity-detector-test.cpp @@ -232,8 +232,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, &a, ctx)); + CHECK (detector.verifyInvocation(CTX_POST).arg(t,&a,ctx)); CHECK (activity::PASS == ctx.tick(t)); CHECK (detector.verifyInvocation(CTX_TICK).arg(t)); diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp index cce360848..98398a896 100644 --- a/tests/vault/gear/activity-detector.hpp +++ b/tests/vault/gear/activity-detector.hpp @@ -544,7 +544,7 @@ namespace test { struct FakeExecutionCtx; - using SIG_post = activity::Proc(Time, Activity&, FakeExecutionCtx&); + using SIG_post = activity::Proc(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 5c05d92e3..9c7661746 100644 --- a/tests/vault/gear/scheduler-activity-test.cpp +++ b/tests/vault/gear/scheduler-activity-test.cpp @@ -418,14 +418,14 @@ namespace test { // insert instrumentation to trace activation detector.watchGate (post.next, "Gate"); - CHECK (activity::PASS == ActivityLang::dispatchChain (post, detector.executionCtx)); // start execution (case/seq == 0) + CHECK (activity::PASS == ActivityLang::dispatchChain (&post, detector.executionCtx)); // start execution (case/seq == 0) CHECK (detector.verifyInvocation("Gate") .arg("1.011 ⧐ Act(GATE") // ...first the Gate was activated .beforeInvocation("after-Gate").arg("1.011 ⧐ Act(TICK") // ...then activation passed out of Gate... .beforeInvocation("CTX-tick") .arg("1.011")); // ...and finally the TICK invoked the λ-tick detector.incrementSeq(); gate.data_.condition.incDependencies(); // Gate is blocked - CHECK (activity::PASS == ActivityLang::dispatchChain (post, detector.executionCtx)); // start execution (case/seq == 1) + CHECK (activity::PASS == ActivityLang::dispatchChain (&post, detector.executionCtx)); // start execution (case/seq == 1) CHECK (detector.verifyInvocation("Gate") .seq(1).arg("1.011 ⧐ Act(GATE") // ...the Gate was activated... .beforeInvocation("CTX-post").seq(1).arg("2.011","Act(GATE","≺test::CTX≻")); // ...but was found blocked and re-scheduled itself to 2.011 CHECK (detector.ensureNoInvocation("after-Gate").seq(1) // verify activation was not passed out behind Gate @@ -436,7 +436,7 @@ namespace test { detector.incrementSeq(); Activity notify{post.next}; // Notification via instrumented connection to the Gate - CHECK (activity::PASS == ActivityLang::dispatchChain (notify, detector.executionCtx)); // dispatch a notification (case/seq == 2) + 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 CHECK (detector.ensureNoInvocation("after-Gate").seq(2) // verify that activation was not passed out directly @@ -478,7 +478,7 @@ namespace test { // insert instrumentation to trace activation detector.watchGate (anchor.next, "theGate"); - CHECK (activity::PASS == ActivityLang::dispatchChain (anchor, detector.executionCtx)); + CHECK (activity::PASS == ActivityLang::dispatchChain (&anchor, detector.executionCtx)); CHECK (detector.verifyInvocation("theGate").arg("5.105 ⧐ Act(GATE") .beforeInvocation("after-theGate").arg("⧐ Act(WORKSTART") @@ -535,7 +535,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, Activity* postedAct, auto& ctx) { if (when == ctx.getSchedTime()) // only for POST to run „right now“ return ActivityLang::dispatchChain (postedAct, ctx); @@ -544,7 +544,7 @@ namespace test { }); ///// Case-1 : send a notification from prerequisite, but prior to activating primary-chain - CHECK (activity::PASS == ActivityLang::dispatchChain (trigger, detector.executionCtx)); + CHECK (activity::PASS == ActivityLang::dispatchChain (&trigger, detector.executionCtx)); 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 @@ -552,7 +552,7 @@ namespace test { ///// Case-2 : now activate the primary-chain detector.incrementSeq(); - CHECK (activity::PASS == ActivityLang::dispatchChain (anchor, detector.executionCtx)); + 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 .arg("<1, until 0:00:10.000") // Note: at this point, the Gate-latch expects 1 notifications @@ -567,7 +567,7 @@ namespace test { .afterInvocation("CTX-done").seq(1)); detector.incrementSeq(); - CHECK (activity::PASS == ActivityLang::dispatchChain (trigger, detector.executionCtx)); // and any further external trigger is likewise blocked: + CHECK (activity::PASS == ActivityLang::dispatchChain (&trigger, detector.executionCtx)); // and any further external trigger is likewise blocked: CHECK (detector.verifyInvocation("trigger") .seq(2).arg("5.555 --notify-↯> 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 @@ -620,7 +620,7 @@ namespace test { ///// Case-1 : trigger off the async IO job - CHECK (activity::PASS == ActivityLang::dispatchChain (anchor, detector.executionCtx)); + CHECK (activity::PASS == ActivityLang::dispatchChain (&anchor, detector.executionCtx)); CHECK (detector.verifyInvocation("CTX-work").seq(0).arg("5.555", "") // activation of WORKSTART .beforeInvocation("loadJob") .seq(0).arg("7.007", 12345)); // activation of JobFunctor CHECK (detector.ensureNoInvocation("CTX-done").seq(0) // IO operation just runs, no further activity yet @@ -629,7 +629,7 @@ namespace test { ///// Case-2 : activate the rest of the chain after IO is done detector.incrementSeq(); - CHECK (activity::PASS == ActivityLang::dispatchChain (notify, detector.executionCtx)); + 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... } @@ -669,7 +669,7 @@ namespace test { // insert test-instrumentation detector.insertActivationTap(anchor.next); - CHECK (activity::PASS == ActivityLang::dispatchChain (anchor, detector.executionCtx)); + CHECK (activity::PASS == ActivityLang::dispatchChain (&anchor, detector.executionCtx)); CHECK (detector.verifyInvocation("tap-INVOKE").arg("5.555 ⧐ Act(INVOKE") .beforeInvocation("metaJob") .arg("7.007",12345)); diff --git a/tests/vault/gear/work-force-test.cpp b/tests/vault/gear/work-force-test.cpp index aa6f6231f..bd4fe48ae 100644 --- a/tests/vault/gear/work-force-test.cpp +++ b/tests/vault/gear/work-force-test.cpp @@ -396,7 +396,7 @@ namespace test { CHECK (fullCnt == wof.size()); wof.activate (2.0); - sleep_for(5ms); + sleep_for(10ms); CHECK (2*fullCnt == uniqueCnt); CHECK (2*fullCnt == wof.size()); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 092e06f64..2e4cd5341 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -81728,8 +81728,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -81751,6 +81752,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + @@ -82142,6 +82146,64 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ und ich habe im Scheduler nur noch λ-post als universellen Eingang +

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
    +
  • + weil sie die Ausgabe aufbläht +
  • +
  • + weil sie i.d.R. zufällig ist +
  • +
  • + es gibt ja noch util::showPtr() +
  • +
  • + und in format-cout hab ich's drinnen gelassen (da möchte man es normalerweise schon sehen) +
  • +
+ +
+ +
+
+