BufferProvider: integrate and verify heuristic trigger of clean-up

The mechanism is tuned now to engage rather quickly
whenever excess capacity builds up in a local allocator pool,
but when all buffers are used most of the time, they will be retained.

Can now demonstrate by integration test that capacity is sent back.
This commit is contained in:
Fischlurch 2026-05-26 04:39:41 +02:00
parent 5a7cfb613f
commit 7989e825b9
5 changed files with 236 additions and 209 deletions

View file

@ -51,6 +51,12 @@
namespace vault {
namespace mem {
namespace { // tuning parameters for clean-up heuristics
const double TRIGGER_SENSITIVIY = 1.0; ///< sensitivity of re-triggering based on max-score and turnover
const double HEURISTIC_CLEANUP = 0.5; ///< cut-off percentage of max-score; blocks with lower score will be sent back
}
using util::_Fmt;
class LocalStoreDiagnostic;
@ -73,16 +79,28 @@ namespace mem {
* This implementation of the BufferProvider::BufferStore interface
* deliberately skips further sanity checks, beyond what is enforced
* through the BufferMetadata lifecycle. Allocation requests are
* passed to the LocalMemPool for the current thread.
* delegated to the LocalMemPool for the current thread.
*
* The PoolSetup creates a connection to the EngineBufferManager,
* retrieved via lib::Depend, so each thread-local pool can unwind
* automatically at thread end and return all leased storage.
*/
class LocalBufferStore
: public BufferProviderSetup::Store
{
struct SetupPool;
using LocalPool = lib::LocalSlice<LocalMemPool, SetupPool>;
struct PoolSetup;
using LocalPool = lib::LocalSlice<LocalMemPool, PoolSetup>;
using GlobalPool = lib::Depend<EngineBufferManager>;
struct SetupPool
static void
send_back2globalPool (Buff* mem, size_t siz)
{
Alloc alloc{mem,siz};
GlobalPool globalPool;
globalPool().supply(alloc);
}
struct PoolSetup
{
using Service = LocalMemPool;
@ -91,12 +109,7 @@ namespace mem {
{
return LocalMemPool{[](LocalMemPool& pool2close)
{
pool2close.purge([](Buff* mem, size_t siz)
{
Alloc alloc{mem,siz};
GlobalPool globalPool;
globalPool().supply(alloc);
});
pool2close.purge (send_back2globalPool);
}};
}
};
@ -105,8 +118,7 @@ namespace mem {
GlobalPool globalPool_;
public:
LocalBufferStore()
{ }
LocalBufferStore() { }
@ -191,15 +203,19 @@ namespace mem {
auto& [storage,buffSiz,specifics] = storageSlot;
REQUIRE (storage);
localPool_->reSupply (storage);
/////////////////////////////////////////////////////OOO trigger heuristic clean-up here
if (localPool_->shouldCleanup (TRIGGER_SENSITIVIY))
localPool_->cleanup (HEURISTIC_CLEANUP
,send_back2globalPool);
}
private:
/** diagnostic access for unit testing */
friend PoolDiagnostic watch (LocalBufferStore const&);
};
/** entrance point to inspection for test */
inline PoolDiagnostic
watch (LocalBufferStore const& lstor)

View file

@ -205,9 +205,9 @@ namespace mem {
/** turnover-based trigger for heuristic pool clean-up */
bool
shouldCleanup (float sharpen =1.0f) const
shouldCleanup (double sharpen =1.0f) const
{
return turnover_ * float(MATCH_SCORE) <= maxScore_ * sharpen;
return turnover_ * double(MATCH_SCORE) <= maxScore_ * sharpen;
}
/**

View file

@ -24,7 +24,6 @@
#include "lib/depend-inject.hpp"
#include "lib/thread.hpp"
#include "lib/util.hpp"
#include "test/diagnostic-output.hpp"///////////////////////TODO
#include <algorithm>
#include <atomic>
@ -40,28 +39,11 @@ namespace vault {
namespace mem {
namespace test {
// using LERR_(LOGIC);
// using LERR_(LIFECYCLE);
namespace { // Test helper
// template<typename X>
// Buff*
// mark_as_Buffer(X& something)
// {
// return reinterpret_cast<Buff*> (std::addressof(something));
// }
//
}
/**************************************************************************//**
* @test verify worker memory handling that relies on a thread-local allocator.
/*********************************************//**
* @test verify memory handling in worker
* that relies on a thread-local allocation pool.
*/
class LocalBufferStore_test : public Test
{
@ -69,8 +51,6 @@ namespace test {
virtual void
run (Arg)
{
// seedRand();
verify_API();
verify_announce();
verify_allocate();
@ -372,7 +352,7 @@ namespace test {
/** @test heuristic clean-up after releasing several allocations
* - at first, a de-allocated block remains available in the local pool
* - after a number of usages, defined as _turnover_, clean-up is triggered
* - this test scenario requests 3 buffers, but repeatedly uses only one
* - this test scenario requests 5 buffers, but repeatedly uses only one
* - after the third round with this usage pattern, the local pool is reduced
*/
void
@ -394,7 +374,7 @@ namespace test {
Thread testWorker{[&] /* === Scenario: test subject requests allocation immediately === */
{
// Step-1 : first round
uint avail = storeAPI.prepareBuffers(TYPEID, 3, BUFFSIZ);
uint avail = storeAPI.prepareBuffers(TYPEID, 5, BUFFSIZ);
CHECK (0 == avail);
CHECK (watch(localStore).isEmpty());
@ -403,36 +383,40 @@ namespace test {
CHECK (storage);
storeAPI.detachBuffer (TYPEID, slot1);
// Result: got three allocations and all three are in the local pool
CHECK (watch(localStore).size() == 3);
CHECK (watch(localStore).cntFree() == 3);
// Result: got five allocations and all are in the local pool
CHECK (watch(localStore).size() == 5);
CHECK (watch(localStore).cntFree() == 5);
++step;
// Step-2 : repeat same pattern
// Step-2 : repeat same pattern -- again only using one of the five buffers
BuffAlloc slot2 = storeAPI.provideBuffer (TYPEID, BUFFSIZ, 0,0);
CHECK (slot2 == slot1);
CHECK (watch(localStore).size() == 3);
CHECK (watch(localStore).cntFree() == 2);
CHECK (watch(localStore).size() == 5);
CHECK (watch(localStore).cntFree() == 4);
storeAPI.detachBuffer (TYPEID, slot1);
CHECK (watch(localStore).size() == 3);
CHECK (watch(localStore).cntFree() == 3);
CHECK (watch(localStore).size() == 5);
CHECK (watch(localStore).cntFree() == 5);
++step;
// Step-3 : repeat for a third time
BuffAlloc slot3 = storeAPI.provideBuffer (TYPEID, BUFFSIZ, 0,0);
CHECK (slot3 == slot1);
CHECK (watch(localStore).size() == 3);
CHECK (watch(localStore).cntFree() == 2);
CHECK (watch(localStore).size() == 5);
CHECK (watch(localStore).cntFree() == 4);
storeAPI.detachBuffer (TYPEID, slot1);
// this completed a "turnover" of three buffers
SHOW_EXPR(watch(localStore).size());
// this triggered a clean-up and detected the unused capacity
CHECK (watch(localStore).size() == 1);
CHECK (watch(localStore).cntFree() == 1);
++step;
// main thread will now see the partial clean-up
}}; // block automatically sent back after thread end
while (step == 4) // wait until main thread could investigate
sleep_for(1ms);
}}; // remaining buffer automatically sent back after thread end
/* === Main thread: observe effects in the global pool === */
@ -440,19 +424,31 @@ namespace test {
// wait until Step-2
while (step < 2)
sleep_for(1ms);
CHECK (watch(*globalPool).numAllocs() == 3);
CHECK (watch(*globalPool).numAllocs() == 5);
// re-check after Step-2
while (step <= 2)
sleep_for(1ms);
// nothing changed; local pool operates independently
CHECK (watch(*globalPool).numAllocs() == 3);
CHECK (watch(*globalPool).numAllocs() == 5);
CHECK (watch(*globalPool).bytesLeased() == 5*BUFFSIZ);
// re-check after Step-3
while (step <= 3)
sleep_for(1ms);
globalPool->processPendingRequests();
SHOW_EXPR(watch(*globalPool).numAllocs())
CHECK (watch(*globalPool).bytesLeased() == 1*BUFFSIZ);
// Note: local pool sent back excess capacity
++step; // allow the worker to terminate after that
while (testWorker)
sleep_for(1ms);
sleep_for(1ms); // additional wait for the dtor
globalPool->processPendingRequests();
CHECK (watch(*globalPool).bytesLeased() == 0);
// all capacity properly returned into global pool
}
};

View file

@ -306,6 +306,7 @@ namespace test {
/** @test heuristic trigger mechanism for pool clean-up
* - use a setup where the global pool is just a queue of buffer pointers
* - create abbreviated notation to play through various scenarios
@ -377,208 +378,199 @@ namespace test {
.feed(MEM32)
.feed(MEM33);
SHOW_EXPR(show(globalPool));
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK (0 == watch(pool).size() );
CHECK (0 == watch(pool).getTurnover() );
supply(3);
SHOW_EXPR("supply")
SHOW_EXPR(show(globalPool));
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
reserve(2);
SHOW_EXPR("reserve")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK (3 == watch(pool).size() );
CHECK (3 == watch(pool).cntFree() );
CHECK (2 == watch(pool).cntResd() );
CHECK (1 == watch(pool).getMaxScore() );
CHECK (3 == watch(pool).getTurnover() );
//------------ PATTERN-1 : use complete local capacity
claim(3);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(usedBuff));
CHECK ( 3 == watch(pool).size() );
CHECK ( 0 == watch(pool).cntFree() );
CHECK ( 0 == watch(pool).cntResd() );
CHECK (10 == watch(pool).getMaxScore() );
CHECK ( 3 == watch(pool).getTurnover() );
free(3);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(usedBuff));
CHECK ( 3 == watch(pool).size() );
CHECK ( 3 == watch(pool).cntFree() );
CHECK (10 == watch(pool).getMaxScore() );
CHECK ( 0 == watch(pool).getTurnover() );
SHOW_EXPR(pool.shouldCleanup())
// heuristic trigger fires at that point,
// since full capacity was »turned over« once
CHECK (pool.shouldCleanup() );
uint removed = pool.cleanup(0.5, pushMem);
SHOW_EXPR(removed)
// However, nothing was removed, since all elements
// were used efficiently and are thus close to maxScore
CHECK (0 == removed);
CHECK (show(globalPool) == "21, 31, 32, 33"_expect);
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(globalPool));
CHECK ( 3 == watch(pool).size() );
CHECK ( 5 == watch(pool).getMaxScore() );
// turnover is re-set to current pool size
CHECK ( 3 == watch(pool).getTurnover() );
//------------ PATTERN-2 : use only one buffer a time (inefficient)
claim(1);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 3 == watch(pool).size() );
CHECK ( 2 == watch(pool).cntFree() );
CHECK (15 == watch(pool).getMaxScore() );
CHECK ( 3 == watch(pool).getTurnover() );
free(1);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 3 == watch(pool).size() );
CHECK ( 3 == watch(pool).cntFree() );
CHECK (15 == watch(pool).getMaxScore() );
CHECK ( 2 == watch(pool).getTurnover() );
SHOW_EXPR(pool.shouldCleanup())
// not yet hitting the trigger...
CHECK (not pool.shouldCleanup() );
claim(1);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 3 == watch(pool).size() );
CHECK ( 2 == watch(pool).cntFree() );
CHECK (25 == watch(pool).getMaxScore() );
CHECK ( 2 == watch(pool).getTurnover() );
free(1);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 3 == watch(pool).size() );
CHECK ( 3 == watch(pool).cntFree() );
CHECK (25 == watch(pool).getMaxScore() );
CHECK ( 1 == watch(pool).getTurnover() );
SHOW_EXPR(pool.shouldCleanup())
// maxScore did increase a lot,
// since effectively just a single buffer was used all the time
CHECK (pool.shouldCleanup() );
removed = pool.cleanup(0.5, pushMem);
SHOW_EXPR(removed)
// Consequently the two superfluous buffers will be sent back
// (cutoff-score is at 12; maxScore will be lowered by that amount afterwards)
CHECK (2 == removed );
CHECK (show(globalPool) == "21, 31, 32, 33, 12, 11"_expect);
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(globalPool));
CHECK ( 1 == watch(pool).size() );
CHECK ( 1 == watch(pool).cntFree() );
CHECK (13 == watch(pool).getMaxScore() );
CHECK ( 1 == watch(pool).getTurnover() );
//------------ PATTERN-3 : dynamic partially overlapping usage
supply(4);
SHOW_EXPR("supply")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(globalPool));
CHECK ( 5 == watch(pool).size() );
CHECK ( 5 == watch(pool).cntFree() );
CHECK (13 == watch(pool).getMaxScore() );
CHECK ( 5 == watch(pool).getTurnover() );
// turnover was bumped +4 due to increased supply
claim(3);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 5 == watch(pool).size() );
CHECK ( 2 == watch(pool).cntFree() );
CHECK (13 == watch(pool).getMaxScore() );
CHECK ( 5 == watch(pool).getTurnover() );
free(2);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 5 == watch(pool).size() );
CHECK ( 4 == watch(pool).cntFree() );
CHECK (13 == watch(pool).getMaxScore() );
CHECK ( 3 == watch(pool).getTurnover() );
SHOW_EXPR(pool.shouldCleanup())
CHECK (not pool.shouldCleanup() );
// no complete turnover cycle yet
claim(2);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 5 == watch(pool).size() );
CHECK ( 2 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 3 == watch(pool).getTurnover() );
supply(4);
SHOW_EXPR("supply")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(globalPool));
CHECK ( 7 == watch(pool).size() );
CHECK ( 4 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 5 == watch(pool).getTurnover() );
free(2);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(usedBuff));
CHECK ( 7 == watch(pool).size() );
CHECK ( 6 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 3 == watch(pool).getTurnover() );
claim(1);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 7 == watch(pool).size() );
CHECK ( 5 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 3 == watch(pool).getTurnover() );
free(1);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(usedBuff));
CHECK ( 7 == watch(pool).size() );
CHECK ( 6 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 2 == watch(pool).getTurnover() );
CHECK (pool.shouldCleanup());
// triggering now, since 20/MATCH_SCORE ≡ 2
SHOW_EXPR(pool.shouldCleanup())
removed = pool.cleanup(0.5, pushMem);
SHOW_EXPR(removed)
CHECK (3 == removed);
CHECK (show(globalPool) == "11, 12, 21"_expect);
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(globalPool));
CHECK ( 4 == watch(pool).size() );
CHECK ( 3 == watch(pool).cntFree() );
CHECK (10 == watch(pool).getMaxScore() );
CHECK ( 4 == watch(pool).getTurnover() );
//------------ PATTERN-4 : decreasing the fraction used
claim(1);
SHOW_EXPR("claim")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).cntResd())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 4 == watch(pool).size() );
CHECK ( 2 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 4 == watch(pool).getTurnover() );
free(2);
SHOW_EXPR("free")
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).cntFree())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
CHECK ( 4 == watch(pool).size() );
CHECK ( 4 == watch(pool).cntFree() );
CHECK (20 == watch(pool).getMaxScore() );
CHECK ( 2 == watch(pool).getTurnover() );
CHECK (pool.shouldCleanup() );
SHOW_EXPR(pool.shouldCleanup())
removed = pool.cleanup(0.5, pushMem);
SHOW_EXPR(removed)
CHECK (3 == removed);
CHECK (show(globalPool) == "11, 12, 21, 33, 31, 13"_expect);
SHOW_EXPR(watch(pool).size())
SHOW_EXPR(watch(pool).getMaxScore())
SHOW_EXPR(watch(pool).getTurnover())
SHOW_EXPR(show(globalPool));
CHECK ( 1 == watch(pool).size() );
CHECK (10 == watch(pool).getMaxScore() );
CHECK ( 1 == watch(pool).getTurnover() );
}
};

View file

@ -145325,7 +145325,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1778802491003" ID="ID_1880673250" MODIFIED="1778979225756" TEXT="brauche noch einen Trigger-Mechanismus f&#xfc;r das heuristische Clean-up">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1778802491003" ID="ID_1880673250" MODIFIED="1779762406992" TEXT="brauche noch einen Trigger-Mechanismus f&#xfc;r das heuristische Clean-up">
<icon BUILTIN="yes"/>
<node CREATED="1778805833301" ID="ID_493965127" MODIFIED="1778806330596" TEXT="man k&#xf6;nnte nach jeder de-Allokation einen partiellen clean-up machen">
<node CREATED="1778805853860" ID="ID_1555068699" MODIFIED="1778806001396" TEXT="w&#xe4;re wirksam, da nur unterhalb der Schwelle aufger&#xe4;umt wird">
@ -145412,7 +145412,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1778946963640" ID="ID_993736225" MODIFIED="1778946997115" TEXT="bei Leerlauf w&#xe4;chst der Score weniger Bl&#xf6;cke (und damit der Max-Score) &#xfc;berproportional"/>
<node CREATED="1778807937581" ID="ID_1900765984" MODIFIED="1778807970854" TEXT="bei lokaler Knappheit werden viele Allokations-Anforderungen gesandt"/>
</node>
<node CREATED="1778945962154" ID="ID_535774391" MODIFIED="1778945972518" TEXT="daraus einen Mechanismus entwickeln">
<node COLOR="#435e98" CREATED="1778945962154" ID="ID_535774391" MODIFIED="1779762420870" TEXT="daraus einen Mechanismus entwickeln">
<icon BUILTIN="idea"/>
<node CREATED="1778946005609" ID="ID_1483973980" MODIFIED="1778946757293" TEXT="Ansatz: die bereits implementierte Aufr&#xe4;um-Heuristik ist zielf&#xfc;hrend">
<richcontent TYPE="NOTE"><html>
@ -145482,9 +145482,9 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1778800559849" ID="ID_1776275235" MODIFIED="1778800646883" TEXT="LocalBufferStore_test">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1778800660632" ID="ID_864970874" MODIFIED="1778979160301" TEXT="Zweck">
<node COLOR="#338800" CREATED="1778800559849" ID="ID_1776275235" MODIFIED="1779762439896" TEXT="LocalBufferStore_test">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1778800660632" ID="ID_864970874" MODIFIED="1779762437163" TEXT="Zweck">
<icon BUILTIN="yes"/>
<node CREATED="1778800664656" ID="ID_690903258" MODIFIED="1778800678906" TEXT="Aufbau der Implemenrierung treiben"/>
<node CREATED="1778800684264" ID="ID_1722512715" MODIFIED="1778800718145" TEXT="korrektes Verhalten auf API-Level zeigen"/>
@ -145668,9 +145668,13 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
</node>
</node>
<node CREATED="1778968918381" ID="ID_752345967" MODIFIED="1778968922655" TEXT="verify_release">
<node COLOR="#338800" CREATED="1778968918381" ID="ID_752345967" MODIFIED="1779762376165" TEXT="verify_release">
<icon BUILTIN="button_ok"/>
<node CREATED="1778969042432" ID="ID_1262962638" MODIFIED="1778969052166" TEXT="hier sollte auch ein clean-up provoziert werden"/>
<node CREATED="1779665675415" ID="ID_1685534215" MODIFIED="1779665691018" TEXT="zu zeigen ist da&#xdf; dieser Clean-up mit R&#xfc;cksendung stattfindet"/>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1779665675415" ID="ID_1685534215" MODIFIED="1779762386504" TEXT="zu zeigen ist da&#xdf; dieser Clean-up mit R&#xfc;cksendung stattfindet">
<linktarget COLOR="#1b26b8" DESTINATION="ID_1685534215" ENDARROW="Default" ENDINCLINATION="408;19;" ID="Arrow_ID_1702718350" SOURCE="ID_1035469430" STARTARROW="None" STARTINCLINATION="577;30;"/>
<icon BUILTIN="yes"/>
</node>
</node>
<node COLOR="#5b280f" CREATED="1778969102656" ID="ID_249806133" MODIFIED="1779664543753" TEXT="verify_heuristic">
<richcontent TYPE="NOTE"><html>
@ -145688,8 +145692,8 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</html>
</richcontent>
<icon BUILTIN="button_cancel"/>
<node CREATED="1779664546343" HGAP="25" ID="ID_453821075" MODIFIED="1779665655342" TEXT="dieser Test geh&#xf6;rt zum LocalMemPool" VSHIFT="18">
<arrowlink COLOR="#765294" DESTINATION="ID_1242239297" ENDARROW="Default" ENDINCLINATION="-1163;-399;" ID="Arrow_ID_1851213418" STARTARROW="None" STARTINCLINATION="-413;30;"/>
<node CREATED="1779664546343" HGAP="25" ID="ID_453821075" MODIFIED="1779762367351" TEXT="dieser Test geh&#xf6;rt zum LocalMemPool" VSHIFT="18">
<arrowlink COLOR="#333263" DESTINATION="ID_1242239297" ENDARROW="Default" ENDINCLINATION="-1163;-399;" ID="Arrow_ID_1851213418" STARTARROW="None" STARTINCLINATION="-413;30;"/>
<icon BUILTIN="yes"/>
</node>
</node>
@ -145755,15 +145759,18 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</body>
</html></richcontent>
</node>
<node CREATED="1776605858192" ID="ID_563549971" MODIFIED="1776605951752" TEXT="bleibe aber bei dem Namen &#x2014; weil er klar ist">
<node CREATED="1776605858192" ID="ID_563549971" MODIFIED="1779762245934" TEXT="bleibe aber bei dem Namen &#x2014; weil er klar ist">
<richcontent TYPE="NOTE"><html>
<head/>
<head>
</head>
<body>
<p>
das hier ist keine generische Library, sondern ein Application-Framework. Auch &#187;Vault&#171; ist inhaltlich genau auf Lumiera ausgerichtet, der Unterschied liegt nur im Grad der Integration mit Application-level Services
</p>
</body>
</html></richcontent>
</html>
</richcontent>
</node>
<node COLOR="#338800" CREATED="1776611511064" ID="ID_419155046" MODIFIED="1776639433744" TEXT="Mutex-Locking einf&#xfc;hren">
<linktarget COLOR="#4ff278" DESTINATION="ID_419155046" ENDARROW="Default" ENDINCLINATION="1269;0;" ID="Arrow_ID_703288918" SOURCE="ID_808614387" STARTARROW="None" STARTINCLINATION="1269;0;"/>
@ -147000,8 +147007,9 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<arrowlink COLOR="#f2ddc1" DESTINATION="ID_1195705832" ENDARROW="Default" ENDINCLINATION="16;47;" ID="Arrow_ID_296050493" STARTARROW="None" STARTINCLINATION="85;12;"/>
<icon BUILTIN="button_cancel"/>
</node>
<node CREATED="1778969102656" ID="ID_1242239297" MODIFIED="1779665655342" TEXT="verify_heuristic">
<linktarget COLOR="#765294" DESTINATION="ID_1242239297" ENDARROW="Default" ENDINCLINATION="-1163;-399;" ID="Arrow_ID_1851213418" SOURCE="ID_453821075" STARTARROW="None" STARTINCLINATION="-413;30;"/>
<node COLOR="#338800" CREATED="1778969102656" ID="ID_1242239297" MODIFIED="1779762145111" TEXT="verify_heuristic">
<linktarget COLOR="#333263" DESTINATION="ID_1242239297" ENDARROW="Default" ENDINCLINATION="-1163;-399;" ID="Arrow_ID_1851213418" SOURCE="ID_453821075" STARTARROW="None" STARTINCLINATION="-413;30;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1778979230293" ID="ID_1129342480" MODIFIED="1779664866045" TEXT="meine Idee der Heuristik umsetzen">
<arrowlink COLOR="#4d5f6c" DESTINATION="ID_1141255727" ENDARROW="Default" ENDINCLINATION="-376;22;" ID="Arrow_ID_514664149" STARTARROW="None" STARTINCLINATION="-76;-210;"/>
</node>
@ -147037,8 +147045,23 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1779733348313" ID="ID_1867741071" MODIFIED="1779733353992" TEXT="wechselnd partiell verwenden"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1778979252922" ID="ID_155080806" MODIFIED="1779733388847" TEXT="Verhalten sollte deterministisch sein und Erwartung entsprechen">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1778979252922" ID="ID_155080806" MODIFIED="1779762143255" TEXT="Verhalten sollte deterministisch sein und Erwartung entsprechen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1779762175999" ID="ID_1035469430" MODIFIED="1779762350703" TEXT="Clean-up ist auch in globaler Kommunikation sichtbar">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Wenn man eine gr&#246;&#223;ere Kapazit&#228;t reserviert, aber dann wiederholt immer nur einzelne Buffer belegt und wieder freigibt, sollte relativ bald der lokale Pool getrimmt werden, und die freigegebenen Buffer an den globaeln Pool zur&#252;ckgessend
</p>
</body>
</html>
</richcontent>
<arrowlink COLOR="#1b26b8" DESTINATION="ID_1685534215" ENDARROW="Default" ENDINCLINATION="408;19;" ID="Arrow_ID_1702718350" STARTARROW="None" STARTINCLINATION="577;30;"/>
<icon BUILTIN="yes"/>
</node>
</node>
</node>