diff --git a/src/vault/mem/local-buffer-store.hpp b/src/vault/mem/local-buffer-store.hpp index df842973f..9c351ebea 100644 --- a/src/vault/mem/local-buffer-store.hpp +++ b/src/vault/mem/local-buffer-store.hpp @@ -42,6 +42,31 @@ namespace mem { class LocalBufferStore : public BufferProviderSetup::Store { + struct SetupPool; + using LocalPool = lib::LocalSlice; + using GlobalPool = lib::Depend; + + struct SetupPool + { + using Service = LocalMemPool; + + static LocalMemPool + init() + { + return LocalMemPool{[](LocalMemPool& pool2close) + { + pool2close.purge([](Buff* mem, size_t siz) + { + Alloc alloc{mem,siz}; + GlobalPool globalPool; + globalPool().supply(alloc); + }); + }}; + } + }; + + LocalPool localReg_; + GlobalPool globalReg_; public: LocalBufferStore() diff --git a/src/vault/mem/local-mem-pool.hpp b/src/vault/mem/local-mem-pool.hpp index a147f0b91..36dde72f8 100644 --- a/src/vault/mem/local-mem-pool.hpp +++ b/src/vault/mem/local-mem-pool.hpp @@ -77,6 +77,7 @@ #include "vault/mem/engine-buffer-manager.hpp" #include "lib/util.hpp" +#include #include #include #include @@ -98,6 +99,7 @@ namespace mem { using std::move; using std::forward; + using std::invocable; using util::unConst; using util::isLimited; @@ -123,7 +125,8 @@ namespace mem { */ class LocalMemPool : public AllocReceiver - { + { // NonCopyable + struct Block { Alloc alloc{nullptr,0}; @@ -143,11 +146,29 @@ namespace mem { int32_t maxScore_{1}; + std::function shutdownHook_{}; + public: LocalMemPool() : AllocReceiver{} , blocks_{} { } + // NonCopyable + + template FUN> + explicit + LocalMemPool (FUN&& onShutdown) + : AllocReceiver{} + , blocks_{} + , shutdownHook_{forward (onShutdown)} + { } + + ~LocalMemPool() + { + if (shutdownHook_) + shutdownHook_(*this); + } + /* ===== Pool Access API ===== */ @@ -283,7 +304,7 @@ namespace mem { * @param siz precise size of the memory block(s) to yield * @return actual number of allocations removed from the pool. */ - template ////////TODO requires std::invocable + template FUN> uint yield (uint cnt, size_t siz, FUN&& consumer) { @@ -317,7 +338,7 @@ namespace mem { * \a degree ≡ 0.5 retains all matches that were frequently successful. * @return actual number of allocations removed from the pool. */ - template ////////TODO requires std::invocable + template FUN> uint cleanup (double degree, FUN&& consumer) { @@ -345,6 +366,23 @@ namespace mem { return removed; } + /** unconditionally dispose of the pool's entire contents. + * @warning there must not be any allocations in use when invoking this function + */ + template FUN> + uint + purge (FUN&& consumer) + { + ingest(); + ///////////////////////////////////////OOO need a way to »lock down« the inqueue at that point + for (Block& block : blocks_) + { + REQUIRE (not block.used); + consumer (block.alloc.mem, block.alloc.siz); + blocks_.clear(); + } + ENSURE (this->empty()); + } private: void diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 6dff95641..3936a6399 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -144403,7 +144403,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -144413,6 +144413,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + @@ -144995,6 +144998,72 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + +

+ ...weil es ja durch das Ende eines Thread verursacht wird, wodurch ein thread-local Subobjekt out of scope geht. +

+ +
+
+ + + + + + + +

+ ...ich hatte bereits über die Konfiguration nachgedacht, und dann für LocalSlice ein policy-based-design verwendet, allerdings in einer Form so daß es weiterhin default-constructible bleibt. Leider kann ich hier den Shutdown / clean-Up nicht einfach anbauen, da LocalSlice davon garnichts merkt. +

+ +
+
+ + + + + + +

+ eine Verdrahtung auf das managing Object ist nur indirekt (trickreich) möglich +

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

+ ...denn ich muß irgendwie einen clean-up-Hook in jede Instanz installieren, sofern ich LocalMemPool weiterhin von der sonstigen Implementierung entkoppelt haben möchte. Das bedingt dann entweder, einen Template-Parameter einzuführen, oder std::function mit einer Konstruktor-Variante. Letzteres erscheint attraktiv, da es diesen Sachverhalt etwas »unter den Teppich kehrt« und eigentlich erst auffällt, wenn man das PROD-Setup für die Engine sieht... +

+ +
+ + + + + + + + + + + + + + + @@ -145026,8 +145095,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) Es müßt nach jeder de-Allokation die ganze Blockliste gescannt werden, wobei erwartungsgemäß nach den meisten Scans nichts zur Freigabe gefunden wird

- - +
@@ -145769,8 +145837,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
- - + + @@ -146843,8 +146911,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -146902,7 +146970,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -146911,6 +146979,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

+
@@ -146944,6 +147013,18 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
+ + + + + + + + + + + + @@ -147176,8 +147257,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -147419,8 +147500,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) ...und das geht nicht mit dem fill()-Populator, denn dieser materialisiert alle Argumente als Kopien. Man könnte das ändern, müßte dann aber explizit ein perfect-forwarding-Tupel bauen. Das war mir jetzt alles zu mühsam

- - +
@@ -147430,8 +147510,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) das ist im Besonderen relevant, wenn die captures nicht-triviale Operationen in der Initialisierung verwenden. Da der fill()-Popoulator kopiert, würden sich alle Threads das gleiche Lambda teilen, und daher auf den gleichen Werten arbeiten. Das ist eine subtile Falle

- -
+