From d622b59dfd2108984dfb6e2a613e3bc96cbcc79e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 2 Nov 2023 22:25:19 +0100 Subject: [PATCH] Scheduler: support for classification data in Layer-1 - this is prerequisite to check for significance of the head entry - implement and verify the information functions at Layer-1 --- src/vault/gear/activity-lang.cpp | 2 +- src/vault/gear/scheduler-invocation.hpp | 66 ++++++- src/vault/gear/scheduler.hpp | 2 +- .../vault/gear/scheduler-invocation-test.cpp | 125 +++++++++--- wiki/thinkPad.ichthyo.mm | 180 +++++++++++++++--- 5 files changed, 311 insertions(+), 64 deletions(-) diff --git a/src/vault/gear/activity-lang.cpp b/src/vault/gear/activity-lang.cpp index e44b0812f..aa5dc5b24 100644 --- a/src/vault/gear/activity-lang.cpp +++ b/src/vault/gear/activity-lang.cpp @@ -50,7 +50,7 @@ namespace gear { HashVal hash_value (ManifestationID const& id) { - return hash_value (uint32_t{id}); + return boost::hash_value (uint32_t{id}); } ///////////////////////////////////////////////////////////////////////////////////////////////TODO extract scheduler-API diff --git a/src/vault/gear/scheduler-invocation.hpp b/src/vault/gear/scheduler-invocation.hpp index f0c038e5d..61e78adc1 100644 --- a/src/vault/gear/scheduler-invocation.hpp +++ b/src/vault/gear/scheduler-invocation.hpp @@ -49,9 +49,11 @@ #include "lib/nocopy.hpp" #include "vault/gear/activity.hpp" #include "lib/time/timevalue.hpp" +#include "lib/util.hpp" #include #include +#include #include namespace vault{ @@ -72,6 +74,7 @@ namespace gear { * Manages pointers to _Render Activity records._ * - new entries passed in through the #instruct_ queue * - time based prioritisation in the #priority_ queue + * @warning not threadsafe; requires Layer-2 to coordinate. * @see Scheduler * @see SchedulerInvocation_test */ @@ -88,6 +91,7 @@ namespace gear { uint32_t manifestationID :32; char :0; bool isCompulsory :1; + ///////////////////////////////////////////////////////////////////////////TICKET #1245 : use direct bit-field initialiser in C++20 /** @internal ordering function for time based scheduling * @note reversed order as required by std::priority_queue @@ -98,21 +102,22 @@ namespace gear { { return waterlevel > o.waterlevel; } - - ActOrder() =default; //////////////////////////////////////////////////////////////////////////////TICKET #1245 : use direct bit-field initialiser in C++20 }; using InstructQueue = boost::lockfree::queue; using PriorityQueue = std::priority_queue; + using ActivationSet = std::unordered_set; InstructQueue instruct_; PriorityQueue priority_; + ActivationSet allowed_; public: SchedulerInvocation() : instruct_{INITIAL_CAPACITY} , priority_{} + , allowed_{} { } @@ -201,6 +206,26 @@ namespace gear { return activity; } + /** + * Enable entries marked with a specific ManifestationID to be processed. + * By default, entries are marked with the default ManifestationID, which + * is always implicitly activated. Any other ID must be actively allowed, + * otherwise the entry is deemed [outdated](\ref isOutdated) and will + * be silently discarded in regular processing by Layer-2. + * @remark this feature allows to supersede part of a schedule + */ + void + activate (ManifestationID manID) + { + allowed_.insert (manID); + } + + void + drop (ManifestationID manID) + { + allowed_.erase (manID); + } + /* ===== query functions ===== */ @@ -211,12 +236,41 @@ namespace gear { return not priority_.empty() and priority_.top().waterlevel <= waterLevel(now); } - - /** detect a compulsory Activity with missed deadline */ + + /** determine if Activity at scheduler head missed it's deadline + * @warning due to memory management, such an Activity must not be dereferenced */ bool - isOutOfTime() const + isMissed (Time now) const { - return false; /////////////////////////////////////////////////////////////////OOO compulsory Activities + return not priority_.empty() + and waterLevel(now) > priority_.top().deathlevel; + } + + /** determine if Activities with the given ManifestationID shall be processed */ + bool + isActivated (ManifestationID manID) const + { + return manID == ManifestationID() + or util::contains (allowed_, manID); + } + + /** determine if Activity at scheduler is outdated and should be discarded */ + bool + isOutdated (Time now) const + { + return isMissed (now) + or (not priority_.empty() + and not isActivated (priority_.top().manifestationID)); + } + + /** detect a compulsory Activity at scheduler head with missed deadline */ + bool + isOutOfTime (Time now) const + { + return isMissed (now) + and (not priority_.empty() + and priority_.top().isCompulsory + and isActivated (priority_.top().manifestationID)); } bool diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index 41e642383..fdd93ca78 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -518,7 +518,7 @@ namespace gear { // consolidate queue content layer1_.feedPrioritisation(); //////////////////////////////////////////////////////////////////////OOO clean-up of outdated tasks here - if (layer1_.isOutOfTime()) + if (layer1_.isOutOfTime (now)) { triggerEmergency(); return; // leave everything as-is diff --git a/tests/vault/gear/scheduler-invocation-test.cpp b/tests/vault/gear/scheduler-invocation-test.cpp index 6b93a34a8..e8e0b579c 100644 --- a/tests/vault/gear/scheduler-invocation-test.cpp +++ b/tests/vault/gear/scheduler-invocation-test.cpp @@ -69,10 +69,11 @@ namespace test { SchedulerInvocation sched; Activity activity; Time when{1,2,3}; + Time dead{2,3,4}; CHECK (not sched.peekHead()); - sched.instruct (activity, when); + sched.instruct (activity, when, dead); sched.feedPrioritisation(); CHECK (sched.peekHead()); @@ -106,6 +107,7 @@ namespace test { CHECK (isSameObject (*sched.pullHead(), two)); CHECK (isSameObject (*sched.pullHead(), ree)); CHECK (not sched.peekHead()); + CHECK (sched.empty()); } @@ -142,36 +144,6 @@ namespace test { - /** @test verify that obsoleted or rejected entries are dropped transparently - */ - void - verify_Significance() - { - SchedulerInvocation sched; - Activity a1{1u,1u}; - Activity a2{2u,2u}; - Activity a3{3u,3u}; - Activity a4{4u,4u}; - - UNIMPLEMENTED ("transparentely discard obsoleted entries from schedule"); - 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}); - CHECK (isSameObject (*sched.peekHead(), a2)); - - sched.feedPrioritisation(); - CHECK (isSameObject (*sched.pullHead(), a1)); - CHECK (isSameObject (*sched.pullHead(), a2)); - CHECK (isSameObject (*sched.pullHead(), a3)); - CHECK (isSameObject (*sched.pullHead(), a4)); - } - - - /** @test sort order is not necessarily stable * if using identical time specs on entrance */ @@ -217,6 +189,97 @@ namespace test { CHECK (not sched.isDue (Time{0,1})); CHECK (not sched.isDue (Time{0,10})); } + + + + /** @test verify that obsoleted or rejected entries are dropped transparently + * - add entries providing extra information regarding significance + * - verify that missing the deadline is detected + * - entries past deadline are marked _outdated_ (will be dropped by Layer-2) + * - entries can be tagged with an ManifestationID, allowing to enable + * or disable visibility for a »family« of schedule entries + * - use the flag for _compulsory_ entries, allowing to detect + * a fatal _jammed_ situation where the head entry is + * out of time, while marked mandatory to process. + */ + void + verify_Significance() + { + SchedulerInvocation sched; + Activity act; + + 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})); + CHECK (not sched.isMissed (Time{3,0})); + CHECK ( sched.isMissed (Time{4,0})); + + CHECK (not sched.isOutdated (Time{2,0})); + CHECK (not sched.isOutdated (Time{3,0})); + CHECK ( sched.isOutdated (Time{4,0})); + + 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})); + + sched.activate (ManifestationID{5}); + CHECK (Time(1,0) == sched.headTime()); + CHECK ( sched.isDue (Time{1,0})); + CHECK (not sched.isOutdated (Time{1,0})); + CHECK (not sched.isOutdated (Time{3,0})); + CHECK ( sched.isOutdated (Time{4,0})); + CHECK ( sched.isMissed (Time{4,0})); + CHECK ( sched.isDue (Time{4,0})); + + sched.drop (ManifestationID{5}); + CHECK (Time(1,0) == sched.headTime()); + CHECK ( sched.isOutdated (Time{1,0})); + CHECK ( sched.isOutdated (Time{4,0})); + CHECK ( sched.isMissed (Time{4,0})); + CHECK (not sched.isMissed (Time{1,0})); + CHECK ( sched.isDue (Time{1,0})); + + sched.feedPrioritisation (act, Time{0,0}, Time{2,0}, ManifestationID{5}, true); + CHECK (Time(0,0) == sched.headTime()); // ^^^^ marked as compulsory + CHECK (not sched.isMissed (Time{1,0})); + CHECK ( sched.isOutdated (Time{1,0})); // marked as outdated since manifestation 5 is not activated + + sched.activate (ManifestationID{5}); + CHECK (not sched.isOutdated (Time{1,0})); + CHECK (not sched.isOutOfTime(Time{2,0})); // still OK /at/ deadline + CHECK ( sched.isOutOfTime(Time{3,0})); // ↯ past deadline yet marked as compulsory + CHECK ( sched.isOutdated (Time{3,0})); + CHECK ( sched.isMissed (Time{3,0})); + + sched.drop (ManifestationID{5}); + CHECK (Time(0,0) == sched.headTime()); + CHECK ( sched.isOutdated (Time{1,0})); + CHECK (not sched.isOutOfTime(Time{2,0})); + CHECK (not sched.isOutOfTime(Time{3,0})); // the disabled manifestation masks the fatal out-of-time state + CHECK ( sched.isOutdated (Time{3,0})); + CHECK ( sched.isMissed (Time{3,0})); + + sched.pullHead(); + CHECK (Time(1,0) == sched.headTime()); + CHECK ( sched.isOutdated (Time{1,0})); + CHECK ( sched.isDue (Time{1,0})); + + sched.pullHead(); + CHECK (Time(2,0) == sched.headTime()); + CHECK (not sched.isOutdated (Time{2,0})); + CHECK ( sched.isOutdated (Time{4,0})); + CHECK ( sched.isMissed (Time{4,0})); + CHECK ( sched.isDue (Time{4,0})); + + sched.pullHead(); + CHECK (Time::NEVER == sched.headTime()); + CHECK (not sched.isMissed (Time{4,0})); + CHECK (not sched.isOutdated (Time{4,0})); + CHECK (not sched.isDue (Time{4,0})); + CHECK (sched.empty()); + } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 133ee4118..3d18b9646 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79768,8 +79768,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -81981,12 +81981,26 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + +

+ ...und das hatte ich an der Stelle gar nicht erwartet: die tatsächlichen Benutzer eines »Scheduler-Service« gehen nach wenigen festen Schemata vor und müssen überhaupt nichts von einer »Activity-Language« wissen +

+ +
+ +
@@ -82010,22 +82024,32 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +

- ...bedingt durch den stark gegliederten Aufbau wird es auf Service-Ebene nichts zu verbergen geben — zudem bewegen sich voraussichtlich auch alle Clients im gleichen Code-Universums (so daß es keine Probleme mit Dependencies gibt. + ...der erhebliche techische Detailierungsgrad der übergreifenden Abläufe legt eine klare Abtrennung zu den Kollaborationsparnern nahe. Zwar bewegen sich voraussichtlich auch alle Clients im gleichen Code-Universums (so daß es keine Probleme mit Dependencies gibt), aber die integration im Scheduler selber, erweist sich bereits als herausfordernd komplex. Zudem zeichnet sich eine deutliche Abgrenzung im Sinne einer Domäne heraus: die tatsächlichen Clients müssen praktisch nichts vom Themenkomplex der »Activity-Language« wissen.

+ -
- - + + + + + + + + + + + + @@ -82163,6 +82187,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ @@ -84113,9 +84138,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + @@ -84165,15 +84190,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - - - + + + - + @@ -84206,9 +84231,102 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + + + +
    +
  • + ein solcher Task würde immer ausführbar bleiben und wäre somit für den Layer-1 neutral. +
  • +
  • + allerdings auf dem Scheduler-API muß die Deadline verpfichtend gemacht werden, denn sonst erstickt der Memory-Allocator irgendwann +
  • +
+ + +
+
+ + + + + + +

+ diese spezielle Marker-ID wird implizit stets freigeschaltet, kann also später nicht verworfen werden; das erscheint mir ein sinnvoller Default +

+ + +
+
+ + + + + + +

+ verpflichtende Activities sind ebenfalls ein Feature, das eigentlich erst auf Scheduler-Level angesiedelt ist, und hier nur den entsprechenden Support braucht +

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

+ also gehört diese Operation in Layer-2 +

+ + +
+ +
+
+
+
+ + + +
@@ -88144,8 +88262,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -88829,9 +88947,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + + + + + + + @@ -88845,7 +88969,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -89024,6 +89148,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + +