Library: restructure wrapper in accordance to the solution found

So this finally solves the fundamental problem regarding a race on
initialisation of the thread-wrapper; it does *not* solve the same problem
for classes deriving from thread-wrapper, which renders this design questionable
altogether -- but this is another story.

In the end, this initialisation-race is rooted in the very nature of starting a thread;
it seems there are the two design alternatives:
- expose the thread-creation directly to user code (offloading the responsibility)
- offer building blocks which are inherently dangerous
This commit is contained in:
Fischlurch 2023-10-09 16:47:56 +02:00
parent 8518cf1fa0
commit dd2fe7da59
3 changed files with 246 additions and 82 deletions

View file

@ -210,6 +210,20 @@ namespace meta {
};
/**
* Metaprogramming helper to mark some arbitrary base type by subclassing.
* In most respects the _specially marked type_ behaves like the base; this
* can be used to mark some element at compile time, e.g. to direct it into
* a specialisation or let it pick some special overload.
*/
template<class BAS, size_t m=0>
struct Marked
: BAS
{
using BAS::BAS;
};

View file

@ -76,7 +76,23 @@
** in cases where a race could be critical, additional means must be implemented; a
** possible solution would be to use a [N-fold synchronisation barrier](\ref lib::SyncBarrier)
** explicitly, or otherwise to ensure there is sufficient delay in the starting thread function.
**
**
** ##Caveat
** While these thread-wrapper building blocks aim at packaging the complexity away, there is
** the danger to miss a potential race, which is inherent with starting threads: the operation
** in the new thread contends with any initialisation done _after_ launching the thread. Even
** though encapsulating complex concurrent logic into an opaque component, as built on top
** of the thread-wrappers, is highly desirable from a code sanity angle it is dangerously
** tempting to package self-contained data initialisation into a subclass, leading to the
** kind of _undefined behaviour,_ which can never happen under normal circumstances.
** Even while the OS scheduler typically adds an latency of at least 100µs to the start
** of the new thread function, initialising anything (even subclass data members) after
** creating the thread-wrapper instance *is undefined behaviour*. As a remedy
** - it should be considered to put the thread-warpper into a _member_ (instead of inheriting)
** - an explicit lib::SyncBarrier can be added, to ensure the thread-function touches any
** extended facilities only after the initialisation is complete (as a downside, note that
** any hard synchronisation adds a possibility for deadlock).
**
** @remarks Historical design evolution:
** - Lumiera offered simplified convenience wrappers long before a similar design
** became part of the C++14 standard. These featured the distinction in join-able or
@ -140,23 +156,10 @@ namespace lib {
using std::is_same;
using std::__or_;
////////////////////////////////////////////////////////////////////////////////////////////////////OOO extract -> lib/meta
/**
* Metaprogramming helper to mark some arbitrary base type by subclassing.
* In most respects the _specially marked type_ behaves like the base; this
* can be used to mark some element at compile time, e.g. to direct it into
* a specialisation or let it pick some special overload.
*/
template<class BAS, size_t m=0>
struct Marked
: BAS
{
using BAS::BAS;
};
////////////////////////////////////////////////////////////////////////////////////////////////////OOO extract(End)
/** @internal wraps the C++ thread handle
* and provides some implementation details,
* which are then combined by the _policy template_
* @internal wraps the C++ thread handle
* and provides some implementation details,
* which are then combined by the _policy template_
*/
struct ThreadWrapper
: util::MoveOnly
@ -173,10 +176,9 @@ namespace lib {
, threadImpl_{}
{ }
template<typename...ARGS>
ThreadWrapper (string const& threadID, ARGS&& ...args)
ThreadWrapper (string const& threadID)
: threadID_{util::sanitise (threadID)}
, threadImpl_{forward<ARGS> (args)... }
, threadImpl_{} //Note: deliberately not starting the thread yet...
{ }
/** @internal actually launch the new thread.
@ -325,49 +327,12 @@ namespace lib {
Policy::handle_thread_still_running();
}
public:
/**
* Is this thread »active« and thus tied to OS resources?
* @note this implies some statefulness, which may contradict the RAII pattern.
* - especially note the possibly for derived classes to create an _empty_ Thread.
* - moreover note that ThreadJoinable may have terminated, but still awaits `join()`.
*/
explicit
operator bool() const
{
return Policy::isLive();
}
/** @return does this call happen from within this thread? */
using Policy::invokedWithinThread;
/** Create a new thread to execute the given operation.
* The new thread starts up synchronously, can't be cancelled and it can't be joined.
* @param threadID human readable descriptor to identify the thread for diagnostics
* @param threadFunction a functor holding the code to execute within the new thread.
* Any function-like entity or callable is acceptable; arguments can be given.
* @warning The operation functor and all arguments will be copied into the new thread.
* The return from this constructor _syncs-with_ the launch of the operation.
*/
template<class FUN, typename...ARGS>
ThreadLifecycle (string const& threadID, FUN&& threadFunction, ARGS&& ...args)
: Policy{threadID
, &ThreadLifecycle::invokeThreadFunction<FUN, decay_t<ARGS>...>, this
, forward<FUN> (threadFunction)
, decay_t<ARGS> (args)... } // Note: need to decay the argument types
{ } // (arguments must be copied)
template<class SUB, typename...ARGS>
ThreadLifecycle (RES (SUB::*memFun) (ARGS...), ARGS ...args)
: ThreadLifecycle{util::joinDash (typeSymbol<SUB>(), args...)
,std::move (memFun)
,static_cast<SUB*> (this)
,forward<ARGS> (args)... }
/** derived classes may create a disabled thread */
ThreadLifecycle()
: Policy{}
{ }
public:
/**
* Build a λ actually to launch the given thread operation later,
* after the thread-wrapper-object is fully initialised.
@ -389,7 +354,7 @@ namespace lib {
return [invocation = move(argCopy)] //Note: functor+args bound by-value into the λ
(ThreadLifecycle& wrapper)
{ //the thread-main function
wrapper.launchThread (tuple_cat (tuple{&ThreadLifecycle::invokeThreadFunction<INVO...>
wrapper.launchThread (tuple_cat (tuple{&ThreadLifecycle::invokeThreadFunction<decay_t<INVO>...>
, &wrapper} // passing the wrapper as instance-this
,move (invocation))); //...invokeThreadFunction() in turn delegates
}; // to the user-provided thread-operation
@ -397,15 +362,18 @@ namespace lib {
/**
* Configuration builder to define the operation to run in the thread,
* and possibly configure further details, depending on the Policy used.
* Configuration builder to define the operation running within the thread,
* and possibly configure further details, depending on the actual Policy used.
* @remark the primary ThreadLifecycle-ctor accepts such a Launch-instance
* and invokes the chain of λ-functions collected in the member #launch
* and invokes a chain of λ-functions collected in the member #launch
*/
struct Launch
: util::MoveOnly
{
function<void(ThreadLifecycle&)> launch;
using Act = function<void(ThreadLifecycle&)>;
Act launch;
string id;
template<class FUN, typename...ARGS>
Launch (FUN&& threadFunction, ARGS&& ...args)
@ -413,23 +381,83 @@ namespace lib {
{ }
Launch&&
threadID (string const& id)
threadID (string const& threadID)
{
launch = [=, chain=std::move(launch)]
id = threadID;
return move(*this);
}
Launch&&
addLayer (Act action)
{
launch = [action=move(action), chain=move(launch)]
(ThreadLifecycle& wrapper)
{
util::unConst(wrapper.threadID_) = id;
action(wrapper);
chain (wrapper);
};
return move(*this);
}
};
/**
* Primary constructor: Launch the new thread with flexible configuration.
* @param launcher a #Launch builder with a λ-chain to configure and
* finally trigger start of the thread
*/
ThreadLifecycle (Launch launcher)
: Policy{}
: Policy{launcher.id}
{
launcher.launch (*this);
}
/**
* Create a new thread to execute the given operation.
* The new thread starts up synchronously, can't be cancelled and it can't be joined.
* @param threadID human readable descriptor to identify the thread for diagnostics
* @param threadFunction a functor holding the code to execute within the new thread.
* Any function-like entity or callable is acceptable; arguments can be given.
* @warning The operation functor and all arguments will be copied into the new thread.
* The return from this constructor _syncs-with_ the launch of the operation.
*/
template<class FUN, typename...ARGS>
ThreadLifecycle (string const& threadID, FUN&& threadFunction, ARGS&& ...args)
: ThreadLifecycle{
Launch{forward<FUN> (threadFunction), forward<ARGS> (args)...}
.threadID(threadID)}
{ }
/**
* Special variant to bind a subclass member function as thread operation.
* @warning potential race between thread function and subclass initialisation
*/
template<class SUB, typename...ARGS>
ThreadLifecycle (RES (SUB::*memFun) (ARGS...), ARGS ...args)
: ThreadLifecycle{
Launch{std::move (memFun)
,static_cast<SUB*> (this)
,forward<ARGS> (args)...
}
.threadID(util::joinDash (typeSymbol<SUB>(), args...))}
{ }
/**
* Is this thread »active« and thus tied to OS resources?
* @note this implies some statefulness, which may contradict the RAII pattern.
* - especially note the possibly for derived classes to create an _empty_ Thread.
* - moreover note that ThreadJoinable may have terminated, but still awaits `join()`.
*/
explicit
operator bool() const
{
return Policy::isLive();
}
/** @return does this call happen from within this thread? */
using Policy::invokedWithinThread;
};
}//(End)base implementation.

View file

@ -65389,8 +65389,13 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696861323485" ID="ID_1893882898" MODIFIED="1696861419490" TEXT="mu&#xdf; flexible Konfiguration hierf&#xfc;r erm&#xf6;glichen">
<arrowlink COLOR="#8184a8" DESTINATION="ID_997556571" ENDARROW="Default" ENDINCLINATION="-767;-772;" ID="Arrow_ID_1317755297" STARTARROW="None" STARTINCLINATION="-380;22;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696628443151" ID="ID_1852065850" MODIFIED="1696640533961" TEXT="das Race-Problem stellt das ganze Design in Frage">
</node>
<node BACKGROUND_COLOR="#dec38f" COLOR="#990000" CREATED="1696628443151" ID="ID_1852065850" MODIFIED="1696861406605" TEXT="das Race-Problem stellt das ganze Design in Frage">
<arrowlink COLOR="#393653" DESTINATION="ID_218116618" ENDARROW="Default" ENDINCLINATION="20;-87;" ID="Arrow_ID_1655702369" STARTARROW="None" STARTINCLINATION="-29;62;"/>
<linktarget COLOR="#fe225e" DESTINATION="ID_1852065850" ENDARROW="Default" ENDINCLINATION="147;-294;" ID="Arrow_ID_1803073395" SOURCE="ID_771623143" STARTARROW="None" STARTINCLINATION="-332;18;"/>
<icon BUILTIN="broken-line"/>
<node CREATED="1696628561935" ID="ID_183158925" MODIFIED="1696628612622" TEXT="so ziemlich jede Form von Layering und Composition ist betroffen">
@ -65467,6 +65472,10 @@
<icon BUILTIN="ksmiletris"/>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1696861236797" ID="ID_678601526" MODIFIED="1696861262107" TEXT="es mu&#xdf; mir gelingen, den Workaround mit move-assign zu integrieren">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff00bd" CREATED="1696639942907" ID="ID_773629884" MODIFIED="1696639967679" TEXT="und was nun?">
<font NAME="SansSerif" SIZE="13"/>
@ -65482,7 +65491,8 @@
<node CREATED="1696640278102" ID="ID_362520772" MODIFIED="1696640302706" TEXT="Thread (Standardfall) so &#xe4;ndern, da&#xdf; er im dtor intern ein detach() macht"/>
<node CREATED="1696640304051" ID="ID_838020700" MODIFIED="1696640339522" TEXT="daf&#xfc;r f&#xe4;llt detach() auf dem API weg und der SteamDispatcher bleibt wie er war"/>
</node>
<node CREATED="1696640573023" ID="ID_1848128254" MODIFIED="1696640645673" TEXT="Komplexit&#xe4;t akzeptieren und das Baukasten-System erweitern">
<node CREATED="1696640573023" ID="ID_1848128254" MODIFIED="1696860986291" TEXT="Komplexit&#xe4;t akzeptieren und das Baukasten-System erweitern">
<linktarget COLOR="#717cb7" DESTINATION="ID_1848128254" ENDARROW="Default" ENDINCLINATION="-84;4;" ID="Arrow_ID_501556845" SOURCE="ID_207476991" STARTARROW="None" STARTINCLINATION="-14;-12;"/>
<node CREATED="1696640688951" ID="ID_308046854" MODIFIED="1696640722766" TEXT="Leistungen">
<node CREATED="1696640723816" ID="ID_1672854598" MODIFIED="1696640771200" TEXT="anheben der Abstraktions-Ebene"/>
<node CREATED="1696640729546" ID="ID_1634619318" MODIFIED="1696640763097" TEXT="explizit und klar in der Verwendung"/>
@ -65498,6 +65508,30 @@
<node CREATED="1696641206002" ID="ID_284758018" MODIFIED="1696641255688" TEXT="Ausblick auf die Zukunft: Priority-Klassen, Supervisor anbinden"/>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1696860909069" ID="ID_207476991" MODIFIED="1696860986291" TEXT="bevor ich aufgebe ... versuche ich die komplexe L&#xf6;sung">
<arrowlink COLOR="#717cb7" DESTINATION="ID_1848128254" ENDARROW="Default" ENDINCLINATION="-84;4;" ID="Arrow_ID_501556845" STARTARROW="None" STARTINCLINATION="-14;-12;"/>
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
<node CREATED="1696861025916" ID="ID_1055867359" MODIFIED="1696861051215" TEXT="diese stellt sich (nat&#xfc;rlich) als viel schwieriger heraus als gedacht...">
<icon BUILTIN="smiley-angry"/>
</node>
<node CREATED="1696861063639" ID="ID_573856897" MODIFIED="1696861076422" TEXT="nach zwei erfolglosen Anl&#xe4;ufen setzte ich auf ein Builder-design">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#338800" CREATED="1696861055152" ID="ID_1482641387" MODIFIED="1696861092287" TEXT="durchgebissen; Problem gel&#xf6;st">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1696861129528" ID="ID_218116618" MODIFIED="1696861206606" TEXT="mit einer Builder-L&#xf6;sung l&#xe4;&#xdf;t sich das Race-Problem &#xbb;einhegen&#xab;">
<linktarget COLOR="#393653" DESTINATION="ID_218116618" ENDARROW="Default" ENDINCLINATION="20;-87;" ID="Arrow_ID_1655702369" SOURCE="ID_1852065850" STARTARROW="None" STARTINCLINATION="-29;62;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1696861147603" ID="ID_1523039562" MODIFIED="1696861176557" TEXT="das Design bleibt dadurch insgesamt fragw&#xfc;rdig">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1696861160426" ID="ID_1982657992" MODIFIED="1696861181025" TEXT="aber auf der Plus-Seite steht das Einkapseln von Komplexit&#xe4;t">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
@ -65604,8 +65638,8 @@
<node CREATED="1696687299435" ID="ID_1271362386" MODIFIED="1696687306334" TEXT="Konfiguration erm&#xf6;glichen">
<node COLOR="#5b280f" CREATED="1696687326411" ID="ID_682366843" MODIFIED="1696773642410" TEXT="BuilderQualfierSupport nutzbar machen">
<icon BUILTIN="button_cancel"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696687354848" ID="ID_1619359990" MODIFIED="1696687358528" TEXT="Einsatz kl&#xe4;ren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#435e98" CREATED="1696687354848" FOLDED="true" ID="ID_1619359990" MODIFIED="1696860882593" TEXT="Einsatz kl&#xe4;ren">
<icon BUILTIN="info"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1696687359967" ID="ID_1324063173" MODIFIED="1696687373111" TEXT="mix-in wo?">
<icon BUILTIN="help"/>
<node CREATED="1696687398879" ID="ID_477151920" MODIFIED="1696687401765" TEXT="Regeln">
@ -65626,7 +65660,7 @@
<node CREATED="1696690894145" ID="ID_1214632249" MODIFIED="1696690928341" TEXT="es gibt (Plan.A) einen separaten, dedizierten Konstruktor ebenda"/>
</node>
</node>
<node COLOR="#fe0548" CREATED="1696691310924" ID="ID_252708158" MODIFIED="1696773419438" TEXT="Qualifier-Konstruktor">
<node COLOR="#fe0548" CREATED="1696691310924" FOLDED="true" ID="ID_252708158" MODIFIED="1696860866131" TEXT="Qualifier-Konstruktor">
<icon BUILTIN="closed"/>
<node CREATED="1696773422943" ID="ID_155610622" MODIFIED="1696773469213" TEXT="es gelingt nicht, die Builder-Funktionen sichtbar zu machen">
<icon BUILTIN="stop-sign"/>
@ -65802,8 +65836,8 @@
</node>
<node CREATED="1696812766502" ID="ID_124892301" MODIFIED="1696812776928" TEXT="Thread-start funktioniert, auch mit Parameter"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1696812791066" ID="ID_1789709297" MODIFIED="1696817507333" TEXT="Code sinnvoll anordnen">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1696812791066" ID="ID_1789709297" MODIFIED="1696860711274" TEXT="Code sinnvoll anordnen">
<icon BUILTIN="button_ok"/>
<node CREATED="1696813183208" ID="ID_100161158" MODIFIED="1696813227123" TEXT="jetzt noch die invokeThreadFunctor-Memberfunktion mit in das Tupel packen..."/>
<node CREATED="1696813171800" ID="ID_155742882" MODIFIED="1696813229500" TEXT="Ha! std::make_from_tuple">
<icon BUILTIN="idea"/>
@ -65811,15 +65845,46 @@
<node COLOR="#338800" CREATED="1696816614379" ID="ID_1380705903" MODIFIED="1696816646902" TEXT="das Starten und Zuweisen in eine Hilfsfunktion &#x2b0a; in die ThreadWrapper-Basisklasse">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#6e4398" CREATED="1696817088763" ID="ID_1405496774" MODIFIED="1696817115630" TEXT="so langsam nimmt dieses Konstrukt eine Form an, mit der ich leben kann...">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#2c236a" CREATED="1696817088763" ID="ID_1405496774" MODIFIED="1696860766981" TEXT="so langsam nimmt dieses Konstrukt eine Form an, mit der ich leben kann...">
<icon BUILTIN="smiley-neutral"/>
</node>
<node COLOR="#338800" CREATED="1696860735220" ID="ID_1665003127" MODIFIED="1696860750337" TEXT="das &#x3bb;-chaining in Hilfsmethode extrahiert">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696817478656" ID="ID_1607424036" MODIFIED="1696817494294" TEXT="Umbau der bestehenden Konstruktoren">
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1696860784996" ID="ID_997556571" MODIFIED="1696861419491" TEXT="spezielle Builder-Methoden">
<linktarget COLOR="#8184a8" DESTINATION="ID_997556571" ENDARROW="Default" ENDINCLINATION="-767;-772;" ID="Arrow_ID_1317755297" SOURCE="ID_1893882898" STARTARROW="None" STARTINCLINATION="-380;22;"/>
<icon BUILTIN="hourglass"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696860795349" ID="ID_1603578610" MODIFIED="1696860801649" TEXT="atStart">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696860798554" ID="ID_772861645" MODIFIED="1696860801650" TEXT="atEnd">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1696860810473" ID="ID_1620627301" MODIFIED="1696860829244" TEXT="...more to follow...">
<font ITALIC="true" NAME="SansSerif" SIZE="10"/>
<icon BUILTIN="hourglass"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1696817478656" ID="ID_1607424036" MODIFIED="1696860385081" TEXT="Umbau der bestehenden Konstruktoren">
<icon BUILTIN="button_ok"/>
<node CREATED="1696817511875" ID="ID_5608930" MODIFIED="1696817549173" TEXT="zwar wird weiterhin der Konstruktor mit threadID + Funktor am meisten verwendet"/>
<node CREATED="1696817550022" ID="ID_1116307132" MODIFIED="1696817567896" TEXT="...aber der neue Konstruktor mit Builder wird nun zum Basis-Fall"/>
<node COLOR="#338800" CREATED="1696860375811" ID="ID_51657413" MODIFIED="1696860383427" TEXT="bisherige Tests und Test-Suite l&#xe4;uft">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1696860541853" ID="ID_1468973098" MODIFIED="1696860551928" TEXT="Applikation funktioniert (bis auf den bad_alloc)">
<icon BUILTIN="button_ok"/>
<node CREATED="1696860554011" ID="ID_465972946" MODIFIED="1696860559110" TEXT="aktueller status-quo"/>
<node COLOR="#338800" CREATED="1696860559906" ID="ID_1976226630" MODIFIED="1696860565601" TEXT="Steam-Dispatcher funktioniert">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1696860845588" ID="ID_1651635592" MODIFIED="1696860853973" TEXT="Konzept geht auf">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
@ -65834,15 +65899,16 @@
<node CREATED="1696538607372" ID="ID_479618351" MODIFIED="1696538611095" TEXT="f&#xfc;r Variante-1"/>
<node CREATED="1696538612261" ID="ID_661367597" MODIFIED="1696538614839" TEXT="f&#xfc;r Variante-2"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696690988844" ID="ID_480585061" MODIFIED="1696690995950" TEXT="das Race-Problem addressieren">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1696690988844" ID="ID_480585061" MODIFIED="1696861459249" TEXT="das Race-Problem addressieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1696690998156" ID="ID_394310225" MODIFIED="1696691010645" TEXT="der Thread wird erst aus dem Konstruktor-Rumpf gestartet"/>
<node CREATED="1696691012041" ID="ID_1592624785" MODIFIED="1696691021403" TEXT="daf&#xfc;r gibt es eine neue Policy-Funktion"/>
<node CREATED="1696691275142" ID="ID_178562434" MODIFIED="1696691300398" TEXT="aufgedoppelt f&#xfc;r den Qualifier-Konstruktor">
<node CREATED="1696691320304" ID="ID_1114482124" MODIFIED="1696691337153" TEXT="da dieser unbedingt vor dem Launch noch die Qualifier aktivieren mu&#xdf;"/>
<node CREATED="1696691363258" ID="ID_1604122768" MODIFIED="1696691380443" TEXT="es sei den... man macht den Qualifier-Konstruktor zum Basisfall"/>
</node>
<node CREATED="1696691391111" ID="ID_939791093" MODIFIED="1696691405089" TEXT="Problem: Funktor-Argumentliste">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696691391111" FOLDED="true" ID="ID_939791093" MODIFIED="1696861489816" TEXT="Problem: Funktor-Argumentliste">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1696691415615" ID="ID_167830298" MODIFIED="1696691425773" TEXT="im einfachen Fall problemlos (perfect forwarding)"/>
<node CREATED="1696691426433" ID="ID_921129676" MODIFIED="1696691497844" TEXT="aber mit Qualifier? pa&#xdf;t nicht so recht">
<node CREATED="1696691501159" ID="ID_1948526889" MODIFIED="1696691527488" TEXT="diese Argumentliste wirkt nicht wie ein Setter"/>
@ -65884,6 +65950,9 @@
<arrowlink COLOR="#ab3573" DESTINATION="ID_768133943" ENDARROW="Default" ENDINCLINATION="-135;7;" ID="Arrow_ID_222249914" STARTARROW="None" STARTINCLINATION="186;8;"/>
<icon BUILTIN="closed"/>
</node>
<node COLOR="#338800" CREATED="1696861442309" ID="ID_252350603" MODIFIED="1696861456905" TEXT="L&#xf6;sung mit Konfigurations-Builder ist durchf&#xfc;hrbar">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1696538743210" ID="ID_1370366397" MODIFIED="1696538750437" TEXT="Tests erg&#xe4;nzen">
@ -95379,6 +95448,59 @@ class Something
</node>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696859902445" ID="ID_428140801" MODIFIED="1696859968378" TEXT="Beobachtungen...">
<icon BUILTIN="help"/>
<node CREATED="1696859972822" FOLDED="true" ID="ID_1229021778" MODIFIED="1696860322728" TEXT="sporadisch gescheiterte Tests">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1696859985804" ID="ID_798414043" MODIFIED="1696860302374" TEXT="23-10-09: JobPlanning_test">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Einmal-Event beim Ausf&#252;hren der Testsuite nach Compile.
</p>
<p>
<font face="Monospaced">TEST Render job planning calculation: JobPlanning_test .. FAILED </font>
</p>
<p>
<font face="Monospaced">unexpected data on stdout </font>
</p>
<p>
<font face="Monospaced">'total latency&#160;&#160;&#160;&#160;: &#8826;11ms&#8827;':1 </font>
</p>
<p>
<font face="Monospaced">does not match </font>
</p>
<p>
<font face="Monospaced">literal: total latency&#160;&#160;&#160;&#160;: &#8826;30ms&#8827;:1 </font>
</p>
<p>
Bemerkenswert:
</p>
<ul>
<li>
der Test selber scheitert nicht, weil die Berechnung wie erwartet abl&#228;uft
</li>
<li>
aber die berichtete Latency ist 11ms statt 30ms wie erwartet
</li>
<li>
das bedeutet: das JobTicket -&gt; isEmpty() [weil dann fallback auf JOB_MINIMUM_RUNTIME in JobTicket::getExpectedRuntime ]
</li>
<li>
ohne das nun im Detail zu analysieren: ich bin beunruhigt, wie KANN das JobTicket empty sein -- es wird doch hier im Test explizit aus der MockFixture erstellt
</li>
</ul>
<p>
Kontext: bin grade am Umbauen des Thread-Frameworks...
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1689610305547" ID="ID_1569437785" MODIFIED="1689610311743" TEXT="QA-testing">
<icon BUILTIN="hourglass"/>
<node CREATED="1689610320586" ID="ID_1544042895" MODIFIED="1689610323417" TEXT="Acceptance"/>