From 72258c06bd399bebfa4a54266830940cc972ce27 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 4 Nov 2023 03:48:49 +0100 Subject: [PATCH] Scheduler: reconciled into clearer design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The problem with passing the deadline was just a blatant symptom that something with the overall design was not quite right, leading to mix-up of interfaces and implementation functions, and more and more detail parameters spreading throughout the call chains. The turning point was to realise the two conceptual levels crossing and interconnected within the »Scheduler-Service« - the Activity-Language describes the patterns of processing - the Scheduler components handle time-bound events So by turning the (previously private) queue entry into an ActivationEvent, the design could be balanced. This record becomes the common agens within the Scheduler, and builds upon / layers on top of the common agens of the Language, which is the Activity record. --- src/vault/gear/scheduler-commutator.hpp | 19 +-- src/vault/gear/scheduler-invocation.hpp | 64 +++++---- src/vault/gear/scheduler.hpp | 88 +++++++----- .../vault/gear/scheduler-commutator-test.cpp | 42 +++--- .../vault/gear/scheduler-invocation-test.cpp | 38 ++--- tests/vault/gear/scheduler-service-test.cpp | 13 +- wiki/thinkPad.ichthyo.mm | 132 ++++++++++++++---- 7 files changed, 244 insertions(+), 152 deletions(-) diff --git a/src/vault/gear/scheduler-commutator.hpp b/src/vault/gear/scheduler-commutator.hpp index bab2f7664..9a70cb836 100644 --- a/src/vault/gear/scheduler-commutator.hpp +++ b/src/vault/gear/scheduler-commutator.hpp @@ -189,7 +189,7 @@ namespace gear { if (layer1.isDue (now) and not layer1.isOutOfTime(now)) return layer1.pullHead(); } - return ActivationEvent::nil(); + return ActivationEvent(); } @@ -216,26 +216,21 @@ namespace gear { */ template activity::Proc - postDispatch (Activity* chain, Time when + postDispatch (ActivationEvent event ,EXE& executionCtx ,SchedulerInvocation& layer1 - //////////////////////////////////////////////////////////////////////////////////////////////OOO API / Design problem with "context" and significance-Params - , Time dead =Time::NEVER //////////////////////////////////TODO booom!! - , ManifestationID manID =ManifestationID() - , bool compulsory = false - //////////////////////////////////////////////////////////////////////////////////////////////OOO API / Design problem with "context" and significance-Params ) { - if (!chain) return activity::SKIP; + if (!event) return activity::SKIP; Time now = executionCtx.getSchedTime(); - if (decideDispatchNow (when, now)) - return ActivityLang::dispatchChain (chain, executionCtx); + if (decideDispatchNow (event.startTime(), now)) + return ActivityLang::dispatchChain (event, executionCtx); else if (holdsGroomingToken (thisThread())) - layer1.feedPrioritisation (*chain, when, dead, manID, compulsory); + layer1.feedPrioritisation (move (event)); else - layer1.instruct (*chain, when, dead, manID, compulsory); + layer1.instruct (move (event)); return activity::PASS; } }; diff --git a/src/vault/gear/scheduler-invocation.hpp b/src/vault/gear/scheduler-invocation.hpp index 9c5f8701c..636a07635 100644 --- a/src/vault/gear/scheduler-invocation.hpp +++ b/src/vault/gear/scheduler-invocation.hpp @@ -90,14 +90,32 @@ namespace gear { */ struct ActivationEvent { - Activity* activity{nullptr}; - int64_t starting{0}; - int64_t deadline{0}; + Activity* activity; + int64_t starting; + int64_t deadline; uint32_t manifestation :32; - char :0; bool isCompulsory :1; - ///////////////////////////////////////////////////////////////////////////TICKET #1245 : use direct bit-field initialiser in C++20 + + ActivationEvent() + : activity{nullptr} + , starting{_raw(Time::ANYTIME)} + , deadline{_raw(Time::NEVER)} + , manifestation{0} + , isCompulsory{false} + { } + + ActivationEvent(Activity& act, Time when + , Time dead =Time::NEVER + , ManifestationID manID =ManifestationID() + , bool compulsory =false) + : activity{&act} + , starting{_raw(when)} + , deadline{_raw(dead)} + , manifestation{manID} + , isCompulsory{compulsory} + { } + // default copy operations acceptable /** @internal ordering function for time based scheduling * @note reversed order as required by std::priority_queue @@ -112,7 +130,7 @@ namespace gear { operator bool() const { return bool{activity}; } operator Activity*() const { return activity; } - static ActivationEvent nil() { return {nullptr, _raw(Time::ANYTIME), _raw(Time::NEVER), 0, false}; } + Time startTime() const { return Time{TimeValue{starting}};} }; @@ -157,34 +175,27 @@ namespace gear { /** - * Accept an Activity for time-bound execution + * Accept an ActivationEvent with an Activity for time-bound execution */ void - instruct (Activity& activity, Time when - , Time dead =Time::NEVER - , ManifestationID manID =ManifestationID() - , bool compulsory =false) + instruct (ActivationEvent actEvent) { - bool success = instruct_.push (ActivationEvent{&activity - , waterLevel(when) - , waterLevel(dead) - , uint32_t(manID) - , compulsory}); + bool success = instruct_.push (move (actEvent)); if (not success) throw error::Fatal{"Scheduler entrance: memory allocation failed"}; } /** - * Pick up all new Activities from the entrance queue + * Pick up all new events from the entrance queue * and enqueue them to be retrieved ordered by start time. */ void feedPrioritisation() { - ActivationEvent actOrder; - while (instruct_.pop (actOrder)) - priority_.push (move (actOrder)); + ActivationEvent actEvent; + while (instruct_.pop (actEvent)) + priority_.push (move (actEvent)); } @@ -194,16 +205,9 @@ namespace gear { * @remark Layer-2 uses this shortcut when in »grooming mode«. */ void - feedPrioritisation (Activity& activity, Time when - , Time dead =Time::NEVER - , ManifestationID manID =ManifestationID() - , bool compulsory =false) + feedPrioritisation (ActivationEvent actEvent) { - priority_.push (ActivationEvent{&activity - , waterLevel(when) - , waterLevel(dead) - , uint32_t(manID) - , compulsory}); + priority_.push (move (actEvent)); } @@ -214,7 +218,7 @@ namespace gear { ActivationEvent peekHead() { - return priority_.empty()? ActivationEvent::nil() + return priority_.empty()? ActivationEvent() : priority_.top(); } diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index 6b3901eca..ba0b1ea08 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -254,11 +254,8 @@ namespace gear { UNIMPLEMENTED("wrap the ActivityTerm"); } - //////////////////////////////////////////////////////////////////////////////////////////////OOO the role of this function remains unclear; currently used from »Tick« - activity::Proc postChain (Activity*, Time start - , Time dead =Time::ANYTIME - , ManifestationID manID =ManifestationID() - , bool isCompulsory = false); + + void postChain (ActivationEvent); //////////////////////////////////////OOO could be private? /** @@ -317,9 +314,16 @@ namespace gear { return setup; } + /** access high-resolution-clock, rounded to µ-Ticks */ + Time + getSchedTime() + { + return RealClock::now(); + } + /** @internal expose a binding for Activity execution */ class ExecutionCtx; - + friend class ExecutionCtx; /** open private backdoor for tests */ friend class test::SchedulerService_test; @@ -337,26 +341,37 @@ namespace gear { * is in fact supplied by the implementation internals of the scheduler itself. */ class Scheduler::ExecutionCtx - : private Scheduler + : util::NonCopyable { + Scheduler& scheduler_; public: - static ExecutionCtx& - from (Scheduler& self) - { - return static_cast (self); - } + + ActivationEvent rootEvent; + + ExecutionCtx(Scheduler& self, ActivationEvent toDispatch) + : scheduler_{self} + , rootEvent{toDispatch} + { } + /* ==== Implementation of the Concept ExecutionCtx ==== */ /** * λ-post: enqueue for time-bound execution, possibly dispatch immediately. - * This is the »main entrance« to get some Activity scheduled. - * @remark the \a ctx argument is redundant (helpful for test/mocking) + * @remark This function represents and _abstracted entrance to scheduling_ + * for the ActivityLang and is relevant for recursive forwarding + * of activations and notifications. The concrete implementation + * needs some further contextual information, which is passed + * down here as a nested sub-context. */ activity::Proc post (Time when, Activity* chain, ExecutionCtx& ctx) { - return layer2_.postDispatch (chain, when, ctx, layer1_); + ActivationEvent chainEvent = ctx.rootEvent; + chainEvent.activity = chain; + chainEvent.starting = _raw(when); + ExecutionCtx subCtx{scheduler_, chainEvent}; + return scheduler_.layer2_.postDispatch (chainEvent, subCtx, scheduler_.layer1_); } void @@ -374,7 +389,7 @@ namespace gear { activity::Proc tick (Time now) { - handleDutyCycle (now); + scheduler_.handleDutyCycle (now); return activity::PASS; } @@ -388,13 +403,28 @@ namespace gear { Time getSchedTime() { - return RealClock::now(); + return scheduler_.getSchedTime(); } }; + /** + * Enqueue for time-bound execution, possibly dispatch immediately. + * This is the »main entrance« to get some Activity scheduled. + * @param actEvent the Activity, start time and deadline + * and optionally further context information + */ + inline void + Scheduler::postChain (ActivationEvent actEvent) + { + ExecutionCtx ctx{*this, actEvent}; + layer2_.postDispatch (actEvent, ctx, layer1_); + } + + + /** * @remarks this function is invoked from within the worker thread(s) and will * - decide if and how the capacity of this worker shall be used right now @@ -416,22 +446,22 @@ namespace gear { Scheduler::getWork() { auto self = std::this_thread::get_id(); - auto& ctx = ExecutionCtx::from (*this); try { auto res = WorkerInstruction{} .performStep([&]{ - Time now = ctx.getSchedTime(); + Time now = getSchedTime(); Time head = layer1_.headTime(); return scatteredDelay(now, loadControl_.markIncomingCapacity (head,now)); }) .performStep([&]{ - Time now = ctx.getSchedTime(); - Activity* act = layer2_.findWork (layer1_,now); - return ctx.post (now, act, ctx); + Time now = getSchedTime(); + auto toDispatch = layer2_.findWork (layer1_,now); + ExecutionCtx ctx{*this, toDispatch}; + return layer2_.postDispatch (toDispatch, ctx, layer1_); }) .performStep([&]{ - Time now = ctx.getSchedTime(); + Time now = getSchedTime(); Time head = layer1_.headTime(); return scatteredDelay(now, loadControl_.markOutgoingCapacity (head,now)); @@ -506,16 +536,6 @@ namespace gear { } - //////////////////////////////////////////////////////////////////////////////////////////////OOO the role of this function remains unclear; currently used from »Tick« - inline activity::Proc - Scheduler::postChain (Activity* chain, Time start, Time dead - ,ManifestationID manID, bool isCompulsory) - { - auto& ctx = ExecutionCtx::from (*this); - return layer2_.postDispatch (chain, start, ctx, layer1_ - //////////////////////////////////////////////////////////////////////////////////////////////OOO API / Design problem with "context" and significance-Params - ,dead,manID,isCompulsory); - } /** @@ -555,7 +575,7 @@ namespace gear { Time nextTick = now + DUTY_CYCLE_PERIOD; Time deadline = nextTick + DUTY_CYCLE_TOLERANCE; Activity& tickActivity = activityLang_.createTick (deadline); - postChain (&tickActivity, nextTick, deadline, ManifestationID(), true); + postChain (ActivationEvent{tickActivity, nextTick, deadline, ManifestationID(), true}); } } diff --git a/tests/vault/gear/scheduler-commutator-test.cpp b/tests/vault/gear/scheduler-commutator-test.cpp index 52d950ae1..160056691 100644 --- a/tests/vault/gear/scheduler-commutator-test.cpp +++ b/tests/vault/gear/scheduler-commutator-test.cpp @@ -119,9 +119,9 @@ namespace test { Time now = detector.executionCtx.getSchedTime(); // prepare scenario: some activity is enqueued - queue.instruct (activity, when); + queue.instruct ({activity, when}); - sched.postDispatch (sched.findWork(queue,now), now, detector.executionCtx,queue); + sched.postDispatch (sched.findWork(queue,now), detector.executionCtx,queue); CHECK (detector.verifyInvocation("CTX-tick").arg(now)); CHECK (queue.empty()); @@ -311,15 +311,15 @@ namespace test { Activity a2{2u,2u}; Activity a3{3u,3u}; - queue.instruct (a3, t3); // activity scheduled into the future + queue.instruct ({a3, t3}); // activity scheduled into the future CHECK (not sched.findWork (queue, now)); // ... not found with time `now` CHECK (t3 == queue.headTime()); - queue.instruct (a1, t1); + queue.instruct ({a1, t1}); CHECK (isSameObject (a1, *sched.findWork(queue, now))); // but past activity is found CHECK (not sched.findWork (queue, now)); // activity was retrieved - queue.instruct (a2, t2); + queue.instruct ({a2, t2}); CHECK (isSameObject (a2, *sched.findWork(queue, now))); // activity scheduled for `now` is found CHECK (not sched.findWork (queue, now)); // nothing more found for `now` CHECK (t3 == queue.headTime()); @@ -329,15 +329,15 @@ namespace test { CHECK (not sched.findWork (queue, t3)); CHECK ( queue.empty()); // Everything retrieved and queue really empty - queue.instruct (a2, t2); - queue.instruct (a1, t1); + queue.instruct ({a2, t2}); + queue.instruct ({a1, t1}); CHECK (isSameObject (a1, *sched.findWork(queue, now))); // the earlier activity is found first CHECK (t2 == queue.headTime()); CHECK (isSameObject (a2, *sched.findWork(queue, now))); CHECK (not sched.findWork (queue, now)); CHECK ( queue.empty()); - queue.instruct (a2, t2); // prepare activity which /would/ be found... + queue.instruct ({a2, t2}); // prepare activity which /would/ be found... blockGroomingToken(sched); // but prevent this thread from acquiring the GroomingToken CHECK (not sched.findWork (queue, now)); // thus search aborts immediately CHECK (not queue.empty()); @@ -369,10 +369,10 @@ namespace test { Time t3{30,0}; Activity a3{3u,3u}; Time t4{40,0}; Activity a4{4u,4u}; - queue.instruct (a1, t1, t4, ManifestationID{5}); - queue.instruct (a2, t2, t2); - queue.instruct (a3, t3, t3, ManifestationID{23}, true); - queue.instruct (a4, t4, t4); + queue.instruct ({a1, t1, t4, ManifestationID{5}}); + queue.instruct ({a2, t2, t2}); + queue.instruct ({a3, t3, t3, ManifestationID{23}, true}); + queue.instruct ({a4, t4, t4}); queue.activate(ManifestationID{5}); queue.activate(ManifestationID{23}); @@ -451,19 +451,19 @@ namespace test { auto myself = std::this_thread::get_id(); CHECK (not sched.holdsGroomingToken (myself)); - // no effect when no Activity given - CHECK (activity::SKIP == sched.postDispatch (nullptr, now, detector.executionCtx, queue)); + // no effect when empty / no Activity given + CHECK (activity::SKIP == sched.postDispatch (ActivationEvent(), detector.executionCtx, queue)); CHECK (not sched.holdsGroomingToken (myself)); // Activity immediately dispatched when on time and GroomingToken can be acquired - CHECK (activity::PASS == sched.postDispatch (&activity, past, detector.executionCtx, queue)); + CHECK (activity::PASS == sched.postDispatch (ActivationEvent{activity, past}, detector.executionCtx, queue)); CHECK (detector.verifyInvocation("testActivity").timeArg(now)); // was invoked immediately CHECK ( sched.holdsGroomingToken (myself)); CHECK ( queue.empty()); detector.incrementSeq(); // Seq-point-1 in the detector log // future Activity is enqueued by short-circuit directly into the PriorityQueue if possible - CHECK (activity::PASS == sched.postDispatch (&activity, future, detector.executionCtx, queue)); + CHECK (activity::PASS == sched.postDispatch (ActivationEvent{activity, future}, detector.executionCtx, queue)); CHECK ( sched.holdsGroomingToken (myself)); CHECK (not queue.empty()); CHECK (isSameObject (activity, *queue.peekHead())); // appears at Head, implying it's in Priority-Queue @@ -474,14 +474,14 @@ namespace test { CHECK (queue.empty()); // ...but GroomingToken is not acquired explicitly; Activity is just placed into the Instruct-Queue - CHECK (activity::PASS == sched.postDispatch (&activity, future, detector.executionCtx, queue)); + CHECK (activity::PASS == sched.postDispatch (ActivationEvent{activity, future}, detector.executionCtx, queue)); CHECK (not sched.holdsGroomingToken (myself)); CHECK (not queue.peekHead()); // not appearing at Head this time, CHECK (not queue.empty()); // rather waiting in the Instruct-Queue blockGroomingToken(sched); - CHECK (activity::PASS == sched.postDispatch (&activity, now, detector.executionCtx, queue)); + CHECK (activity::PASS == sched.postDispatch (ActivationEvent{activity, now}, detector.executionCtx, queue)); CHECK (not sched.holdsGroomingToken (myself)); CHECK (not queue.peekHead()); // was enqueued, not executed @@ -562,7 +562,7 @@ namespace test { // ·=================================================================== actual test sequence // Add the Activity-Term to be scheduled for planned start-Time - sched.postDispatch (&anchor, start, detector.executionCtx, queue); + sched.postDispatch (ActivationEvent{anchor, start}, detector.executionCtx, queue); CHECK (detector.ensureNoInvocation("testJob")); CHECK (not sched.holdsGroomingToken (myself)); CHECK (not queue.empty()); @@ -572,11 +572,11 @@ namespace test { detector.incrementSeq(); // Assuming a worker runs "later" and retrieves work... - Activity* act = sched.findWork(queue,now); + ActivationEvent act = sched.findWork(queue,now); CHECK (sched.holdsGroomingToken (myself)); // acquired the GroomingToken CHECK (isSameObject(*act, anchor)); // "found" the rigged Activity as next piece of work - sched.postDispatch (act, now, detector.executionCtx, queue); + sched.postDispatch (act, detector.executionCtx, queue); CHECK (queue.empty()); CHECK (not sched.holdsGroomingToken (myself)); // the λ-work was invoked and dropped the GroomingToken diff --git a/tests/vault/gear/scheduler-invocation-test.cpp b/tests/vault/gear/scheduler-invocation-test.cpp index 8dfbec9c7..725d3b340 100644 --- a/tests/vault/gear/scheduler-invocation-test.cpp +++ b/tests/vault/gear/scheduler-invocation-test.cpp @@ -72,7 +72,7 @@ namespace test { CHECK (not sched.peekHead()); - sched.instruct (activity, when, dead); + sched.instruct ({activity, when, dead}); sched.feedPrioritisation(); CHECK (sched.peekHead()); @@ -93,18 +93,18 @@ namespace test { SchedulerInvocation sched; Activity one{1u,1u}; Activity two{2u,2u}; - Activity ree{3u,3u}; + Activity wee{3u,3u}; Time t{5,5}; - sched.instruct (one, t); - sched.instruct (two, t); - sched.instruct (ree, t); + sched.instruct ({one, t}); + sched.instruct ({two, t}); + sched.instruct ({wee, t}); CHECK (not sched.peekHead()); sched.feedPrioritisation(); CHECK (isSameObject (*sched.pullHead(), one)); CHECK (isSameObject (*sched.pullHead(), two)); - CHECK (isSameObject (*sched.pullHead(), ree)); + CHECK (isSameObject (*sched.pullHead(), wee)); CHECK (not sched.peekHead()); CHECK (sched.empty()); } @@ -125,13 +125,13 @@ namespace test { Activity a3{3u,3u}; Activity a4{4u,4u}; - sched.instruct (a2, Time{2,0}); - sched.instruct (a4, Time{4,0}); + sched.instruct ({a2, Time{2,0}}); + sched.instruct ({a4, Time{4,0}}); sched.feedPrioritisation(); CHECK (isSameObject (*sched.peekHead(), a2)); - sched.instruct (a3, Time{3,0}); - sched.instruct (a1, Time{1,0}); + sched.instruct ({a3, Time{3,0}}); + sched.instruct ({a1, Time{1,0}}); CHECK (isSameObject (*sched.peekHead(), a2)); sched.feedPrioritisation(); @@ -155,10 +155,10 @@ namespace test { Activity a3{3u,3u}; Activity a4{4u,4u}; - sched.feedPrioritisation (a1, Time{0,5}); - sched.feedPrioritisation (a2, Time{0,5}); - sched.feedPrioritisation (a3, Time{0,5}); - sched.feedPrioritisation (a4, Time{0,4}); + sched.feedPrioritisation ({a1, Time{0,5}}); + sched.feedPrioritisation ({a2, Time{0,5}}); + sched.feedPrioritisation ({a3, Time{0,5}}); + sched.feedPrioritisation ({a4, Time{0,4}}); CHECK (isSameObject (*sched.pullHead(), a4)); CHECK (isSameObject (*sched.pullHead(), a3)); CHECK (isSameObject (*sched.pullHead(), a1)); @@ -177,7 +177,7 @@ namespace test { SchedulerInvocation sched; Activity a1{1u,1u}; - sched.feedPrioritisation (a1, Time{0,5}); + sched.feedPrioritisation ({a1, Time{0,5}}); CHECK (isSameObject (*sched.peekHead(), a1)); CHECK ( sched.isDue (Time{0,10})); CHECK ( sched.isDue (Time{0,5})); @@ -207,7 +207,7 @@ namespace test { SchedulerInvocation sched; Activity act; - sched.feedPrioritisation (act, Time{2,0}, Time{3,0}); + sched.feedPrioritisation ({act, Time{2,0}, Time{3,0}}); CHECK (Time(2,0) == sched.headTime()); CHECK ( sched.isDue (Time{2,0})); CHECK (not sched.isMissed (Time{2,0})); @@ -218,7 +218,7 @@ namespace test { CHECK (not sched.isOutdated (Time{3,0})); CHECK ( sched.isOutdated (Time{4,0})); - sched.feedPrioritisation (act, Time{1,0}, Time{3,0}, ManifestationID{5}); + sched.feedPrioritisation ({act, Time{1,0}, Time{3,0}, ManifestationID{5}}); CHECK (Time(1,0) == sched.headTime()); CHECK ( sched.isOutdated (Time{1,0})); CHECK (not sched.isMissed (Time{1,0})); @@ -243,8 +243,8 @@ namespace test { CHECK (not sched.isMissed (Time{1,0})); CHECK ( sched.isDue (Time{1,0})); - sched.feedPrioritisation (act, Time{0,0}, Time{2,0}, ManifestationID{23}, true); - CHECK (Time(0,0) == sched.headTime()); // ^^^^ marked as compulsory + sched.feedPrioritisation ({act, Time{0,0}, Time{2,0}, ManifestationID{23}, true}); + CHECK (Time(0,0) == sched.headTime()); // ^^^^ marked as compulsory CHECK (not sched.isMissed (Time{1,0})); CHECK (not sched.isOutdated (Time{1,0})); CHECK (not sched.isOutOfTime(Time{2,0})); // still OK /at/ deadline diff --git a/tests/vault/gear/scheduler-service-test.cpp b/tests/vault/gear/scheduler-service-test.cpp index e0ddde342..e16cb1589 100644 --- a/tests/vault/gear/scheduler-service-test.cpp +++ b/tests/vault/gear/scheduler-service-test.cpp @@ -113,9 +113,7 @@ namespace test { CHECK (isnil (scheduler)); Activity dummy{Activity::FEED}; - auto postIt = [&] { auto& schedCtx = Scheduler::ExecutionCtx::from(scheduler); - schedCtx.post (RealClock::now()+t200us, &dummy, schedCtx); - }; + auto postIt = [&] { scheduler.postChain (ActivationEvent{dummy, RealClock::now()+t200us}); }; scheduler.ignite(); CHECK (isnil (scheduler)); // no start without any post() @@ -167,9 +165,8 @@ namespace test { auto createLoad = [&](Offset start, uint cnt) { // use internal API (this test is declared as friend) - auto& schedCtx = Scheduler::ExecutionCtx::from(scheduler); - for (uint i=0; i + + + + + +

+ hier spielt eine Deadline nur die Rolle einer zu prüfenden Bedingung +

+ +
+
+ + + + +

+ diese enthalten eine Activity, aber auch ein Zeitfenster sowie weitere Kontext-Parameter (ManifestationID, Flags wie isCompulsory) +

+ +
+
+ + + + + + @@ -82678,9 +82705,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + @@ -82703,37 +82730,62 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...denn sonst würde in jedem Fall der Zugriff komplizierter; zwar handelt es sich um ein Implementierungs-Detail, aber im ScheudlerService selber darf es durchaus Verkopplung auf die Implementierung geben

- -
+
- - - +

Wir sind hier in einem »inline«-Universum, und die wenigen Verwendungen machen entweder direkt die bool-conversion oder speichern die Datenwerte irgendwo auf den Stack. Da is nix „schwergewichtig“

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

+ neuer Haupt-Eingang: postChain(ActivationEvent) +

+ +
+ + + + + +
@@ -84349,13 +84401,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - - +

das heißt, das ist latent ein Design-Problem — welches ich derzeit nicht lösen kann, da mir der Gesamtüberblick noch fehlt, und ich genau deshalb dieses offene Design gewählt habe @@ -84366,9 +84416,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

kann es auch gar nicht, denn die Activity selber kennt i.A. keine Deadline @@ -84376,7 +84424,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -84384,7 +84433,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -84392,11 +84441,40 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + +

+ Umbau: Dispatch-mit-Kontext +

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

+ der Queue-Entry wurde zum ActivationEvent als zentrale Austausch-Einheit +

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