diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index ca54f5674..6734a224b 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -361,7 +361,13 @@ namespace lib { Policy (AllocationCluster& clu) - : AllocationPolicy (clu.getAllocator()) + : Base{clu.getAllocator()} + { } + + /** @note allow cross-construction from any AllocationCluster based allocator */ + template + Policy (AllocationPolicy& relatedPolicy) + : Base(relatedPolicy) { } bool diff --git a/src/steam/engine/node-builder.hpp b/src/steam/engine/node-builder.hpp index 2aeaa290b..955867244 100644 --- a/src/steam/engine/node-builder.hpp +++ b/src/steam/engine/node-builder.hpp @@ -58,7 +58,7 @@ ** specialised parametrisation for some media handling library, and a lot of cross-linking pointers, ** it is important to care for efficient usage of memory with good locality. Furthermore, the higher ** levels of the build process will generate additional temporary data structures, refined gradually - ** until the actual render node network can be emitted. Each builder level can thus be outfitted + ** until the actual render node network can be emitted. Each builder level can thus be outfitted ** with a custom allocator — typically an instance of lib::AllocationCluster. Notably the higher ** levels can be attached to a separate AllocationCluster instance, which will be discarded once ** the build process is complete, while Level-2 (and below) uses the allocator for the actual @@ -257,7 +257,7 @@ namespace engine { Connectivity build() { - PortData ports; + PortData ports{leads_.policyConnect()}; // use same allocator... patternData_.collectEntries(ports); return Connectivity{ports.build() ,leads_.build() diff --git a/tests/steam/engine/node-builder-test.cpp b/tests/steam/engine/node-builder-test.cpp index fdf8e800e..eb53aa358 100644 --- a/tests/steam/engine/node-builder-test.cpp +++ b/tests/steam/engine/node-builder-test.cpp @@ -20,6 +20,7 @@ #include "steam/engine/engine-ctx.hpp" #include "steam/engine/node-builder.hpp" #include "steam/asset/meta/time-grid.hpp" +#include "lib/allocation-cluster.hpp" #include "lib/time/timequant.hpp" #include "lib/time/timecode.hpp" #include "lib/iter-explorer.hpp" @@ -66,11 +67,12 @@ namespace test { build_Node_closedParam(); build_connectedNodes(); build_ParamNode(); + + useAllocationCluster(); } /** @test build a simple output-only Render Node - * @todo 12/24 ✔ define ⟶ ✔ implement */ void build_simpleNode() @@ -97,11 +99,10 @@ namespace test { * - expect the buffer to hold a single `uint` value after invocation */ uint - invokeRenderNode (ProcNode& theNode, Time nomTime =Time::ZERO) + invokeRenderNode (ProcNode& theNode, Time nomTime =Time::ZERO, uint port =0) { BuffHandle buff = EngineCtx::access().mem.lockBufferFor (-55); ProcessKey key{0}; - uint port{0}; CHECK (-55 == buff.accessAs()); @@ -115,7 +116,6 @@ namespace test { /** @test build a Node with a fixed invocation parameter - * @todo 12/24 ✔ define ⟶ ✔ implement */ void build_Node_fixedParam() @@ -139,7 +139,6 @@ namespace test { * the time into an implicitly defined grid * - install both into a render node * - set a random _nominal time_ for invocation - * @todo 12/24 ✔ define ⟶ ✔ implement */ void build_Node_dynamicParam() @@ -171,7 +170,6 @@ namespace test { * - again use a processing function which takes a parameter * - but then _decorate_ this functor, so that it takes different arguments * - attach parameter handling to supply these adapted arguments - * @todo 2/25 ✔ define ⟶ ✔ implement */ void build_Node_adaptedParam() @@ -239,7 +237,6 @@ namespace test { * - use the »simplified 1:1 wiring«, which connects consecutively * each input slot to the next given node on the same port number; * here we only use port#0 on all three nodes. - * @todo 12/24 ✔ define ⟶ ✔ implement */ void build_connectedNodes() @@ -287,7 +284,6 @@ namespace test { * - perform effectively the same computation as the preceding test * - but use two new custom parameters in the Param Agent Node * - pick them up from the nested source nodes by accessor-functors - * @todo 12/24 ✔ define ⟶ ✔ implement */ void build_ParamNode() @@ -343,6 +339,49 @@ namespace test { CHECK (res == peek+1 + LIFE_AND_UNIVERSE_4EVER+1 ); CHECK (peek != -1); } + + + + /** @test demonstrate use of a custom allocator + * - use local AllocationCluster instance for this test + * - place and manage the resulting ProcNode in this cluster + * - furthermore, allocate administrative internals also there + * - build a Node with two ports, each with a parameter and output buffer + * - demonstrate the created Node works as expected + * - verify that allocations happened + */ + void + useAllocationCluster() + { + auto procFun = [](ushort param, uint* buff){ *buff = param; }; + + lib::AllocationCluster alloc; + CHECK (0 == alloc.numBytes()); + + auto& node = alloc.create( // ◁───────────────────────────────────┨ place resulting ProcNode into the cluster + prepareNode("Test") + .withAllocator(alloc) // ◁───────────────────────────────────┨ policy to use this cluster for internal allocations + .preparePort() + .invoke ("funA()", procFun) + .setParam (LIFE_AND_UNIVERSE_4EVER) + .completePort() + .preparePort() + .invoke ("funB()", procFun) + .setParam (LIFE_AND_UNIVERSE_4EVER + 13) + .completePort() + .build() + ); + + CHECK (LIFE_AND_UNIVERSE_4EVER == invokeRenderNode (node, Time::ZERO, 0)); + CHECK (LIFE_AND_UNIVERSE_4EVER+13 == invokeRenderNode (node, Time::ZERO, 1)); + + // minimum bounds of memory allocations... + const auto MIN_EXPECT = sizeof(ProcNode) + + 2 * (sizeof(Port) + sizeof(BuffDescr) // defined two ports, each with an output type... + + 2*(sizeof(void*) + sizeof(std::function))); // and parameter + buffer + proc-fun and param-fun + + CHECK (MIN_EXPECT < alloc.numBytes()); + } }; diff --git a/tests/steam/engine/node-opera-test.cpp b/tests/steam/engine/node-opera-test.cpp index 04d6d0fcf..dc3e9a030 100644 --- a/tests/steam/engine/node-opera-test.cpp +++ b/tests/steam/engine/node-opera-test.cpp @@ -32,7 +32,7 @@ #include "lib/depend-inject.hpp" #include "lib/thread.hpp" //#include "lib/symbol.hpp" -//#include "lib/util.hpp" +#include "lib/util.hpp" #include "test/diagnostic-output.hpp"///////////////TODO @@ -52,6 +52,7 @@ using steam::asset::meta::TimeGrid; //using lib::Symbol; //using util::isnil; //using util::isSameObject; +using util::isLimited; using test::TestFrame; using test::testRand; namespace ont = test::ont; @@ -314,6 +315,7 @@ namespace test { // A source node to produce random test frames auto& n1s = alloc_.create( prepareNode("srcA") + .withAllocator(alloc_) .preparePort() .invoke(testGen.procID(), testGen.makeFun()) // params(frameNo, flavour) .closeParam<1>(SRC_A) // --> fix the flavour ≔ SRC_A @@ -324,6 +326,7 @@ namespace test { // A node to »filter« the data in chain-A auto& n1f = alloc_.create( prepareNode("filterA") + .withAllocator(alloc_) .preparePort() .invoke(testMan.procID(), testMan.makeFun()) .attachParamFun(autoFilter) // filter-param <-- autoFilter(frameNo) @@ -335,6 +338,7 @@ namespace test { // A secondary source Node for the »chain-B« auto& n2s = alloc_.create( prepareNode("srcB") + .withAllocator(alloc_) .preparePort() .invoke(testGen.procID(), testGen.makeFun()) // params(frameNo, flavour) .closeParam<1>(SRC_B) // --> fix the flavour ≔ SRC_B @@ -346,6 +350,7 @@ namespace test { // A mixing Node to combine both chains auto& mix = alloc_.create( prepareNode("mix") + .withAllocator(alloc_) .preparePort() .invoke(testMix.procID(), testMix.makeFun()) .attachParamFun(autoMixer) // mixer-param <-- autoMixer(frameNo) @@ -358,12 +363,16 @@ namespace test { // Place a »Param-Agent«-Node on top to pre-compute the FrameNo auto& parNode = alloc_.create( prepareNode("Param") + .withAllocator(alloc_) .preparePort() .computeParam(paramSpec) .delegateLead(mix) .completePort() .build()); + // the AllocationCluster managed some storage... + CHECK (isLimited (1_KiB, alloc_.numBytes(), 5_KiB)); + // here the »Param-Agent« also acts as the Exit-Node return parNode; } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 0b79a1370..c22aed7dc 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -116724,15 +116724,255 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + + - - + + + + + + + + + + + + + + + + +

+ mir fällt auf: das hab ich noch nie getestet +

+ + +
+ + + + + +

+ Wie konnte das passieren....? +

+

+ Vermutlich habe ich es genau auf diesen NodeOperation-Test hier verschoben, weil ich bisher noch gar nicht die Möglichkeit hatte, das ganze Setup im Zusammenhang zu testen.... +

+

+ Nun ja, man hätte dafür schon einen Testfall im NodeBuilder_test einfügen können ⟹ das sollte ich dann auch tun +

+

+ +

+

+ Wie dem auch sei — ich hab mich auf den Standpunkt gestellt, daß dieser ganze Aufbau stark typisiert ist, und deshalb funktionieren muß, sobald er durch den Compiler geht — unter der Vorraussetzung daß die Basis-Technologie funktioniert (und das wäre lib::Several mit AllocationCluster, was in der Tat getestet ist) +

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

+ ...insofern er identisch ist zu der direkten Verwendung auf lib::SeveralBuilder, welche detailliert per Unit-Test abgedeckt ist +

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

+ denn speziell beim AllocationCluster ist die Lage besonders, da ein dedizierter Adaptor zum Einsatz kommt, in dessen Template-Parameter der Zieltyp eingeht; insofern ist dieser Adapter nicht  cross-constructible, obwohl die unterliegende AllocatorCluster-Instanz gleich wäre +

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

+ geht einfach, da die Adapter-Policy letztlich nur eine AllocatorPolicy mit dem speziellen Allokator erweitert; insofern ist nur der außen explizit definierte Konstruktor das Problem; aus Gründen der Klarheit ziehe ich es trotzdem vor, den cross-ctor ebenfalls explizit zu definieren, dann aber lediglich an die Basisklasse zu delegieren (die dann selbst auf den unterliegenden Allokator zugreifen kann) +

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

+ weiteres Problem: Allocator ist hier explizit nicht monostate +

+ + +
+ + + + + + + +

+ denn wir verwenden einen bestimmten AllocationCluster +

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

+ src/steam/engine/node-builder.hpp:260:20: error: use of deleted function 'lib::SeveralBuilder<I, E, POL>::SeveralBuilder() [with I = steam::engine::Port; E = steam::engine::Port; POL = lib::allo::SetupSeveral<std::void_t, lib::AllocationCluster&>::Policy]' +

+

+ 260 | PortData ports; +

+

+ | ^~~~~ +

+

+ In file included from src/steam/engine/weaving-pattern-builder.hpp:135, +

+

+ from src/steam/engine/node-builder.hpp:123: +

+

+ src/lib/several-builder.hpp:407:7: note: 'lib::SeveralBuilder<I, E, POL>::SeveralBuilder() [with I = steam::engine::Port; E = steam::engine::Port; POL = lib::allo::SetupSeveral<std::void_t, lib::AllocationCluster&>::Policy]' is implicitly deleted because the default definition would be ill-formed: +

+

+ 407 | SeveralBuilder() = default; +

+

+ | ^~~~~~~~~~~~~~ +

+

+ src/lib/several-builder.hpp:407:7: error: no matching function for call to 'lib::allo::SetupSeveral<std::void_t, lib::AllocationCluster&>::Policy<steam::engine::Port, steam::engine::Port>::Policy()' +

+

+ +

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

+ ....denn die Leads sind im Objekt bereits instantiiert, und werden nicht erst im Build-Vorgang erzeugt +

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

+ ...denn... +

+
    +
  • + der Aufbau im WeavingPattern ist komplexer als ich im Moment leicht nachvollziehen kann +
  • +
  • + was ich sofort sehe ist die Storage für die Connectivity-Komponenten +
  • +
  • + sowie die Feed-Funktore +
  • +
  • + oben drauf kommt, daß meine lib::Several-Implementierung noch verschwenderisch ist +
  • +
+ + +
+
+
+
+
+
+ @@ -116744,8 +116984,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) +
+ + +
@@ -116761,6 +117005,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + +