diff --git a/src/vault/gear/scheduler-commutator.hpp b/src/vault/gear/scheduler-commutator.hpp
index 0cf259965..38b46de1a 100644
--- a/src/vault/gear/scheduler-commutator.hpp
+++ b/src/vault/gear/scheduler-commutator.hpp
@@ -184,6 +184,21 @@ namespace gear {
layer1.feedPrioritisation();
}
+ /** update queue head to discard obsolete content.
+ * @param now _current time_ to use for decision about dropping tasks
+ * @return `false` when failing to establish a consistent state due to
+ * missed **compulsory** entries; should cause **Emergency halt**.
+ */
+ bool
+ maintainQueueHead (SchedulerInvocation& layer1, Time now)
+ {
+ ENSURE (holdsGroomingToken (thisThread()));
+ layer1.feedPrioritisation();
+ while (layer1.isOutdated (now) and not layer1.isOutOfTime(now))
+ layer1.pullHead();
+ return not layer1.isOutOfTime(now);
+ }
+
/**
* Look into the queues and possibly retrieve work due by now.
* @note transparently discards any outdated entries,
@@ -198,17 +213,17 @@ namespace gear {
layer1.feedPrioritisation();
while (layer1.isOutdated (now) and not layer1.isOutOfTime(now))
layer1.pullHead();
+ if (not maintainQueueHead (layer1,now))
+ ALERT (engine, "MISSED compulsory job -- should raise Scheduler-Emergency"); //////////////TICKET #1362 : not clear where Scheduler-Emergency is to be handled and how it can be triggered. See Scheduler::triggerEmergency()
+ else
if (layer1.isDue (now))
- {
- if (layer1.isOutOfTime(now))
- UNIMPLEMENTED ("how to trigger a Scheduler-Emergency from here"); ///////////////////////TICKET #1362 : not clear where Scheduler-Emergency is to be handled and how it can be triggered. See Scheduler::triggerEmergency()
- else
- return layer1.pullHead();
- } }
+ return layer1.pullHead();
+ }
return ActivationEvent();
}
+
/***********************************************************//**
* This is the primary entrance point to the Scheduler.
* Place the given event into the schedule, with prioritisation
diff --git a/src/vault/gear/scheduler-invocation.hpp b/src/vault/gear/scheduler-invocation.hpp
index 200a0293d..28228dfad 100644
--- a/src/vault/gear/scheduler-invocation.hpp
+++ b/src/vault/gear/scheduler-invocation.hpp
@@ -278,10 +278,10 @@ namespace gear {
and priority_.top().starting <= waterLevel(now);
}
- /** determine if Activity at scheduler head missed it's deadline
+ /** determine if the Activity at scheduler head missed it's deadline.
* @warning due to memory management, such an Activity must not be dereferenced */
bool
- isMissed (Time now) const
+ isMissed (Time now) const
{
return not priority_.empty()
and waterLevel(now) > priority_.top().deadline;
diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp
index 390f9ef58..e564312aa 100644
--- a/src/vault/gear/scheduler.hpp
+++ b/src/vault/gear/scheduler.hpp
@@ -71,7 +71,7 @@
**
** If however a thread is put to work, it will start dequeuing an entry from
** the head of the [priority queue](\ref SchedulerInvocation::pullHead),
- ** and start interpreting this entry as a _chain of render activities_ with
+ ** and start interpreting this entry as a _chain of render activities,_ with
** the help of the [»Activity Language«](\ref ActivityLang::dispatchChain).
** In the typical scenario, after some preparatory checks and notifications,
** the thread [transitions into work mode](\ref Scheduler::ExecutionCtx::work),
@@ -94,8 +94,10 @@
** @see SchedulerCommutator Layer-2
** @see activity.hpp description of »Render Activities«
**
- ** @todo WIP 11/2023 »Playback Vertical Slice«
- **
+ ** @todo WIP 11/2024 »Playback Vertical Slice«
+ ** - initial version of Scheduler was built and validated by \ref scheduler-stress-test.cpp
+ ** - now awaiting integration with Render-Node invocation and Job-Planning
+ ** - very likely we'll extract a Scheduler-Interface (and this file then becomes a service-impl)
*/
@@ -112,11 +114,8 @@
#include "vault/gear/load-controller.hpp"
#include "vault/gear/engine-observer.hpp"
#include "vault/real-clock.hpp"
-//#include "lib/symbol.hpp"
#include "lib/nocopy.hpp"
-//#include "lib/util.hpp"
-//#include
...weil ich Stand 8/2018 nicht im Stande bin,
@@ -17336,9 +17334,7 @@
das Diff wird auf den Platzhalter angewendet
@@ -17794,9 +17790,7 @@
in diesem Fall ist das dann eine Art Toggle-Button, d.h. er wechselt auch seine Gestalt
@@ -18803,9 +18797,7 @@
Nein! minimal und natural size sollten gleich sein
@@ -19378,9 +19370,7 @@
Unterscheidung resize ⟺ rerender
@@ -20525,9 +20515,7 @@
Standard UI-Mechanik überlassen wir GTK
@@ -22863,9 +22851,7 @@
...denn die CSS-Node-Namen von Custom-Widgets kann man via GTKmm nicht ändern.
@@ -43304,9 +43290,7 @@
das hier ist die beste Alternative
@@ -44596,9 +44580,7 @@
#--◆--# _raw(win.overallSpan().duration()) ? = 307445734561825860
@@ -46361,9 +46343,7 @@
für wirklich generische Styles sollte man generische Klassen schaffen
@@ -46587,9 +46567,7 @@
generisch
@@ -46759,9 +46737,7 @@
need to bubble up
@@ -47054,9 +47030,7 @@
ist er aber nicht notwendig,
@@ -57521,7 +57495,7 @@
+ hab damals einfach »aufgegeben«,
+
+ da der Trigger-Punkt unpassenderweise in Layer-2 liegt
+
+ ...und genau diese Entscheidung konnte/wollte ich vor einem halben Jahr nicht treffen (und bin im Moment nicht sicher, ob ich sie jetzt treffen kann)
+
- https://issues.lumiera.org/ticket/1362#comment:1 -
- - - + ++ es gibt Situationen, in denen das Schedule »gebrochen« wird, und das führt u.U. zum Deadlock bzw. dazu, daß ein Berechnungvorgang stillschweigend stecken bleibt; diese Situationen können relativ leicht auf einem unteren Level im Code erkannt werden, aber von dort ist es nicht einfach, einen Alarm auf einem globalen Level auszulösen +
+ + ++ genauer: eine externe Reaktion ist sicher nicht etws, was Layer-2 einfach machen kann — hier entstehe eine zunehmend komplexe Dependency-Injection, insofern der Layer-2 auch Entscheidungen für den Service als Ganzes trifft +
+ + ++ ...wenn diese naiv reingelassen werden und die gesamte Work-Kapazität ausschöpfen, führt der nächste »Tick«-Job zur Scheduler-Emergency. +
+ + ++ wenn man einen Verarbeitungsvorkang darstellt als Jobs mit Dependency-Verkettung, dann kann diese Verarbeitung ohne Weiteres irgendwo stecken bleiben, ohne daß ein Fehler ersichtlich ist; der einzige Signalisierungs-Mechanismus sind compulsory-Jobs (und das ist ein relativ brachialer Mechanismus) +
+ + ++ Es sind Render-jobs denkbar, die mehrere Stunden brauchen! +
++ Da es keine übergeordnete Koordinierungs-Instanz gibt, muß in diesem Fall von der Job-Planung aus für frei bleibende Restkapazität für Admin-Aufgaben gesorgt werden — denn wenn alle verfügbaren Worker mit solchen langwierigen Berechnungen belegt sind, findet kein »Tick« mehr statt (und der nächste Tick hat seine Deadline überfahren und löst eine Scheduler-Emergency aus). Ohne eine übergeordnete Kapazitäts- und Engine-Steuerung ist diese Situation nicht adäquat zu handhaben: entweder wir bekommen permanent und erwartbar eine Fehler-Situation, oder wir können die Parallelität nicht ausschöpfen, weil wir eine ganze Core freihalten müssen, oder wir müssen überprovisionieren. +
+ + ++ ...wenn diese die gesamte Work-Kapazität ausschöpfen, führt der nächste »Tick«-Job zur Scheduler-Emergency. +
+ + +- derzeit eingestellt auf 50ms + derzeit eingestellt auf 200ms +
++ (das sind Tick-Zyklen — nicht klar ob das reicht, denn in den Streß-Tests habe ich gesehen, daß das Schedule durchaus leicht mal um mehrere 100ms weg-rutschen kann)
@@ -134138,10 +134257,31 @@ std::cout << tmpl.render({"what", "World"}) << s+ Sowohl der Tick-Service, alsauch das pullWork müssen die Queue aktualisieren und obsolete Einträge wegwerfen; dabei kann aber eine Scheduler-Emergency auftreten (konkret: ein compulsory-task steht am Queue-Head, hat aber bereits seine Deadline überfahren, weshalb man ihn nur noch wegwerfen kann, denn der zugehörige Activity-Chain könnte bereits dealloziert bzw. recycled sein; und im Besonderen könnte dieser compulsory-task der nächste Tick sein). +
++ Aber Scheduler-Emergency gehört ganz eindeutig auf den top-Level, und muß dann sogar nach außen signalisiert werden, weil der Scheduler dieses Problem nicht selbst lösen kann +
+ + +