From ccf0710903a70609adb21c0a251ea1d08cfdb0d6 Mon Sep 17 00:00:00 2001
From: Ichthyostega
Date: Sun, 9 Jul 2023 01:32:27 +0200
Subject: [PATCH] =?UTF-8?q?Block-Flow:=20maintain=20an=20=C2=BBEpoch=C2=AB?=
=?UTF-8?q?=20within=20the=20raw=20allocation=20Extent?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- the idea is to use slot-0 in each extent for administrative metadata
- to that end, a specialised GATE-Activity is placed into slot-0
- decision to use the next-pointer for managing the next free slot
- thus we need the help of the underlying ExtentFamily for navigating Extents
Decision to refrain from any attempt to "fix" excessive memory usage,
caused by Epochs still blocked by pending IO operations. Rather, we
assume the engine uses sane parametrisation (possibly with dynamic adjustment)
Yet still there will be some safety limit, but when exceeding this limit,
the allocator will just throw, thereby killing the playback/render process
---
src/lib/iter-adapter.hpp | 4 +-
src/vault/gear/activity.hpp | 16 +-
src/vault/gear/block-flow.hpp | 98 +++++++++++-
src/vault/mem/extent-family.hpp | 22 ++-
tests/library/iter-adapter-test.cpp | 24 +--
tests/vault/gear/block-flow-test.cpp | 2 +-
wiki/thinkPad.ichthyo.mm | 227 +++++++++++++++++++++++++--
7 files changed, 347 insertions(+), 46 deletions(-)
diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp
index 581613611..069d57fcd 100644
--- a/src/lib/iter-adapter.hpp
+++ b/src/lib/iter-adapter.hpp
@@ -135,7 +135,7 @@ namespace lib {
* - it is not just a disguised pointer (meaning, it's more expensive)
* - it checks validity on every operation and may throw
* - it has a distinct back-link to the source container
- * - the source container needs to provide hasNext() and iterNext() free functions.
+ * - the source container needs to support `checkPoint()` and `iterNext()` free functions.
* - we may need friendship to implement those extension points on the container
* - the end-of-iteration can be detected by bool check
* @note it is possible to "hide" a smart-ptr within the CON template parameter.
@@ -303,7 +303,7 @@ namespace lib {
inline bool operator== (IterAdapter const& il, IterAdapter const& ir) { return il.pos_ == ir.pos_; }
template
- inline bool operator!= (IterAdapter const& il, IterAdapter const& ir) { return !(il == ir); }
+ inline bool operator!= (IterAdapter const& il, IterAdapter const& ir) { return not (il == ir); }
diff --git a/src/vault/gear/activity.hpp b/src/vault/gear/activity.hpp
index dd12beb44..1f8ad14a3 100644
--- a/src/vault/gear/activity.hpp
+++ b/src/vault/gear/activity.hpp
@@ -181,7 +181,7 @@ namespace gear {
explicit
- Activity (Verb verb)
+ Activity (Verb verb) noexcept
: verb_{verb}
, next{nullptr}
{
@@ -193,7 +193,7 @@ namespace gear {
/* ==== special case initialisation ==== */
- Activity (size_t o1, size_t o2)
+ Activity (size_t o1, size_t o2) noexcept
: Activity{FEED}
{
data_.feed.one = o1;
@@ -201,42 +201,42 @@ namespace gear {
}
explicit
- Activity (Activity* target)
+ Activity (Activity* target) noexcept
: Activity{NOTIFY}
{
data_.notification.target = target;
}
explicit
- Activity (int expectNotifications, Time deadline = Time::NEVER)
+ Activity (int expectNotifications, Time deadline = Time::NEVER) noexcept
: Activity{GATE}
{
data_.condition.rest = expectNotifications;
data_.condition.dead = deadline;
}
- Activity (Time when, Activity* followUp)
+ Activity (Time when, Activity* followUp) noexcept
: verb_{POST}
, next{followUp}
{
data_.timeWindow = {when,when};
}
- Activity (Time start, Time after, Activity* followUp)
+ Activity (Time start, Time after, Activity* followUp) noexcept
: verb_{POST}
, next{followUp}
{
data_.timeWindow = {start,after};
}
- Activity()
+ Activity() noexcept
: Activity{TICK}
{ }
private:
void
- setDefaultArg (Verb verb)
+ setDefaultArg (Verb verb) noexcept
{
data_ = {0,0};
switch (verb) {
diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp
index 4fabda044..7cfd17229 100644
--- a/src/vault/gear/block-flow.hpp
+++ b/src/vault/gear/block-flow.hpp
@@ -34,7 +34,7 @@
** activities, necessitating timely clean-up of obsolete descriptors.
** Used memory should be recycled, calling for an arrangement of
** pooled allocation tiles, extending the underlying block
- ** allocation on increased throughput.
+ ** allocation on increased throughput.
**
** @note currently this rather marks the intended memory management pattern,
** while the actual allocations are still performed on the heap.
@@ -60,6 +60,7 @@
//#include "lib/util.hpp"
//#include
+#include
namespace vault{
@@ -75,30 +76,66 @@ namespace gear {
const size_t INITIAL_FRAMES = 50;
const size_t INITIAL_ALLOC = 1 + (INITIAL_FRAMES * ACTIVITIES_PER_FRAME) / EPOCH_SIZ;
+ /** raw allocator to provide a sequence of Extents to place Activity records */
using Allocator = mem::ExtentFamily;
}
+
/**
- *
+ * Allocation Extent holding _scheduler Activities_ to be performed altogether
+ * before a common _deadline._ Other than the underlying raw Extent, the Epoch
+ * maintains a deadline time and keeps track of storage slots already claimed.
+ * This is achieved by using the Activity record in the first slot as a GATE term
+ * to maintain those administrative information.
+ * @remark rationale is to discard the Extent as a whole, once the deadline passed.
*/
class Epoch
: public Allocator::Extent
{
-
+
/// @warning will be faked, not constructed
Epoch() = delete;
public:
+ /**
+ * specifically rigged GATE Activity,
+ * used for managing Epoch metadata
+ * - the Condition::rest tracks pending async IO operations
+ * - the Condition::deadline is the nominal deadline of this Epoch
+ * - the field `next` points to the next free allocation Slot to use
+ */
struct EpochGate
: Activity
{
+ /** @note initially by default there is...
+ * - effectively no deadline
+ * - no IO operations pending (i.e. we can just discard the Epoch)
+ * - the `next` usable Slot is the last Storage slot, and will be
+ * decremented until there is only one slot left (EpochGate itself)
+ * @warning EpochGate is assumed to sit in the Epoch's first slot
+ */
EpochGate()
: Activity{int(0), Time::ANYTIME}
{
- UNIMPLEMENTED ("initialise allocation usage marker to zero");
+ // initialise allocation usage marker: start at last usable slot
+ next = this + (Epoch::SIZ() - 1);
+ ENSURE (next != this);
}
// default copyable
+
+ bool
+ hasFreeSlot()
+ { // see C++ § 5.9 : comparison of pointers within same array
+ return next > this;
+ }
+
+ Activity*
+ claimNextSlot()
+ {
+ REQUIRE (hasFreeSlot());
+ return next--;
+ }
};
static Epoch&
@@ -116,8 +153,12 @@ namespace gear {
}
};
+
+
/**
- * Basic (abstracted) view of...
+ * Allocation scheme for the Scheduler, based on Epoch(s).
+ * Scheduling entails to provide a chain of Activity definitions,
+ * which will then »flow« through the priority queue until invocation.
*
* @see SchedulerCommutator
* @see BlockFlow_test
@@ -132,10 +173,51 @@ namespace gear {
: alloc_{INITIAL_ALLOC}
{ }
- Activity&
- createActivity (Activity::Verb verb, Time deadline)
+ /**
+ * Local handle to allow allocating a collection of Activities,
+ * all sharing a common deadline. Internally, these records are
+ * maintained in fixed-sized _extents_ and thus allocations may
+ * _overflow_ — leading to allocation of further extents. However,
+ * this extension is handled transparently by the embedded iterator.
+ */
+ class AllocatorHandle
{
- UNIMPLEMENTED ("place new allocation");
+ Allocator::iterator extent;
+
+ public:
+ template
+ Activity&
+ create (ARGS&& ...args)
+ {
+ return *new(claimSlot()) Activity {std::forward (args)...};
+ }
+
+ private:
+ Epoch&
+ currEpoch()
+ {
+ REQUIRE (bool(extent));
+ return static_cast (extent->access()); ////////////////////////////OOO is it possible to adapt the iterator, so that dereferentiation directly yields the Extent?
+ }
+
+ void*
+ claimSlot() ///< EX_SANE
+ {
+ if (currEpoch().gate().hasFreeSlot())
+ {
+ return currEpoch().gate().claimNextSlot();
+ }
+ else // Epoch overflow
+ { // use following Epoch; possibly allocate
+ UNIMPLEMENTED ("advance to next epoch");
+ }
+ }
+ };
+
+ AllocatorHandle
+ until (Time deadline)
+ {
+ UNIMPLEMENTED ("search through existing Epochs to locate the latest one to support given deadline");
}
void
diff --git a/src/vault/mem/extent-family.hpp b/src/vault/mem/extent-family.hpp
index d7e156327..7746e0f0d 100644
--- a/src/vault/mem/extent-family.hpp
+++ b/src/vault/mem/extent-family.hpp
@@ -46,7 +46,8 @@
#include "vault/common.hpp"
//#include "lib/symbol.hpp"
-#include "lib/nocopy.hpp"
+#include "lib/iter-adapter.hpp"
+#include "lib/nocopy.hpp"
//#include "lib/util.hpp"
//#include
@@ -76,6 +77,7 @@ namespace mem {
: std::array
{
using Payload = T;
+ using SIZ = std::integral_constant;
};
private:
@@ -101,6 +103,7 @@ namespace mem {
}
};
using Extents = std::vector;
+ using RawIter = typename Extents::iterator;
Extents extents_;
@@ -119,6 +122,23 @@ namespace mem {
{
extents_.reserve (expectedMaxExtents);
}
+
+ /** allow transparent iteration of Extents, expanding storage on demand */
+ using iterator = lib::IterAdapter; ////////////////////OOO consider to use a Extent* instead of the RawIter??
+
+ /* == Iteration control API (used by IterAdapter via ADL) == */
+
+ friend bool
+ checkPoint (ExtentFamily* exFam, RawIter& pos)
+ {
+ UNIMPLEMENTED ("ExtentFamily iteration control: check and adapt position");
+ }
+
+ friend void
+ iterNext (ExtentFamily* exFam, RawIter& pos)
+ {
+ UNIMPLEMENTED ("ExtentFamily iteration control: access next Extent, possibly expand allocation");
+ }
};
diff --git a/tests/library/iter-adapter-test.cpp b/tests/library/iter-adapter-test.cpp
index 89b20101e..c29bbb0a0 100644
--- a/tests/library/iter-adapter-test.cpp
+++ b/tests/library/iter-adapter-test.cpp
@@ -144,9 +144,9 @@ namespace test{
template
friend void
iterNext (const TestContainer*, ITER& pos)
- {
- ++pos;
- }
+ {
+ ++pos;
+ }
/** Implementation of Iteration-logic: detect iteration end.
* @note the problem here is that this implementation chooses to use
@@ -161,15 +161,15 @@ namespace test{
template
friend bool
checkPoint (const TestContainer* src, ITER& pos)
- {
- REQUIRE (src);
- if ((pos != ITER()) && (pos != src->numberz_.end()))
- return true;
- else
- {
- pos = ITER();
- return false;
- } }
+ {
+ REQUIRE (src);
+ if ((pos != ITER()) && (pos != src->numberz_.end()))
+ return true;
+ else
+ {
+ pos = ITER();
+ return false;
+ } }
};
} // (END) impl test dummy container
diff --git a/tests/vault/gear/block-flow-test.cpp b/tests/vault/gear/block-flow-test.cpp
index 4354925bf..f73ac460e 100644
--- a/tests/vault/gear/block-flow-test.cpp
+++ b/tests/vault/gear/block-flow-test.cpp
@@ -76,7 +76,7 @@ namespace test {
{
BlockFlow bFlow;
Time deadline = randTime();
- Activity tick = bFlow.createActivity(Activity::TICK, deadline);
+ Activity tick = bFlow.until(deadline).create();
///////////////////////////////////////////////////////////////////////////////OOO diagnostic function to check allocation
bFlow.discardBefore (deadline + Time{0,5});
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 63e58c4a9..df3f23932 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -78646,7 +78646,15 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
+
+
+
+
+
+
+
+
@@ -78741,6 +78749,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
@@ -78879,11 +78888,38 @@ Date: Thu Apr 20 18:53:17 2023 +0200