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:
parent
62a1310566
commit
72258c06bd
7 changed files with 244 additions and 152 deletions
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = [&] {
|
||||
|
|
|
|||
|
|
@ -80630,6 +80630,33 @@ Date:   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ür die reinen Activities zuständig">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
hier spielt eine Deadline nur die Rolle einer zu prü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üpfung beider Ebenen läuft über den ExecutionContext">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1699068892165" ID="ID_675338306" MODIFIED="1699068902425" TEXT="für die Sprache ist dieser ein Konzept mit bestimmten Operationen"/>
|
||||
<node CREATED="1699068903139" ID="ID_1436947530" MODIFIED="1699068930220" TEXT="fü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 »when«-Parameters">
|
||||
<node CREATED="1697501206702" ID="ID_327384351" MODIFIED="1697501307538" TEXT="POST-Activity: data_.timeWindow.life ⟼ »when«-Parameter"/>
|
||||
|
|
@ -82678,9 +82705,9 @@ Date:   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 »ExecutionCtx«"/>
|
||||
<node CREATED="1699024566660" ID="ID_655395356" MODIFIED="1699024593618" TEXT="muß 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:   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ürde in jedem Fall der Zugriff komplizierter; zwar handelt es sich um ein Implementierungs-Detail, aber <i>im ScheudlerService selber</i> 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 »inline«-Universum, und die wenigen Verwendungen machen entweder direkt die bool-conversion oder speichern die Datenwerte irgendwo auf den Stack. Da is nix „schwergewichtig“
|
||||
</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ändigen Daten-Tupel"/>
|
||||
<node CREATED="1699059642976" ID="ID_1502497255" MODIFIED="1699059660298" TEXT="enthä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ö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ühlsmäß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 — 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:   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: λ-post übermittelt nicht zwingend eine Deadline" VSHIFT="17">
|
||||
<node COLOR="#435e98" CREATED="1698971859240" HGAP="82" ID="ID_1814368786" MODIFIED="1699067968145" TEXT="Problem: λ-post ü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ßt, das ist latent ein Design-Problem — welches ich derzeit nicht lösen kann, da mir der Gesamtüberblick noch fehlt, und ich <i>genau deshalb </i>dieses offene Design gewählt habe
|
||||
|
|
@ -84366,9 +84416,7 @@ Date:   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:   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 — aber nur als Gate-Check"/>
|
||||
|
|
@ -84384,7 +84433,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1698976678515" ID="ID_203481682" MODIFIED="1698976708963" TEXT="Sie muß aber durch den abstrahierten Kontext hindurch propagieren können"/>
|
||||
<node CREATED="1698976759992" ID="ID_1975784916" MODIFIED="1698976784377" TEXT="die Semantik sollte sich „gemäß Kontext“ sinnvoll erweitern"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1698976787213" ID="ID_1115119952" MODIFIED="1699023969671" TEXT="die Kontext-Abstraktion muß dieses Problem absorbieren">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698976787213" ID="ID_1115119952" MODIFIED="1699068014124" TEXT="die Kontext-Abstraktion muß 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ür muß dann allerdings der ExecutionCtx eigenständig werden"/>
|
||||
|
|
@ -84392,11 +84441,40 @@ Date:   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ü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> 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="λ-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änzen">
|
||||
|
|
|
|||
Loading…
Reference in a new issue