From 5055ba71444c6fbaebf96ffa2f2348a37862cac0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 13 Jul 2023 18:35:10 +0200 Subject: [PATCH] Block-Flow: rationalise iterator usage ...with the preceding IterableDecorator refactoring, the navigation and access to the storage extents can now be organised into a clear progression Allocator::iterator -> EpochIter -> Epoch& Convenience management and support functions can then be pushed down into Epoch, while iteration control can be done high-level in BlockFlow, based on the helpers in Epoch --- src/vault/gear/block-flow.hpp | 100 ++++++++++++++++--------- src/vault/mem/extent-family.hpp | 62 +++++++++------ tests/vault/mem/extent-family-test.cpp | 4 +- wiki/thinkPad.ichthyo.mm | 26 +++++-- 4 files changed, 125 insertions(+), 67 deletions(-) diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp index 6af893b81..60760e043 100644 --- a/src/vault/gear/block-flow.hpp +++ b/src/vault/gear/block-flow.hpp @@ -54,12 +54,10 @@ #include "vault/common.hpp" #include "vault/gear/activity.hpp" #include "vault/mem/extent-family.hpp" -//#include "lib/symbol.hpp" #include "lib/time/timevalue.hpp" #include "lib/nocopy.hpp" #include "lib/util.hpp" -//#include #include @@ -67,12 +65,12 @@ namespace vault{ namespace gear { using util::isnil; -// using std::string; using lib::time::Time; using lib::time::TimeVar; using lib::time::Duration; using lib::time::FrameRate; + namespace {// hard-wired parametrisation const size_t EPOCH_SIZ = 100; const size_t ACTIVITIES_PER_FRAME = 10; @@ -94,13 +92,13 @@ namespace gear { * 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. + * @remark rationale is to discard the Extent as a whole, once deadline passed. */ class Epoch : public Allocator::Extent { - /// @warning will be faked, not constructed + /// @warning will be faked, never constructed Epoch() = delete; public: @@ -150,19 +148,27 @@ namespace gear { } }; + + EpochGate& gate() { return static_cast ((*this)[0]); } + Time deadline() { return Time{gate().deadline()}; } + + static Epoch& - implantInto (Allocator::Extent& rawStorage) + implantInto (Allocator::iterator storageSlot) { - Epoch& target = static_cast (rawStorage); + Epoch& target = static_cast (*storageSlot); new(&target[0]) EpochGate{}; return target; } - EpochGate& - gate() + static Epoch& + setup (Allocator::iterator storageSlot, Time deadline) { - return static_cast ((*this)[0]); + Epoch& newEpoch{implantInto (storageSlot)}; + newEpoch.gate().deadline() = deadline; + return newEpoch; } + }; @@ -181,6 +187,22 @@ namespace gear { Allocator alloc_; TimeVar epochStep_; + + /** @internal use a raw storage Extent as Epoch (unchecked cast) */ + static Epoch& + asEpoch (Allocator::Extent& extent) + { + return static_cast (extent); + } + + struct StorageAdaptor : Allocator::iterator + { + StorageAdaptor(Allocator::iterator it) : Allocator::iterator{it} { } + Epoch& yield() const { return asEpoch (Allocator::iterator::yield()); } + }; + + + public: BlockFlow() : alloc_{INITIAL_ALLOC} @@ -194,6 +216,10 @@ namespace gear { } + /** Adapted storage-extent iterator, directly exposing Extent& */ + using EpochIter = lib::IterableDecorator; + + /** * Local handle to allow allocating a collection of Activities, * all sharing a common deadline. Internally, these records are @@ -203,13 +229,16 @@ namespace gear { */ class AllocatorHandle { - Allocator::iterator extent; + EpochIter epoch_; public: AllocatorHandle(Allocator::iterator slot) - : extent{slot} + : epoch_{slot} { } + /*************************************************//** + * Main API operation: allocate a new Activity record + */ template Activity& create (ARGS&& ...args) @@ -218,18 +247,12 @@ namespace gear { } private: - Epoch& - currEpoch() - { - return asEpoch(extent); - } - void* claimSlot() ///< EX_SANE { - if (currEpoch().gate().hasFreeSlot()) + if (epoch_->gate().hasFreeSlot()) { - return currEpoch().gate().claimNextSlot(); + return epoch_->gate().claimNextSlot(); } else // Epoch overflow { // use following Epoch; possibly allocate @@ -238,14 +261,16 @@ namespace gear { } }; + + /* ===== public BlockFlow API ===== */ + AllocatorHandle until (Time deadline) { if (isnil (alloc_)) {//just create new Epoch one epochStep ahead alloc_.openNew(); - Epoch& newEpoch = Epoch::implantInto (alloc_.first()); - newEpoch.gate().deadline() = deadline + Time{epochStep_}; + Epoch::setup (alloc_.begin(), deadline + Time{epochStep_}); return AllocatorHandle{alloc_.begin()}; } else @@ -258,23 +283,24 @@ namespace gear { discardBefore (Time deadline) { if (isnil (alloc_) - or asEpoch(alloc_.first()).gate().deadline() > deadline) + or firstEpoch().deadline() > deadline) return; } - private: - /** @internal use a raw allocator Extent as Epoch (unchecked cast) */ - static Epoch& - asEpoch (Allocator::iterator slot) - { - REQUIRE (bool(slot)); - return asEpoch (*slot); - } - static Epoch& - asEpoch (Allocator::Extent& extent) + + private: + Epoch& + firstEpoch() { - return static_cast (extent); + REQUIRE (not isnil (alloc_)); + return asEpoch(*alloc_.begin()); + } + Epoch& + lastEpoch() + { + REQUIRE (not isnil (alloc_)); + return asEpoch(*alloc_.last()); } @@ -300,10 +326,10 @@ namespace gear { : flow_{theFlow} { } - Time first() { return Time{BlockFlow::asEpoch(flow_.alloc_.first()).gate().deadline()}; } - Time last() { return Time{BlockFlow::asEpoch(flow_.alloc_.last() ).gate().deadline()}; } + Time first() { return flow_.firstEpoch().deadline();} + Time last() { return flow_.lastEpoch().deadline(); } size_t cntEpochs() { return watch(flow_.alloc_).active(); } - size_t poolSize() { return watch(flow_.alloc_).size(); } + size_t poolSize() { return watch(flow_.alloc_).size(); } }; inline FlowDiagnostic diff --git a/src/vault/mem/extent-family.hpp b/src/vault/mem/extent-family.hpp index 429f0de73..08f0ac940 100644 --- a/src/vault/mem/extent-family.hpp +++ b/src/vault/mem/extent-family.hpp @@ -141,7 +141,7 @@ namespace mem { void iterNext() { - exFam->incWrap (index); + index = exFam->incWrap (index); } bool @@ -150,6 +150,12 @@ namespace mem { return exFam == oi.exFam and index == oi.index; } + + + /* === pass-through extended functionality === */ + + size_t getIndex() { return index; } + void expandAlloc(){ exFam->openNew();} }; @@ -184,7 +190,7 @@ namespace mem { { if (not canAccomodate (cnt)) {//insufficient reserve => allocate - size_t oldSiz = extents_.size(); + size_t oldSiz = slotCnt(); size_t addSiz = cnt - freeSlotCnt() + EXCESS_ALLOC; // add a strike of new extents at the end @@ -202,7 +208,7 @@ namespace mem { } // now sufficient reserve extents are available ENSURE (canAccomodate (cnt)); - incWrap (after_, cnt); + after_ = incWrap (after_, cnt); } /** discard oldest \a cnt extents */ @@ -210,18 +216,13 @@ namespace mem { dropOld (size_t cnt) { REQUIRE (cnt <= activeSlotCnt()); - incWrap (start_, cnt); + start_ = incWrap (start_, cnt); } ////////////////////////////////////////////////////////////////////////////TICKET #1316 : should reduce excess allocation (with appropriate damping to avoid oscillations) /** allow transparent iteration of Extents, * with the ability to expand storage */ - struct iterator - : lib::IterStateWrapper - { - size_t getIndex() { return this->stateCore().index; } - void expandAlloc(){ this->stateCore().exFam->openNew();} - }; + using iterator = lib::IterableDecorator; /** iterate over all the currently active Extents */ iterator begin() { return iterator{IdxLink{this, start_}}; } @@ -232,8 +233,17 @@ namespace mem { bool empty() const { return start_ == after_; } - Extent& last() const { return access((after_+extents_.size()-1) % extents_.size()); } ///< @warning undefined behaviour when empty - Extent& first() const { return access(start_); } ///< @warning undefined behaviour when empty + + /** positioned to the last / latest storage extent opened + * @warning undefined behaviour when empty + */ + iterator + last() + { + REQUIRE (not empty()); // trick to safely decrement by one + size_t penultimate = incWrap (after_, slotCnt()-1); + return iterator{IdxLink{this, penultimate}}; + } private: /* ====== storage management implementation ====== */ @@ -243,24 +253,30 @@ namespace mem { return after_ < start_; } // note: both are equal only when empty + size_t + slotCnt() const + { + return extents_.size(); + } + /** @return number of allocated slots actually used */ size_t activeSlotCnt() const { - REQUIRE (start_ < extents_.size()); - REQUIRE (after_ <= extents_.size()); + REQUIRE (start_ < slotCnt()); + REQUIRE (after_ <= slotCnt()); return not isWrapped()? after_ - start_ : (after_ - 0) - +(extents_.size() - start_); + +(slotCnt() - start_); } size_t freeSlotCnt() const { // always keep one in reserve... - REQUIRE (activeSlotCnt() < extents_.size()); + REQUIRE (activeSlotCnt() < slotCnt()); - return extents_.size() - activeSlotCnt(); + return slotCnt() - activeSlotCnt(); } bool @@ -273,19 +289,19 @@ namespace mem { /** increment index, but wrap at array end. * @remark using the array cyclically */ - void - incWrap (size_t& idx, size_t inc =1) + size_t + incWrap (size_t idx, size_t inc =1) { - idx = (idx+inc) % extents_.size(); + return (idx+inc) % slotCnt(); } bool isValidPos (size_t idx) const { - REQUIRE (idx < extents_.size()); + REQUIRE (idx < slotCnt()); REQUIRE (activeSlotCnt() > 0); - return isWrapped()? (start_ <= idx and idx < extents_.size()) + return isWrapped()? (start_ <= idx and idx < slotCnt()) or idx < after_ : (start_ <= idx and idx < after_); } @@ -319,7 +335,7 @@ namespace mem { size_t first() { return exFam_.start_; } size_t last() { return exFam_.after_; } - size_t size() { return exFam_.extents_.size(); } + size_t size() { return exFam_.slotCnt(); } size_t active() { return exFam_.activeSlotCnt(); } }; diff --git a/tests/vault/mem/extent-family-test.cpp b/tests/vault/mem/extent-family-test.cpp index 9b7ba7008..6c09bbca9 100644 --- a/tests/vault/mem/extent-family-test.cpp +++ b/tests/vault/mem/extent-family-test.cpp @@ -127,7 +127,7 @@ namespace test { extents.openNew(2); // allot two extents for active use CHECK (it); CHECK (0 == it.getIndex()); - CHECK (isSameObject(*it, extents.first())); + CHECK (isSameObject(*it, *extents.begin())); Extent& extent{*it}; CHECK (10 == extent.size()); @@ -141,7 +141,7 @@ namespace test { CHECK (1 == it.getIndex()); Extent& nextEx{*it}; CHECK (not isSameObject(extent, nextEx)); - CHECK (isSameObject(nextEx, extents.last())); + CHECK (isSameObject(nextEx, *extents.last())); nextEx[5] = extent[2] + 1; CHECK (num == extent[2]); CHECK (num+1 == nextEx[5]); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index b1e0258b5..ed4c97ec9 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79815,8 +79815,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -79837,7 +79837,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79867,14 +79867,14 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -79916,6 +79916,22 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + +