diff --git a/src/vault/mem/buffer-provider.cpp b/src/vault/mem/buffer-provider.cpp index e68778be0..4fbd1bbf6 100644 --- a/src/vault/mem/buffer-provider.cpp +++ b/src/vault/mem/buffer-provider.cpp @@ -134,6 +134,9 @@ namespace mem { * automatic tracking and clean-up. The client is explicitly * responsible to invoke #releaseBuffer (either on the * BufferProvider, or directly on the BuffHandle). + * @param type the _generic buffer type_ to base this buffer allocation on; + * at minimum this implicitly defines the buffer size, possibly also a TypeHandler + * @param arg (optional) custom argument to pass-through to the backing buffer manager * @return a copyable handle, representing this buffer and this usage transaction. * @throw error::State when unable to provide this buffer * @note this function may be used right away, without prior announcing, but then @@ -146,11 +149,11 @@ namespace mem { * return a metadata::Entry */ BuffHandle - BufferProvider::lockBuffer (BuffDescr const& type) + BufferProvider::lockBuffer (BuffDescr const& type, int64_t arg) { REQUIRE (was_created_by_this_provider (type)); auto& typeKey = bufferStage_->lookup (type); - BuffAlloc alloc= bufferStore_->provideBuffer (type,typeKey.storageSize(),typeKey.localTag()); + BuffAlloc alloc= bufferStore_->provideBuffer (type,typeKey.storageSize(),typeKey.localTag(),arg); auto& stateKey = bufferStage_->mark_locked (typeKey, alloc); return BuffHandle (buildDescriptor(stateKey), getAddr(alloc)); @@ -299,15 +302,15 @@ namespace mem { BuffDescr::announce (uint count) { ENSURE (provider_); - return provider_->announce(count, *this); + return provider_->announce (count, *this); } BuffHandle - BuffDescr::lockBuffer() + BuffDescr::lockBuffer (int64_t arg) { ENSURE (provider_); - return provider_->lockBuffer(*this); + return provider_->lockBuffer (*this, arg); } diff --git a/src/vault/mem/buffer-provider.hpp b/src/vault/mem/buffer-provider.hpp index 1a6a03db7..4ba4f8b99 100644 --- a/src/vault/mem/buffer-provider.hpp +++ b/src/vault/mem/buffer-provider.hpp @@ -182,7 +182,7 @@ namespace mem { uint announce (uint count, BuffDescr const&); /** claim storage for exclusive use, based on a descriptor */ - BuffHandle lockBuffer (BuffDescr const&); + BuffHandle lockBuffer (BuffDescr const&, int64_t=0); /** directly claim storage and construct an object, * without preparing a descriptor beforehand */ @@ -236,10 +236,10 @@ namespace mem { public: virtual ~BufferStore() { } ///< this is an interface - virtual uint prepareBuffers (HashVal typeID, uint cnt, size_t) =0; - virtual BuffAlloc provideBuffer (HashVal typeID, size_t, LocalTag) =0; - virtual void mark_emitted (HashVal typeID, BuffAlloc) =0; - virtual void detachBuffer (HashVal typeID, BuffAlloc) =0; + virtual uint prepareBuffers (HashVal typeID, uint cnt,size_t) =0; + virtual BuffAlloc provideBuffer (HashVal typeID, size_t,LocalTag,int64_t) =0; + virtual void mark_emitted (HashVal typeID, BuffAlloc) =0; + virtual void detachBuffer (HashVal typeID, BuffAlloc) =0; }; unique_ptr bufferStage_; diff --git a/src/vault/mem/buffer-proxy-adaptor.hpp b/src/vault/mem/buffer-proxy-adaptor.hpp index 9584e035b..d92638a13 100644 --- a/src/vault/mem/buffer-proxy-adaptor.hpp +++ b/src/vault/mem/buffer-proxy-adaptor.hpp @@ -87,7 +87,7 @@ namespace mem { } BuffAlloc - provideBuffer (HashVal,size_t siz,LocalTag targetMarker) override + provideBuffer (HashVal,size_t siz,LocalTag targetMarker, int64_t customArg) override { BuffAlloc storageSlot{asBuffer(targetMarker), siz, targetMarker}; CONF::on_lock (storageSlot); diff --git a/src/vault/mem/buffhandle.hpp b/src/vault/mem/buffhandle.hpp index fe987f84b..9ce4dae1e 100644 --- a/src/vault/mem/buffhandle.hpp +++ b/src/vault/mem/buffhandle.hpp @@ -115,7 +115,11 @@ namespace mem { /** callback to engage buffer handling cycle */ uint announce (uint count); - BuffHandle lockBuffer(); + + /** claim an actual buffer for exclusive use. + * @param arg a custom argument to pass to the buffer manager + */ + BuffHandle lockBuffer (int64_t arg=0); }; diff --git a/src/vault/mem/heap-mem-buffer-store.cpp b/src/vault/mem/heap-mem-buffer-store.cpp index 8db5c8d8c..13b5d4527 100644 --- a/src/vault/mem/heap-mem-buffer-store.cpp +++ b/src/vault/mem/heap-mem-buffer-store.cpp @@ -289,7 +289,7 @@ namespace mem { BuffAlloc - HeapMemBufferStore::provideBuffer (HashVal typeID, size_t buffSiz, LocalTag) + HeapMemBufferStore::provideBuffer (HashVal typeID, size_t buffSiz, LocalTag, int64_t) { BlockPool& blocks = getBlockPoolFor (buffSiz, typeID); Block& newBlock = blocks.createBlock(); diff --git a/src/vault/mem/heap-mem-buffer-store.hpp b/src/vault/mem/heap-mem-buffer-store.hpp index cffac8b44..b87725c51 100644 --- a/src/vault/mem/heap-mem-buffer-store.hpp +++ b/src/vault/mem/heap-mem-buffer-store.hpp @@ -66,10 +66,10 @@ namespace mem { public: /* === BufferStore interface === */ - virtual uint prepareBuffers (HashVal typeID, uint cnt, size_t) override; - virtual BuffAlloc provideBuffer (HashVal,size_t,LocalTag) override; - virtual void mark_emitted (HashVal typeID, BuffAlloc) override; - virtual void detachBuffer (HashVal typeID, BuffAlloc) override; + uint prepareBuffers (HashVal typeID, uint cnt, size_t) override; + BuffAlloc provideBuffer (HashVal,size_t,LocalTag,int64_t) override; + void mark_emitted (HashVal typeID, BuffAlloc) override; + void detachBuffer (HashVal typeID, BuffAlloc) override; public: ~HeapMemBufferStore(); diff --git a/src/vault/out/diagnostic-output-slot.hpp b/src/vault/out/diagnostic-output-slot.hpp index fc96931fa..924cc0c84 100644 --- a/src/vault/out/diagnostic-output-slot.hpp +++ b/src/vault/out/diagnostic-output-slot.hpp @@ -135,19 +135,21 @@ namespace out { /* === Connection API === */ - BuffHandle - claimBufferFor(FrameID frameNr) + Buff* + claimBufferFor (FrameID frameNr) override { +#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring TRACE (test, "Con=%p : lock buffer for frame-#%lu", this, frameNr); REQUIRE (!closed_); return trackFrame (frameNr, buffProvider_.lockBuffer (bufferType_)); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code } bool - isTimely (FrameID frameNr, TimeValue currentTime) + isTimely (FrameID frameNr, TimeValue currentTime) override { TRACE (test, "Con=%p : timely? frame-#%lu" , this, frameNr); @@ -159,32 +161,38 @@ namespace out { } void - transfer (BuffHandle & filledBuffer) + transfer (Buff* filledBuffer) override { +#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring TRACE (test, "Con=%p : transfer buffer %zu" , this, HashVal(filledBuffer)); REQUIRE (!closed_); pushout (filledBuffer); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code } void - pushout (BuffHandle & data4output) + pushout (Buff* data4output) override { +#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring REQUIRE (!closed_); buffProvider_.emitBuffer (data4output); buffProvider_.releaseBuffer(data4output); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code } void - discard (BuffHandle & superseededData) + discard (Buff* superseededData) override { +#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring REQUIRE (!closed_); buffProvider_.releaseBuffer (superseededData); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code } void - shutDown () + shutDown() override { closed_ = true; } diff --git a/src/vault/out/output-buffer-proxy.hpp b/src/vault/out/output-buffer-proxy.hpp index 344ca4ded..cb02bfe94 100644 --- a/src/vault/out/output-buffer-proxy.hpp +++ b/src/vault/out/output-buffer-proxy.hpp @@ -66,7 +66,7 @@ namespace out { * - callback functors for the lifecycle stages related to the client's access * @todo 3/2026 this is prototyping code and was retained for demonstration purposes. */ - template + template class OutputBufferProxy : public BufferProviderSetup { @@ -74,13 +74,11 @@ namespace out { class OutputBufferStore : public BufferProviderSetup::Store { - HUB& output_; - - Buff* - asBuffer (LocalTag targetMarker) + CON& + asTarget (LocalTag targetMarker) { - void* buffMem{targetMarker}; - return static_cast (buffMem); + void* targetAdr{targetMarker}; + return * static_cast (targetAdr); } uint @@ -91,9 +89,10 @@ namespace out { } BuffAlloc - provideBuffer (HashVal,size_t siz,LocalTag targetMarker) override + provideBuffer (HashVal,size_t siz, LocalTag targetMarker, int64_t frameNr) override { - BuffAlloc storageSlot{asBuffer(targetMarker), siz, targetMarker}; + Buff* buffer = asTarget(targetMarker).claimBufferFor (frameNr); + BuffAlloc storageSlot{buffer, siz, targetMarker}; TODO ("delegate buffer lock"); return storageSlot; } @@ -109,19 +108,12 @@ namespace out { { TODO ("delegate buffer release"); } - - public: - OutputBufferStore(HUB& outputHub) - : output_{outputHub} - { } }; struct Setup { - HUB& outputHub_; - auto buildStage() { return std::make_unique("OutputBufferProxy"); } - auto buildStore() { return std::make_unique (outputHub_); } + auto buildStore() { return std::make_unique(); } }; BuffDescr @@ -135,8 +127,8 @@ namespace out { public: - OutputBufferProxy(HUB& outputHub) - : BufferProviderSetup{Setup{outputHub}} + OutputBufferProxy() + : BufferProviderSetup{Setup{}} { } diff --git a/src/vault/out/output-slot-connection.hpp b/src/vault/out/output-slot-connection.hpp index 5b344f4ac..b4ee5dc7b 100644 --- a/src/vault/out/output-slot-connection.hpp +++ b/src/vault/out/output-slot-connection.hpp @@ -54,6 +54,7 @@ namespace vault { namespace out { + using vault::mem::Buff; using vault::mem::BuffHandle; using vault::mem::BufferProvider; using lib::time::TimeValue; /////////////////OOO Rly? @@ -94,12 +95,12 @@ namespace out { public: virtual ~Connection(); - virtual BuffHandle claimBufferFor(FrameID) =0; - virtual bool isTimely (FrameID, TimeValue) =0; - virtual void transfer (BuffHandle &) =0; - virtual void pushout (BuffHandle &) =0; - virtual void discard (BuffHandle &) =0; - virtual void shutDown () =0; + virtual Buff* claimBufferFor(FrameID) =0; + virtual bool isTimely (FrameID, TimeValue) =0; + virtual void transfer (Buff*) =0; + virtual void pushout (Buff*) =0; + virtual void discard (Buff*) =0; + virtual void shutDown () =0; }; @@ -127,7 +128,7 @@ namespace out { { using Connections = lib::ScopedCollection; using OpenedSinks = OutputSlot::OpenedSinks; - using BufferProxy = OutputBufferProxy; + using BufferProxy = OutputBufferProxy; Connections connections_; BufferProxy bufferProxy_; @@ -171,7 +172,7 @@ namespace out { template AllocState(uint numChannels, CTOR&& populator) : connections_(numChannels, std::forward(populator)) - , bufferProxy_{*this} + , bufferProxy_{} { } diff --git a/tests/steam/play/dummy-builder-context.hpp b/tests/steam/play/dummy-builder-context.hpp index 35f80d2e3..f34983fa8 100644 --- a/tests/steam/play/dummy-builder-context.hpp +++ b/tests/steam/play/dummy-builder-context.hpp @@ -67,6 +67,7 @@ namespace play { namespace test { using fixture::ModelPortRegistry; + using vault::mem::Buff; using vault::mem::BuffHandle; using vault::out::DataSink; using vault::out::FrameID; @@ -79,11 +80,11 @@ namespace test { class UnimplementedConnection : public vault::out::OutputSlot::Connection { - BuffHandle claimBufferFor (FrameID) override; + Buff* claimBufferFor (FrameID) override; bool isTimely (FrameID, TimeValue) override { return true; } - void transfer (BuffHandle &) override { UNIMPLEMENTED ("transfer (BuffHandle const&)"); } - void pushout (BuffHandle &) override { UNIMPLEMENTED ("pushout (BuffHandle const&)"); } - void discard (BuffHandle &) override { UNIMPLEMENTED ("discard (BuffHandle const&)"); } + void transfer (Buff*) override { UNIMPLEMENTED ("transfer (BuffHandle const&)"); } + void pushout (Buff*) override { UNIMPLEMENTED ("pushout (BuffHandle const&)"); } + void discard (Buff*) override { UNIMPLEMENTED ("discard (BuffHandle const&)"); } void shutDown () override { UNIMPLEMENTED ("shutDown() Connection"); } public: @@ -199,7 +200,7 @@ namespace test { } }; - inline vault::mem::BuffHandle UnimplementedConnection::claimBufferFor (FrameID) + inline vault::mem::Buff* UnimplementedConnection::claimBufferFor (FrameID) { UNIMPLEMENTED ("claimBufferFor(FrameID)"); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index a6ef79898..ab9635a93 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -5157,17 +5157,17 @@ Thus the mapping is a copyable value object, using an associative array. It may First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit. -
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities (video in GUI window, video fullscreen, using a beamer, sound, Jack, rendering to file) can be added and removed dynamically from various components (Vault, Stage), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
 !Properties of an output slot
-Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right soundcard output channels), or an output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
+Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right sound card output channels), or an output file or similar capability accepting media content. The setup for an {{{OutputSlot}}} is provided, configured and registered with an OutputManager, using an implementation suitable for the kind of media data to be sent (sound, video) and also suitable for the desired kind if output capability (render to file, display video in a GUI widget, send video to a full screen display, establish a Jack port or use any kind of "sound out"). Thus it is always limited to a single kind of media, and to a single connection unit, but this connection may still comprise several data feeds (e.g. stereoscopic video, multichannel sound). The number of such feeds and the expected data format depends on the _media stream type_ used for configuring or retrieving this OutputSlot. For example, a stereo sound output may expect only a single data feed, where data blocks for both channels are sent in interleaved format.
 
-In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (in special cases, some external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
+Once the client retrieves the OutputSlot for some desired output capability, the slot was already _allocated_ for exclusive use for that client. At any time, there may be only a single client using a given output capability this way. It should be noted thus that output slots don't provide any kind of //inherent mixing capability;// any adaptation, mixing, overlaying and sharing needs to be done within the Render Node Network that produces the data to be output. (As an aside, //some// external output frameworks — e.g. the Jack audio connection system — may still provide additional mixing capabilities, yet these are beyond the scope of the Lumiera application)
 
 [>img[Outputslot implementation structures|uml/OutputSlot_components.svg]]
-Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames are assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these [[timings|Timings]] when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Based on this information, the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
+The {{{OutputSlot}}} object is a (copyable) front-end handle, that should be stored by the client and discarded when further output is no longer required. From the OutputSlot, a set of actual ''data sink handles'' can be retrieved (one for each possible data feed). Starting from these handles, a ''two-phase output protocol'' can be executed for each frame of data to send to the output, which includes to obtain a BuffHandle, similar to what the BufferProvider exposes. The suitability of the buffer and the data format is not validated in any way at that level, since the setup works under the assumption that the StreamType and further metadata has been validated or configured suitably at a higher level. However, the OutputSlot exposes a definition of [[timing constraints|Timings]] to describe when data must be provided, and what deadline to observe for each frame. The client is required to comply to these {{{vault::out::Timings}}} both when acquiring the BuffHandle for some frame and when ''emitting'' data -- any use outside these limits will cause that frame to be treated as a glitch. Furthermore, the client is required to invoke {{{BuffHandle::release()}}} as soon as no further access to the buffer is required (and after invoking `emit()`). Failure to do so will cause a blocked buffer and typically leads to abort of the output and play process. However, individual frames that miss the deadline are handled gracefully; the actual implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically whenever a timing failure is detected.
 
 !!!usage and implementation
 Clients retrieve a reference to an output slot by asking a suitable OutputManager for an output possibility that supports some specific media and format. Usually, they just "claim" this slot by invoking {{{allocate()}}}, which causes actual output connections to be built and data handover mechanisms to be established, behind the scenes. For each such connection -- corresponding to a single distinct data feed within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The feed may, or may not correspond to a nominal channel in the media format, since some output interfaces require to deliver the data in separate buffers per channel, while others require data to be interleaved. Generally speaking, the concrete ~OutputSlot implementation performs operations rather specific to the kind of output and external interface in question -- thereby calling directly into a specific connection implementation. The »''Output Slot Protocol''« however is designed in a way to abstract from all those technical details.
@@ -6265,7 +6265,7 @@ This is the core service provided by the player subsystem. The purpose is to cre
 :any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
 
-
+
//Integration effort to promote the development of rendering, playback and video display in the GUI//
 This IntegrationSlice was started in {{red{2023}}} as [[Ticket #1221|https://issues.lumiera.org/ticket/1221]] to coordinate the completion and integration of various implementation facilities, planned, drafted and built during the last years; this effort marks the return of development focus to the lower layers (after years of focussed UI development) and will implement the asynchronous and time-bound rendering coordinated by the [[Scheduler]] in the [[Vault|Vault-Layer]]
 
@@ -6312,6 +6312,8 @@ __February.25__: built extended verification mechanisms, including some Node met
 
 __January.26__: During the last nine months, unfortunately I was occupied with generic clean-up, upgrade, infrastructure and research work -- and so there was ''no progress'' regarding the PlaybackVerticalSlice. Just before that recess, an important breakthrough was achieved: a complete node graph was built and invoked end to end in a unit test setting. The further integration up to a RenderJob invocation is still blocked by some yet unresolved [[design mismatch (#1395)|https://issues.lumiera.org/ticket/1395]] related to the //»BufferProvider Protocol«// vs the //»OutputSlot Protocol«//; this is where I'll pick up the development work stalled almost a year ago.
 
+__March.26__: Meanwhile I was able to get at the root of the confusion related to BufferProvider and OutputSlot. This insight allows to rectify and realign the architecture, while largely keeping the APIs and both »Protocols« intact. As part of this design revalidation, I did an in-depth re-assessment of buffer metadata handling and got a much better understanding of the requirements for an actual buffer manager implementation -- and it is clear now that at least such a basic implementation is required to achieve a first engine integration. Furthermore, I was indeed able to build a proxy-BufferProvider that can be used as façade within the OutputSlot Protocol, so that both Protocols can be reconciled. The integration of this new proxy implementation necessitates a deeper refactoring within OutputSlot though, leading to a much better and cleaner design overall.
+
 !Decisions
 ;Scheduler
 :is understood as a high-level Service, not a bare bone implementation mechanism
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index ec683524d..0e6949235 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -138551,7 +138551,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
 
 
 
-
+
 
 
 
@@ -138655,7 +138655,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
   
 
 
-
+
 
 
 
@@ -138689,6 +138689,105 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
 
 
 
+
+
+
+
+  
+  
+    

+ das Connection-Interface (Entwurf) verlangt eine Frame-Nr für claimBufferFor(frame) +

+ +
+ +
+ + + + + + +

+ das wäre die schöne und elegante Lösung die ich mir heute ausgedacht habe (und dabei die Frame-Nr übersehen) +

+ +
+
+ + + + +

+ das wäre die offensichtliche und dämliche Lösung: man verdrahtet einen back-Link auf den AllocState, und gibt einen Index in die Connection-Tafel über das LocalTag durch. +

+ +
+
+ + + + +

+ Deshalb habe ich die zunächst gebaute Lösung mit dem BufferProxyAdaptor aufgegeben: denn in den einzelnen Lifecycle-Methoden weiß man dann nicht, zu welcher Connection der Callback gehört. Man müßte dann im AllocState eine reverse-Index-Tabelle aufbauen, mit der man von der Buffer-Adresse zur passenden Connection zurückfindet. Prinzipiell wäre das möglich (da Buffer-Adressen global eindeutig sind und daher niemals gleichzeitig von zwei verschiedenen Connections verwendet werden können .... allerdings damit hörts auch schon auf, denn später könnte durchaus eine andere Connection den gleichen Buffer verwenden) +

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

+ Das API ist nicht stateless, da Buffer belegt und verwaltet werden. Das ist essntieller Zustand. Insofern liefert der claimBufferFor(frame)-Aufruf eine Buffer-Adresse, und die weiteren Aufrufe beziehen sich darauf. +

+ +
+
+
+ + + + +

+ Auf den ersten Blick ist natürlich Lösung-2 viel attraktiver, denn das ist schnell reingehackt, und dann funktioniert alles wie bisher. Hinsichtlich Interface-Design wäre das aber eine Bankrott-Erklärung; jetzt geb ich mir so viel Mühe, da ein klares Design zu schaffen, und dann führe ich eine solche faule Konvention ein, die beim Implementatior jede Menge Fragen aufwirft, und praktisch dazu zwingt, die Implementierung des Aufrufers (OutputSlot::AllocState<CON> bzw. OutputBufferProxy) im Detail zu durchdringen (⟹ Kopplung). +

+

+ Dem gegenüber ist Lösung-1 zwar nicht schön, aber akzeptabel; letztlich schaffen wir uns ein Interface immer zu einem Zweck, und hier verlangt der Zweck eben, eine weitere Info zum Buffer-Manager durchzuleiten (ohne sie direkt in die Metadaten zu speichern). SowaskommtindenbestenFamilienvor. Zudem kann ich das Argument defaulten. Ich bin sogar geneigt, das ganz offiziell auch auf das Buffer-Descripter-API zu heben: aus Gründen der Symmetrie, und weil wir uns damit den zusätzlichen Pointer sparen können) +

+ +
+ + +
+
+