Scheduler: reconciled into clearer design

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.
This commit is contained in:
Fischlurch 2023-11-04 03:48:49 +01:00
parent 62a1310566
commit 72258c06bd
7 changed files with 244 additions and 152 deletions

View file

@ -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<class EXE>
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;
}
};

View file

@ -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();
}

View file

@ -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<ExecutionCtx&> (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});
}
}

View file

@ -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

View file

@ -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

View file

@ -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<cnt; ++i) // flood the queue
schedCtx.post (anchor + start + TimeValue{i}, &dummy, schedCtx);
for (uint i=0; i<cnt; ++i) // flood the queue
scheduler.postChain (ActivationEvent{dummy, anchor + start + TimeValue{i}});
};
@ -314,10 +311,8 @@ namespace test {
auto post = [&](Time start)
{ // this test class is declared friend to get a backdoor to Scheduler internals...
auto& schedCtx = Scheduler::ExecutionCtx::from(scheduler);
scheduler.layer2_.acquireGoomingToken();
schedCtx.post (start, &probe, schedCtx);
scheduler.postChain(ActivationEvent{probe, start});
};
auto pullWork = [&] {

View file

@ -80630,6 +80630,33 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="forward"/>
</node>
<node CREATED="1699068752568" ID="ID_491166338" LINK="#ID_794200787" MODIFIED="1699068955141" TEXT="diese Ebenen haben sich letztlich im SchedulerService sortiert">
<node CREATED="1699068776516" ID="ID_171673689" MODIFIED="1699068810554" TEXT="die Activity-Language ist f&#xfc;r die reinen Activities zust&#xe4;ndig">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
hier spielt eine Deadline nur die Rolle einer zu pr&#252;fenden Bedingung
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1699068811596" ID="ID_1052903633" MODIFIED="1699068861165" TEXT="im Scheduler werden ActivationEvents verarbeitet">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
diese enthalten eine Activity, aber auch ein Zeitfenster sowie weitere Kontext-Parameter (ManifestationID, Flags wie isCompulsory)
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1699068862433" ID="ID_84744804" MODIFIED="1699068890174" TEXT="die abstrakte Verkn&#xfc;pfung beider Ebenen l&#xe4;uft &#xfc;ber den ExecutionContext">
<icon BUILTIN="idea"/>
<node CREATED="1699068892165" ID="ID_675338306" MODIFIED="1699068902425" TEXT="f&#xfc;r die Sprache ist dieser ein Konzept mit bestimmten Operationen"/>
<node CREATED="1699068903139" ID="ID_1436947530" MODIFIED="1699068930220" TEXT="f&#xfc;r die Scheduler-Impl ist dieser ein impliziter Daten-Vererbungs-Zusammenhang"/>
</node>
</node>
</node>
<node CREATED="1697500165153" ID="ID_1967372083" MODIFIED="1697500296652" TEXT="Bedeutung des &#xbb;when&#xab;-Parameters">
<node CREATED="1697501206702" ID="ID_327384351" MODIFIED="1697501307538" TEXT="POST-Activity: data_.timeWindow.life &#x27fc; &#xbb;when&#xab;-Parameter"/>
@ -82678,9 +82705,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1699024386786" ID="ID_461028283" MODIFIED="1699024525908" TEXT="Verbindung von Schedule + Dispatch + Kontext">
<linktarget COLOR="#e8694c" DESTINATION="ID_461028283" ENDARROW="Default" ENDINCLINATION="-786;46;" ID="Arrow_ID_1280528411" SOURCE="ID_1301519166" STARTARROW="None" STARTINCLINATION="-1155;1082;"/>
<icon BUILTIN="flag-pink"/>
<node COLOR="#338800" CREATED="1699024386786" ID="ID_461028283" MODIFIED="1699067755855" TEXT="Verbindung von Schedule + Dispatch + Kontext">
<linktarget COLOR="#4c8de8" DESTINATION="ID_461028283" ENDARROW="Default" ENDINCLINATION="-786;46;" ID="Arrow_ID_1280528411" SOURCE="ID_1301519166" STARTARROW="None" STARTINCLINATION="-1155;1082;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1699024540045" ID="ID_1296957170" MODIFIED="1699024561590" TEXT="Erweiterung und Konkretisierung des Konzepts &#xbb;ExecutionCtx&#xab;"/>
<node CREATED="1699024566660" ID="ID_655395356" MODIFIED="1699024593618" TEXT="mu&#xdf; einen Aufruf-Kontext an Chain-Dispatch weitergeben"/>
<node COLOR="#338800" CREATED="1699024676051" ID="ID_103937457" MODIFIED="1699059213450" TEXT="Umbau der Layer-2-Schnittstelle">
@ -82703,37 +82730,62 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
<node CREATED="1699051463164" ID="ID_1195414225" MODIFIED="1699051581149" TEXT="und zwar den Queue-Entry selber">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...denn sonst w&#252;rde in jedem Fall der Zugriff komplizierter; zwar handelt es sich um ein Implementierungs-Detail, aber <i>im ScheudlerService selber</i>&#160;darf es durchaus Verkopplung auf die Implementierung geben
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1699051584938" ID="ID_213354892" MODIFIED="1699051595561" TEXT="diesen aus SchedulerInvocation herausziehen"/>
<node CREATED="1699058918297" ID="ID_1882852880" MODIFIED="1699058938154" TEXT="und als Ergebnis der Such/Zugriffsfunktionen"/>
<node CREATED="1699058939126" ID="ID_1931163701" MODIFIED="1699059075327" TEXT="per value und gut is">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Wir sind hier in einem &#187;inline&#171;-Universum, und die wenigen Verwendungen machen entweder direkt die bool-conversion oder speichern die Datenwerte irgendwo auf den Stack. Da is nix &#8222;schwergewichtig&#8220;
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1699059226152" ID="ID_517060181" MODIFIED="1699059239374" TEXT="ExecutionCtx neu interpretieren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1699059226152" ID="ID_517060181" MODIFIED="1699066627672" TEXT="ExecutionCtx neu interpretieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1699059620700" ID="ID_360359127" MODIFIED="1699059642428" TEXT="wird nun zu einem eigenst&#xe4;ndigen Daten-Tupel"/>
<node CREATED="1699059642976" ID="ID_1502497255" MODIFIED="1699059660298" TEXT="enth&#xe4;lt eine Scheduler-Referenz, sowie die Kontext-Daten">
<node CREATED="1699059662146" ID="ID_397956314" MODIFIED="1699059688494" TEXT="starting"/>
<node CREATED="1699059689052" ID="ID_841331806" MODIFIED="1699059690848" TEXT="deadline"/>
<node CREATED="1699059691450" ID="ID_656170104" MODIFIED="1699059693797" TEXT="manifestation"/>
<node CREATED="1699059694322" ID="ID_1944612474" MODIFIED="1699059702249" TEXT="isCompulsory"/>
</node>
<node CREATED="1699064095814" ID="ID_1608237175" MODIFIED="1699066731683" TEXT="naheliegend: er bettet das ActivationEvent direkt ein">
<arrowlink COLOR="#2f51c8" DESTINATION="ID_594270596" ENDARROW="Default" ENDINCLINATION="149;5;" ID="Arrow_ID_969326317" STARTARROW="None" STARTINCLINATION="169;9;"/>
</node>
</node>
<node CREATED="1699064124674" ID="ID_594270596" MODIFIED="1699066733362" STYLE="fork" TEXT="damit gruppieren sich nun pl&#xf6;tzlich alle internen Scheduler-APIs um das ActivationEvent">
<linktarget COLOR="#2f51c8" DESTINATION="ID_594270596" ENDARROW="Default" ENDINCLINATION="149;5;" ID="Arrow_ID_969326317" SOURCE="ID_1608237175" STARTARROW="None" STARTINCLINATION="169;9;"/>
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="idea"/>
</node>
</node>
<node BACKGROUND_COLOR="#fefc4e" COLOR="#351d75" CREATED="1699068256826" HGAP="12" ID="ID_660894370" MODIFIED="1699068345021" VSHIFT="13">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
neuer <u>Haupt-Eingang</u>: <font face="Monospaced" color="#a20a15">postChain(ActivationEvent)</font>
</p>
</body>
</html></richcontent>
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="idea"/>
<node COLOR="#594398" CREATED="1699068463527" ID="ID_82378192" MODIFIED="1699068489999" TEXT="gef&#xfc;hlsm&#xe4;&#xdf;ig... jetzt sortiert sich das Design doch noch">
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
</node>
<node CREATED="1699068495938" ID="ID_1790197795" MODIFIED="1699068527242" TEXT="das ist ein internes API &#x2014; aber die zentrale Achse"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697763699242" ID="ID_272943268" MODIFIED="1697763810547" TEXT="EngineObserver-Schnittstelle">
@ -84349,13 +84401,11 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698945215604" ID="ID_1778628995" MODIFIED="1698946166942" TEXT="Scheduler high-level-API erweitern">
<arrowlink COLOR="#3d3aa9" DESTINATION="ID_780386157" ENDARROW="Default" ENDINCLINATION="69;74;" ID="Arrow_ID_103420327" STARTARROW="None" STARTINCLINATION="317;25;"/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1698971859240" HGAP="82" ID="ID_1814368786" MODIFIED="1698971899517" TEXT="Problem: &#x3bb;-post &#xfc;bermittelt nicht zwingend eine Deadline" VSHIFT="17">
<node COLOR="#435e98" CREATED="1698971859240" HGAP="82" ID="ID_1814368786" MODIFIED="1699067968145" TEXT="Problem: &#x3bb;-post &#xfc;bermittelt nicht zwingend eine Deadline" VSHIFT="17">
<icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698971900742" ID="ID_970794288" MODIFIED="1698972278874" TEXT="Problem bedingt durch die Offenheit der Activity-Language">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
das hei&#223;t, das ist latent ein Design-Problem &#8212; welches ich derzeit nicht l&#246;sen kann, da mir der Gesamt&#252;berblick noch fehlt, und ich <i>genau deshalb </i>dieses offene Design gew&#228;hlt habe
@ -84366,9 +84416,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node CREATED="1698972217673" ID="ID_1012249644" MODIFIED="1698972251202">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<i>kann </i>es auch gar nicht, denn die Activity selber kennt i.A. keine Deadline
@ -84376,7 +84424,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1698976477150" ID="ID_1307962302" MODIFIED="1698976496606" TEXT="die Bedeutungs-Ebenen schichten sich hier nicht sauber">
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1698976477150" ID="ID_1307962302" MODIFIED="1699068112389" TEXT="die Bedeutungs-Ebenen schichten sich hier nicht sauber">
<arrowlink COLOR="#fef7ad" DESTINATION="ID_794200787" ENDARROW="Default" ENDINCLINATION="3;-49;" ID="Arrow_ID_1938210019" STARTARROW="None" STARTINCLINATION="13;35;"/>
<icon BUILTIN="broken-line"/>
<node CREATED="1698976502827" ID="ID_429532581" MODIFIED="1698976539535" TEXT="Manifestation und compulsory werden erst auf Scheduler-Level bedeutsam"/>
<node CREATED="1698976540742" ID="ID_1531605944" MODIFIED="1698976573680" TEXT="die Deadline gibt es auf Activity-Language-Ebene &#x2014; aber nur als Gate-Check"/>
@ -84384,7 +84433,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1698976678515" ID="ID_203481682" MODIFIED="1698976708963" TEXT="Sie mu&#xdf; aber durch den abstrahierten Kontext hindurch propagieren k&#xf6;nnen"/>
<node CREATED="1698976759992" ID="ID_1975784916" MODIFIED="1698976784377" TEXT="die Semantik sollte sich &#x201e;gem&#xe4;&#xdf; Kontext&#x201c; sinnvoll erweitern"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1698976787213" ID="ID_1115119952" MODIFIED="1699023969671" TEXT="die Kontext-Abstraktion mu&#xdf; dieses Problem absorbieren">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698976787213" ID="ID_1115119952" MODIFIED="1699068014124" TEXT="die Kontext-Abstraktion mu&#xdf; dieses Problem absorbieren">
<arrowlink COLOR="#863957" DESTINATION="ID_1301519166" ENDARROW="Default" ENDINCLINATION="51;-68;" ID="Arrow_ID_552857902" STARTARROW="None" STARTINCLINATION="-187;9;"/>
<icon BUILTIN="yes"/>
<node CREATED="1699023474989" ID="ID_171697917" MODIFIED="1699023495844" TEXT="daf&#xfc;r mu&#xdf; dann allerdings der ExecutionCtx eigenst&#xe4;ndig werden"/>
@ -84392,11 +84441,40 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1699023516788" ID="ID_984606864" MODIFIED="1699023534470" TEXT="ist aber stets im lokalen Call-stack und massiv concurrent im Einsatz"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1699023878349" HGAP="146" ID="ID_1301519166" MODIFIED="1699024525908" TEXT="Umbau: Dispatch-mit-Kontext" VSHIFT="1">
<node COLOR="#435e98" CREATED="1699023878349" HGAP="146" ID="ID_1301519166" MODIFIED="1699068042973" VSHIFT="1">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
Umbau: <b>Dispatch-mit-Kontext</b>
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#7136ac" DESTINATION="ID_1886746867" ENDARROW="Default" ENDINCLINATION="224;12;" ID="Arrow_ID_759487829" STARTARROW="None" STARTINCLINATION="50;-182;"/>
<arrowlink COLOR="#e8694c" DESTINATION="ID_461028283" ENDARROW="Default" ENDINCLINATION="-786;46;" ID="Arrow_ID_1280528411" STARTARROW="None" STARTINCLINATION="-1155;1082;"/>
<arrowlink COLOR="#4c8de8" DESTINATION="ID_461028283" ENDARROW="Default" ENDINCLINATION="-786;46;" ID="Arrow_ID_1280528411" STARTARROW="None" STARTINCLINATION="-1155;1082;"/>
<linktarget COLOR="#863957" DESTINATION="ID_1301519166" ENDARROW="Default" ENDINCLINATION="51;-68;" ID="Arrow_ID_552857902" SOURCE="ID_1115119952" STARTARROW="None" STARTINCLINATION="-187;9;"/>
<icon BUILTIN="flag-pink"/>
<icon BUILTIN="yes"/>
<node CREATED="1699067790118" HGAP="44" ID="ID_1620165722" MODIFIED="1699067814211" TEXT="das hatte kaskadierende Konsequenzen f&#xfc;r das Scheduler-Design" VSHIFT="17"/>
<node CREATED="1699067815133" HGAP="46" ID="ID_836622489" MODIFIED="1699067891529" VSHIFT="1">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
der Queue-Entry wurde zum <b>ActivationEvent</b>&#160;als zentrale Austausch-Einheit
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#373aa8" CREATED="1699067892381" HGAP="48" ID="ID_794200787" MODIFIED="1699068148831" TEXT="damit sortieren sich nun die Design-Ebenen logisch sauber" VSHIFT="6">
<linktarget COLOR="#fef7ad" DESTINATION="ID_794200787" ENDARROW="Default" ENDINCLINATION="3;-49;" ID="Arrow_ID_1938210019" SOURCE="ID_1307962302" STARTARROW="None" STARTINCLINATION="13;35;"/>
<icon BUILTIN="idea"/>
<node CREATED="1699067909833" ID="ID_1876838421" LINK="#ID_816321833" MODIFIED="1699068736393" TEXT="Activity-Language: behandelt Activities"/>
<node CREATED="1699067928790" ID="ID_1552671650" MODIFIED="1699067940881" TEXT="SchedulerService: behandelt Activation-Events"/>
</node>
<node CREATED="1699067982500" HGAP="61" ID="ID_627574639" MODIFIED="1699068008177" TEXT="&#x3bb;-post erzeugt nun eine Kaskade von verschachtelten Kontexten" VSHIFT="1"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1699068176202" HGAP="81" ID="ID_1943235489" MODIFIED="1699068208749" TEXT="(hoffe der Optimiser bekommt das sortiert)" VSHIFT="19">
<font NAME="SansSerif" SIZE="9"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1698946201921" ID="ID_1058086166" MODIFIED="1698963474934" TEXT="Informationsfunktionen erg&#xe4;nzen">