BufferProvider: a proxy implementation mechanism for BufferProvider internals
Use a generic helper function in the `BufferProviderSetup` base class to allow layering of a decorator implementation on top of an already instantiated implementation class. After the preceding internal reorganisation, it suffices for the `DiagnosticBufferProvider` to instrument the `BufferStage` interface, in order to capture and record all relevant buffer state transitions
This commit is contained in:
parent
6de1837042
commit
43c4f7a518
4 changed files with 139 additions and 25 deletions
|
|
@ -83,6 +83,39 @@ namespace engine {
|
|||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Enable subclasses to layer a decorator on top of
|
||||
* an already instantiated implementation object
|
||||
* @tparam IMP concrete type of the implementation decorator; must be specified.
|
||||
* @tparam API the API to decorate; the decorator IMP must implement this API
|
||||
* @tparam ARGS (optionally) further constructor arguments for the decorator
|
||||
* @param rawImp the `uniqe_ptr` with an already instantiated implementation of API
|
||||
* @note the decorator subclass is assumed to incorporate an unique_ptr&&, holding
|
||||
* and managing the underlying implementation from that point on
|
||||
* @warning the new decorator implementation is responsible for the overall sane state,
|
||||
* starting from that point when it picks up the given unique_ptr. An exception
|
||||
* emanating after that point will not leak memory, but destroy the original
|
||||
* implementation and thus leave the whole BufferProvider in undefined state.
|
||||
*/
|
||||
template<class IMP, class API, typename...ARGS>
|
||||
void
|
||||
decorate (unique_ptr<API>& rawImp, ARGS&& ...furtherDecoratorArgs)
|
||||
{
|
||||
unique_ptr<API> underlying;
|
||||
try {
|
||||
REQUIRE (rawImp);
|
||||
std::swap (rawImp, underlying);
|
||||
ENSURE (not rawImp);
|
||||
rawImp = make_unique<IMP> (move(underlying), forward<ARGS> (furtherDecoratorArgs)...);
|
||||
ENSURE (rawImp);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
REQUIRE (not rawImp);
|
||||
REQUIRE (underlying);
|
||||
std::swap (rawImp, underlying);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@
|
|||
|
||||
|
||||
/** @file diagnostic-buffer-provider.cpp
|
||||
** Implementation details of unit test support regarding internals of the render engine.
|
||||
** Implementation details for an instrumented BufferProvider to support
|
||||
** verifying render engine buffer allocation schemes through unit tests.
|
||||
**
|
||||
** @todo WIP from 2013, half finished, stalled
|
||||
** @see buffer-provider-protocol-test.cpp
|
||||
*/
|
||||
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
#include "lib/util.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace steam {
|
||||
|
|
@ -39,6 +40,7 @@ namespace engine {
|
|||
using util::unConst;
|
||||
using diagn::Block;
|
||||
using diagn::StateReg;
|
||||
using std::unique_ptr;
|
||||
using std::ranges::find_if;
|
||||
|
||||
|
||||
|
|
@ -92,11 +94,78 @@ namespace engine {
|
|||
}
|
||||
|
||||
|
||||
class DiagnosticBufferProvider::InstrumentedStageProxy
|
||||
: public BufferProviderSetup::Stage
|
||||
{
|
||||
using StageImp = unique_ptr<BufferProvider::BufferStage>;
|
||||
using Tracker = DiagnosticBufferProvider::BlockTracker;
|
||||
|
||||
StageImp stage_;
|
||||
Tracker& tracker_;
|
||||
|
||||
|
||||
/* === BufferStage proxy implementation === */
|
||||
|
||||
ID
|
||||
lookup (HashVal key) override
|
||||
{
|
||||
return stage_->lookup (key);
|
||||
}
|
||||
|
||||
bool
|
||||
isAccessible (HashVal stateKey) const override
|
||||
{
|
||||
return stage_->isAccessible (stateKey);
|
||||
}
|
||||
|
||||
ID
|
||||
defineBufferType (size_t buffSiz, TypeHandler handlerFunctions)
|
||||
{
|
||||
return stage_->defineBufferType (buffSiz, move(handlerFunctions));
|
||||
}
|
||||
|
||||
ID
|
||||
mark_locked (ID typeKey, Buff* storage, LocalTag implMark) override
|
||||
{
|
||||
return stage_->mark_locked (typeKey, storage, implMark);
|
||||
}
|
||||
|
||||
ID
|
||||
mark_emitted (HashVal stateKey) override
|
||||
{
|
||||
return stage_->mark_emitted (stateKey);
|
||||
}
|
||||
|
||||
ID
|
||||
mark_released (HashVal stateKey) override
|
||||
{
|
||||
return stage_->mark_released (stateKey);
|
||||
}
|
||||
|
||||
ID
|
||||
abandon (HashVal stateKey, bool invokeDtor) override
|
||||
{
|
||||
return stage_->abandon (stateKey, invokeDtor);
|
||||
}
|
||||
|
||||
void
|
||||
discard (HashVal stateKey) override
|
||||
{
|
||||
stage_->discard (stateKey);
|
||||
}
|
||||
|
||||
public:
|
||||
InstrumentedStageProxy (StageImp rawImpl, Tracker& tracker)
|
||||
: stage_{move(rawImpl)}
|
||||
, tracker_{tracker}
|
||||
{ }
|
||||
};
|
||||
|
||||
/* ====== Accounting API ====== */
|
||||
void
|
||||
StateReg::record (BuffHandle const& handle)
|
||||
{
|
||||
|
||||
reg_.emplace_back (std::make_shared<Block> (handle));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -104,7 +173,9 @@ namespace engine {
|
|||
: NaiveBufferSetup{}
|
||||
, heapStore_{dynamic_cast<HeapMemBufferStore&> (*bufferStore_)} //////////////////////////////////////////TICKET 1410 : obsolete after switch to newtracking-API
|
||||
, tracker_{std::make_unique<BlockTracker>()}
|
||||
{ }
|
||||
{
|
||||
decorate<InstrumentedStageProxy> (bufferStage_, *tracker_);
|
||||
}
|
||||
|
||||
DiagnosticBufferProvider::~DiagnosticBufferProvider()
|
||||
{
|
||||
|
|
@ -117,7 +188,7 @@ namespace engine {
|
|||
, emitted{dbp_.tracker_->emitted}
|
||||
, released{dbp_.tracker_->released}
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
|
||||
/* === diagnostic API === */
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ namespace engine {
|
|||
: public NaiveBufferSetup
|
||||
{
|
||||
HeapMemBufferStore& heapStore_; //////////////////////////////OOO fällt dann weg nach dem Umbau
|
||||
class InstrumentedStageProxy;
|
||||
struct BlockTracker;
|
||||
std::unique_ptr<BlockTracker> tracker_;
|
||||
|
||||
|
|
|
|||
|
|
@ -112046,12 +112046,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1770765035161" ID="ID_763895589" MODIFIED="1770765044567" TEXT="wie generisch wird das sein?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1770765047151" ID="ID_1369329762" MODIFIED="1770765133094">
|
||||
<node CREATED="1770765047151" ID="ID_1369329762" MODIFIED="1771274464468">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<u><font color="#210aa6">Frage</font></u><font color="#210aa6">:</font> kann <font face="Monospaced" color="#5b3535">ProviderBufferProvider</font> einen <i>beliebigen</i>  <font face="Monospaced" color="#5b3535">DiagnosticBuffer</font> instrumentieren?
|
||||
<u><font color="#210aa6">Frage</font></u><font color="#210aa6">:</font> kann <font face="Monospaced" color="#5b3535">DiagnosticBufferProvider</font> einen <i>beliebigen</i>  <font face="Monospaced" color="#5b3535">BufferProvider</font> instrumentieren?
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
|
|
@ -112063,7 +112063,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1770765260219" ID="ID_901586804" MODIFIED="1770765300102" TEXT="⟹ das bedeutet: das Ziel der Instrumentierung muß »kanibalisiert« werden">
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
</node>
|
||||
<node CREATED="1770765321000" ID="ID_1181670397" MODIFIED="1770765383761">
|
||||
<node COLOR="#105e49" CREATED="1770765321000" ID="ID_1181670397" MODIFIED="1771274554883">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -112116,9 +112116,16 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1770765700160" ID="ID_1579675471" LINK="https://en.cppreference.com/w/cpp/language/access.html#Protected_member_access" MODIFIED="1770766571232" TEXT="Thema: Sichtbarkeit von protected elements">
|
||||
<arrowlink COLOR="#4f5d8f" DESTINATION="ID_1070045765" ENDARROW="Default" ENDINCLINATION="-1154;119;" ID="Arrow_ID_484648111" STARTARROW="None" STARTINCLINATION="-1340;84;"/>
|
||||
</node>
|
||||
<node CREATED="1770766854965" ID="ID_164565147" MODIFIED="1770766871197" TEXT="man muß einen unique_ptr eines Dekorators erzeugen können"/>
|
||||
<node CREATED="1770766872225" ID="ID_1590901542" MODIFIED="1770766901998" TEXT="dieser Dekorator muß den ursprünglichen unique_ptr „huckepack“ nehmen"/>
|
||||
<node CREATED="1770766910933" ID="ID_1255549720" MODIFIED="1770766930079" TEXT="damit kann sich eine abgeleitete Klasse in bestehende Chains »einschleifen«"/>
|
||||
<node COLOR="#338800" CREATED="1770766854965" ID="ID_164565147" MODIFIED="1771276504239" TEXT="man muß einen unique_ptr eines Dekorators erzeugen können">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1770766872225" ID="ID_1590901542" MODIFIED="1771276650864" TEXT="dieser Dekorator muß den ursprünglichen unique_ptr „huckepack“ nehmen">
|
||||
<arrowlink COLOR="#1334c3" DESTINATION="ID_1462598861" ENDARROW="Default" ENDINCLINATION="-63;-1181;" ID="Arrow_ID_169512531" STARTARROW="None" STARTINCLINATION="-588;23;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1770766910933" ID="ID_1255549720" MODIFIED="1771276508584" TEXT="damit kann sich eine abgeleitete Klasse in bestehende Chains »einschleifen«">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -112565,7 +112572,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
<node CREATED="1770868481915" ID="ID_1687235750" MODIFIED="1770868497045" TEXT="dieser Teil ist im Grunde banal: es ist ein dummer Allokator"/>
|
||||
<node CREATED="1770868498116" ID="ID_1625162119" MODIFIED="1770868508672" TEXT="die kritische Logik steckt in der Type-Registry"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1770868509834" ID="ID_1753146390" MODIFIED="1770868535191" TEXT="⟹ auch die LIfecycle-Transitions müssen dorthin umziehen"/>
|
||||
<node COLOR="#435e98" CREATED="1770868509834" ID="ID_1753146390" MODIFIED="1771273966571" TEXT="⟹ auch die Lifecycle-Transitions müssen dorthin umziehen"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1770917116413" ID="ID_40466555" MODIFIED="1770923603445" TEXT="Diagnosefunktionen">
|
||||
|
|
@ -112840,6 +112847,16 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1770950167400" ID="ID_1883755499" MODIFIED="1770950182484" TEXT="die Tracking-Funktion muß den Zustandsübergang erkennen"/>
|
||||
<node CREATED="1770950209162" ID="ID_1174437807" MODIFIED="1770950232620" TEXT="und dann den Block aktualisieren und in die emitted/released-Registry übernehmen"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1771276599801" ID="ID_1205105935" MODIFIED="1771276627247" TEXT="Einschleifen der Instrumentierung">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1771276524376" ID="ID_1462598861" MODIFIED="1771276643962" TEXT="Instrumentierungs-Dekorator anlegen">
|
||||
<linktarget COLOR="#1334c3" DESTINATION="ID_1462598861" ENDARROW="Default" ENDINCLINATION="-63;-1181;" ID="Arrow_ID_169512531" SOURCE="ID_1590901542" STARTARROW="None" STARTINCLINATION="-588;23;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1771276666297" ID="ID_80656960" MODIFIED="1771276686235" TEXT="mit dem nun angelegten API genügt es, die BufferStage zu dekorieren">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -112896,16 +112913,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
<node CREATED="1771267839810" ID="ID_583729267" MODIFIED="1771267977789" TEXT="ich halte das für übertrieben — eingedenk der doch sehr limitierten Verwendung">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Angemessen wäre so was, wenn wir hier über einen generischen Memory-Allokator reden würden, der überall in der Applikation zum Einsatz kommt. Siehe als Beispiel das lib::Several, dort habe ich so eine segmentierte Struktur gebaut, denn dort überwiegt m.E. der Nutzen so eines »insider-APIs«
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -113135,22 +113149,17 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1771266770433" ID="ID_347345361" MODIFIED="1771266774908" TEXT="das Handle selber"/>
|
||||
<node CREATED="1771266775875" ID="ID_1781625402" MODIFIED="1771266906677" TEXT="niemand sonst kann das tun">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
BuffHandle hat keine Freunde ... das heißt, wenn man die release(handle) oder emegencyCleanup(handle) auf dem buffer-provider direkt aufruft, dann wird der Pointer im Handle <i>nicht invalidiert!</i> — das Handle ist aber danach trotzdem invalid und kann keine weitere State-transition mehr machen, weil der Entry aus den BufferMetada weggeworfen wurde.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1771266908318" ID="ID_19949747" MODIFIED="1771267284665" TEXT="das ist nicht schön — aber paßt ins Konzept">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Deshalb nenne ich es auch ein »Protocol« — wollten wir etwas wirklich <i>wasserdichtes,</i>  dann müßten wir die Struktur ganz anders anlegen, und vermutlich auch deutlich rigider, und mehr interne Zugriffe verwenden.
|
||||
|
|
|
|||
Loading…
Reference in a new issue