diff --git a/src/vault/gear/activity.hpp b/src/vault/gear/activity.hpp index 53a6e448e..89910973a 100644 --- a/src/vault/gear/activity.hpp +++ b/src/vault/gear/activity.hpp @@ -137,6 +137,11 @@ namespace gear { , Time now , void* executionCtx) =0; + /** Callback when dispatching a NOTIFY-Activity to \a thisHook */ + virtual Proc notify ( Activity& thisHook + , Time now + , void* executionCtx) =0; + virtual std::string diagnostic() const { @@ -366,6 +371,12 @@ namespace gear { template activity::Proc activate (Time now, EXE& executionCtx); + template + activity::Proc dispatch (Time now, EXE& executionCtx); + + template + activity::Proc notify (Time now, EXE& executionCtx); + private: void @@ -418,6 +429,7 @@ namespace gear { return activity::PASS; } + template activity::Proc signalStart (Time now, EXE& executionCtx) @@ -438,8 +450,16 @@ namespace gear { activity::Proc dispatchNotify (Time now, EXE& executionCtx) { - UNIMPLEMENTED ("double-dispatch a notification trigger"); - return executionCtx.post (now, *next, executionCtx); + return executionCtx.post (now, *this, executionCtx); + } + + template + activity::Proc + notifyTarget (Time now, EXE& executionCtx) + { + REQUIRE (NOTIFY == verb_); + REQUIRE (data_.notification.target); + return data_.notification.target->notify (now, executionCtx); } template @@ -447,14 +467,36 @@ namespace gear { checkGate (Time now, EXE& executionCtx) { REQUIRE (GATE == verb_); - if (now > data_.condition.dead) // beyond deadline + if (data_.condition.isDead(now)) // beyond deadline return activity::SKIP; - if (0 < data_.condition.rest) // prerequisite count not(yet) fulfilled -> spin (=re-invoke later) - return executionCtx.post (executionCtx.spin(now), *this, executionCtx); + if (data_.condition.isHold()) // prerequisite count not(yet) fulfilled -> spin (=re-invoke later) + return dispatchSelfDelayed (now, executionCtx); else return activity::PASS; } + template + activity::Proc + receiveGateNotification (Time now, EXE& executionCtx) + { + REQUIRE (GATE == verb_); + if (data_.condition.rest > 0) + --data_.condition.rest; + // maybe the Gate has been opened by this notification? + if (data_.condition.isFree(now)) // yes => activate gated chain + return postChain (now, executionCtx); + else + return activity::PASS; + } + + template + activity::Proc + dispatchSelfDelayed (Time now, EXE& executionCtx) + { + REQUIRE (next); + return executionCtx.post (executionCtx.spin(now), *this, executionCtx); + } + template activity::Proc postChain (Time now, EXE& executionCtx) @@ -471,6 +513,14 @@ namespace gear { : activity::PASS; } + template + activity::Proc + notifyHook (Time now, EXE& executionCtx) + { + return data_.callback.hook? data_.callback.hook->notify (*this, now, &executionCtx) + : activity::PASS; + } + template activity::Proc doTick (Time now, EXE& executionCtx) @@ -512,6 +562,64 @@ namespace gear { } } + + /** + * Entrance point for an activation, which has been dispatched indirectly + * through the dispatch and/or priority queue; typically this is achieved + * by invoking the `post`-λ on the \a executionCtx, or by _activating_ + * a `POST`-Activity. Control flow passing here has acquired the `GroomingToken` + * and can thus assume single threaded execution until `WORKSTART`. + * @note special twist for the `NOTIFY`-Activity: it is not _activated_ + * itself, rather the #notify operation is invoked on its target(`next`); + * this is necessary since a notification passes control-flow outside + * the regular linear `next`-chain; when a `NOTIFY` is _activated,_ + * it will `post()` itself to acquire the `GroomingToken` and then + * invoke this dispatch() function to pass the notification + */ + template + activity::Proc + Activity::dispatch (Time now, EXE& executionCtx) + { + activity::_verify_usable_as_ExecutionContext(); + + switch (verb_) { + case NOTIFY: + return notifyTarget (now, executionCtx); + case POST: + case FEED: // signal just to proceed with next... + return activity::PASS; + default: + return activate (now, executionCtx); + } + } + + + /** + * Special operation to receive a message or trigger from some other Activity. + * Notably this is used to implement _gating_ to wait for prerequisites; when + * a notification is passed to a `GATE`-Activity, the embedded counter is + * decremented; after all prerequisites are „checked off“ this way, the + * Activity-chain behind the Gate is activated. + */ + template + activity::Proc + Activity::notify (Time now, EXE& executionCtx) + { + activity::_verify_usable_as_ExecutionContext(); + + switch (verb_) { + case GATE: + return receiveGateNotification (now, executionCtx); + case HOOK: + return notifyHook (now, executionCtx); + case POST: + case FEED: + return postChain (now, executionCtx); + default: + return dispatchSelfDelayed (now, executionCtx); + } // Fallback: self-re-dispatch for async execution + } + }} // namespace vault::gear diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp index 8eb85c1c3..8acf46d54 100644 --- a/tests/vault/gear/activity-detector.hpp +++ b/tests/vault/gear/activity-detector.hpp @@ -381,6 +381,26 @@ namespace test { } } + activity::Proc + notify ( Activity& thisHook + , Time now + , void* executionCtx) override + { + REQUIRE (Activity::HOOK == thisHook.verb_); + if (data_.callback.arg == 0) + {// no adapted target; just record this notification + log_(util::toString(now) + " --notify-↯• "); + return activity::PASS; + } + else + {// forward notification-dispatch to the adapted target Activity + Activity& target = *reinterpret_cast (data_.callback.arg); + auto ctx = *static_cast (executionCtx); + log_(util::toString(now) + " --notify-↯> " + util::toString (target)); + return target.activate (now, ctx);/////////////////////////////////////////////////////OOO + } + } + std::string diagnostic() const override { diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 1836e0d16..ea5c9ddf0 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -78469,7 +78469,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -78532,13 +78532,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + - + + @@ -78612,13 +78618,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + - + + @@ -78628,28 +78638,52 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + + + + + + - + - - + + + + + + + + + + + - - - + + + + + @@ -79095,7 +79129,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79104,22 +79138,40 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + - - + + + + + +

+ Unterscheidung in Aktivierung und Dispatch +

+ +
+ +
+ +

- d.h. wird stehts von einem Worker ausgeführt, der aktuell das GroomingToken hält + d.h. wird stehts von einem Worker ausgeführt, der aktuell das GroomingToken hält ⟹ Race zwischen Inkrementieren und Gate-Check ausgeschlossen

+ +
@@ -79495,8 +79547,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -80201,9 +80253,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + @@ -82370,7 +82422,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -82505,8 +82557,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + +

+ (Deadlines sind danach und wirken daher exclusiv) +

+ +
+ +
@@ -83261,8 +83324,30 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + + + + + + + + + + + + + + + +