Library: test coverage for self-managed thread

...extract and improve the tuple-rewriting function
...improve instance tracking test dummy objects
...complete test coverage and verify proper memory handling
This commit is contained in:
Fischlurch 2023-10-11 21:06:56 +02:00
parent f6a6b0b68f
commit 7b25609896
9 changed files with 449 additions and 113 deletions

View file

@ -205,6 +205,54 @@ namespace meta{
/** Placeholder marker for a special argument position to be supplied later */
template<class TAR>
struct InstancePlaceholder { };
namespace {// Helper to inject instance-pointer instead of placeholder...
/**
* @internal helper to detect an InstancePlaceholder
* @return an instance-pointer, statically casted to the marked target type.
*/
template<class W, class TAR>
constexpr inline TAR*
maybeInject (W& instance, InstancePlaceholder<TAR>)
{
return static_cast<TAR*> (&instance);
}
/** (default case: fall-through) */
template<class W, typename X>
constexpr inline X
maybeInject (W&, X&& x)
{
return std::move(x);
}
}//(End)Helper for lateBindInstance.
/**
* Fix-up the arguments for a member-function invocation,
* allowing to inject the actual `this` instance into an existing argument sequence.
* @remark invocation of a member function requires to supply the _object instance_ as
* first element in the argument list; sometimes this poses a design challenge,
* since the actual instance may not be known at the point where the other arguments
* are prepared. As a remedy, the position of the instance pointer can be marked with
* the \ref InstancePlaceholder, allowing to splice in the actual pointer when known.
*/
template<class W, class TUP>
constexpr inline auto
lateBindInstance (W& instance, TUP&& invocation)
{
auto splice = [&instance](auto&& ...xs)
{
return std::tuple{maybeInject (instance, std::move(xs))...};
};
return std::apply (splice, std::forward<TUP> (invocation));
}
@ -214,7 +262,7 @@ namespace meta{
* and the bare function signature #Sig
* @param RET the function return type
* @param ARGS a type sequence describing the arguments
*/
*/ //////////////////////////////////////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic, then replace this by a single variadic template
template<typename RET, typename ARGS>
struct FunctionTypedef;

View file

@ -49,6 +49,8 @@ namespace test{
long Dummy::_local_checksum = 0;
bool Dummy::_throw_in_ctor = false;
EventLog Tracker::log{"Instance-Tracker"};
string

View file

@ -22,23 +22,32 @@
/** @file testdummy.hpp
** unittest helper code: test dummy object.
** This dummy includes some distinct random identity marker
** plus a checksum facility to verify instance management, i.e.
** to verify each created instance was properly destroyed after use.
** unittest helper code: test dummy objects to track instances.
** These can be used to verify proper allocation handling, either by
** watching the checksum of \ref Dummy, or by matching on the \ref EventLog
** embedded into \ref Tracker.
*/
#include "lib/nocopy.hpp"
#include "lib/test/event-log.hpp"
#include "lib/format-string.hpp"
#include "lib/util.hpp"
#include <algorithm>
#include <limits>
namespace lib {
namespace test{
class Dummy
/**
* A Dummy object for tests. Each instance includes some distinct
* random identity marker plus a checksum facility to verify instance management,
* i.e. to verify each created instance was properly destroyed after use.
*/
class Dummy
: util::NonCopyable
{
int val_;
@ -62,6 +71,7 @@ namespace test{
}
virtual long
acc (int i) ///< dummy API operation
{
return val_+i;
@ -107,8 +117,100 @@ namespace test{
if (_throw_in_ctor)
throw val_;
}
};
/**
* A tracking Dummy object for tests.
* All construction-, destruction and copy operations
* are logged into an embedded lib::test::EventLog
*/
struct Tracker
{
static lib::test::EventLog log;
static constexpr int DEFUNCT = std::numeric_limits<int>::min();
static constexpr int DEAD = std::numeric_limits<int>::max();
int val;
~Tracker()
{
log.call (this,"dtor", val);
val = DEAD;
}
Tracker()
: val{rand() % 1000}
{
log.call (this,"ctor");
}
Tracker(int v)
: val{v}
{
log.call (this,"ctor", v);
}
Tracker(Tracker const& ol)
: val{ol.val}
{
log.call (this,"ctor-copy", ol);
}
Tracker(Tracker && oo)
: val{oo.val}
{
log.call (this,"ctor-move", oo);
oo.val = DEFUNCT;
}
Tracker&
operator= (Tracker const& ol)
{
if (util::isSameObject (*this, ol))
{
log.call (this, "self-assign-copy");
}
else
{
log.call (this, "assign-copy", ol);
val = ol.val;
}
return *this;
}
Tracker&
operator= (Tracker && oo)
{
if (util::isSameObject (*this, oo))
{
log.call (this, "self-assign-move");
}
else
{
log.call (this, "assign-move", oo);
val = oo.val;
oo.val = DEFUNCT;
}
return *this;
}
operator string() const
{
return util::_Fmt{"Track{%02d}"} % val;
}
friend void
swap (Tracker& t1, Tracker& t2)
{
log.call ("static", "swap", t1, t2);
std::swap (t1.val, t2.val);
}
};
}} // namespace lib::test

View file

@ -150,6 +150,7 @@ namespace lib {
namespace thread {// Thread-wrapper base implementation...
using lib::meta::lateBindInstance;
using lib::meta::typeSymbol;
using lib::meta::_Fun;
using util::isnil;
@ -360,27 +361,6 @@ namespace lib {
}
};
template<class TAR>
struct InstancePlaceholder { };
template<class W, class INVO>
inline INVO
lateBindInstance (W&, INVO invocation)
{
return invocation;
}
template<class W, class F, class TAR, typename...ARGS>
inline auto
lateBindInstance (W& instance, const tuple<F, InstancePlaceholder<TAR>, ARGS...> invocation)
{
auto splice = [&instance](auto f, auto&, auto ...args)
{
TAR* instancePtr = static_cast<TAR*> (&instance);
return tuple{move(f), instancePtr, move(args)...};
};
return std::apply (splice, invocation);
}
/**
* Policy-based configuration of thread lifecycle
@ -784,7 +764,7 @@ namespace lib {
{
using Launch = typename TAR::Launch;
launchDetached<TAR> (Launch{std::move (memFun)
,thread::InstancePlaceholder<TAR>{}
,lib::meta::InstancePlaceholder<TAR>{}
,forward<ARGS> (args)...
}
.threadID (threadID));
@ -795,10 +775,10 @@ namespace lib {
inline void
launchDetached (void (TAR::*memFun) (ARGS...), ARGS ...args)
{
return launchDetached (util::joinDash (lib::meta::typeSymbol<TAR>(), args...)
,memFun
,forward<ARGS> (args)...
);
launchDetached (util::joinDash (lib::meta::typeSymbol<TAR>(), args...)
,memFun
,forward<ARGS> (args)...
);
}

View file

@ -42,7 +42,7 @@ return: 0
END
PLANNED "Launch self-contained detached Thread" ThreadWrapperAutonomous_test <<END
TEST "Launch self-contained detached Thread" ThreadWrapperAutonomous_test <<END
return: 0
END

View file

@ -238,6 +238,11 @@ return: 0
END
TEST "member function late binding" LateBindInstance_test <<END
return: 0
END
TEST "metaprogramming helpers" MetaUtils_test <<END
return: 0
END

View file

@ -0,0 +1,196 @@
/*
LateBindInstance(Test) - verify rewriting of member function invocation
Copyright (C) Lumiera.org
2023, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file late-bind-instance-test.cpp
** unit test \ref LateBindInstance_test
*/
#include "lib/test/run.hpp"
#include "lib/meta/function.hpp"
#include "lib/meta/tuple-helper.hpp"
#include "lib/test/testdummy.hpp"
#include "lib/format-cout.hpp"
#include "lib/format-util.hpp"
#include "lib/test/diagnostic-output.hpp"
#include "lib/rational.hpp"
#include "lib/util.hpp"
#include <tuple>
#include <string>
#include <cstdlib>
namespace lib {
namespace meta {
namespace test {
using std::move;
using std::rand;
using std::tuple;
using std::string;
using lib::meta::dump;
using lib::test::Tracker;
/**********************************************************************************//**
* @test verify a scheme to supply the actual instance for member function invocation.
* @remark in the end this is just a value replacement in a tuple, where the position
* is marked with a placeholder type. This scheme is used for starting threads,
* where the actual thread instance is not known yet at the point where the
* arguments for the thread-function are constructed
* @see lib::launchDetached
*/
class LateBindInstance_test : public Test
{
void
run (Arg)
{
demonstrateUsage();
verify_forwarding();
verify_cornerCases();
}
/** @test demonstrate the usage of lib::meta::lateBindInstance():
* - construct an argument tuple
* - mark the position of the `this`-ptr with a placeholder
* - rewrite the arguments later when the actual instance is known
* - member function can now be invoked, since the instance was injected.
*/
void
demonstrateUsage()
{
uint randomLimit = 2 + rand() % 98;
auto plannedArgs
= tuple{InstancePlaceholder<LateBindInstance_test>{}
,randomLimit
};
// the actual instance may be available only later...
Test& instanceRef = *this;
// now rewrite the argument tuple to inject the instance-ptr
auto preparedArgs = lateBindInstance (instanceRef, plannedArgs);
uint res = std::apply (&LateBindInstance_test::theMember, preparedArgs);
CHECK (res < randomLimit);
}
uint
theMember (uint limit)
{
return rand() % limit;
}
/** @test verify that the rewriting process does not incur unnecessary data copies
*/
void
verify_forwarding()
{
auto& log = Tracker::log;
log.clear (this);
InstancePlaceholder<LateBindInstance_test> marker;
Tracker t1(11);
log.event("construct tuple");
tuple tup(t1, marker, Tracker{23}, 55);
Test& instanceRef = *this;
log.event("invoke lateBindInstance");
auto tup1r = lateBindInstance (instanceRef, tup);
log.event("got result");
// before invocation the Tracker instances are created (obviously..)
CHECK (log.verifyCall("ctor").arg(11)
.beforeCall("ctor-copy").arg("Track{11}")
.beforeEvent("invoke lateBindInstance"));
CHECK (log.verifyCall("ctor").arg(23)
.beforeCall("ctor-move").arg("Track{23}")
.beforeCall("dtor").arg(Tracker::DEFUNCT)
.beforeEvent("invoke lateBindInstance"));
// but there is no copy operation after the invocation
CHECK (log.ensureNot("ctor-copy")
.afterEvent("invoke lateBindInstance"));
// both Tracker instances are moved two times
// - once to create the maybeInject-invocation and
// - once to consolidate the result
// for each instance one moved-away temporary is destroyed
CHECK (log.verifyEvent("invoke lateBindInstance")
.beforeCall("ctor-move").arg("Track{11}")
.beforeCall("ctor-move").arg("Track{11}")
.beforeCall("dtor").arg(Tracker::DEFUNCT)
.beforeEvent("got result"));
CHECK (log.verifyEvent("invoke lateBindInstance")
.beforeCall("ctor-move").arg("Track{23}")
.beforeCall("ctor-move").arg("Track{23}")
.beforeCall("dtor").arg(Tracker::DEFUNCT)
.beforeEvent("got result"));
cout << "____Tracker-Log_______________\n"
<< util::join(Tracker::log, "\n")
<< "\n───╼━━━━━━━━━━━╾──────────────"<<endl;
}
/** @test replacement happens uniformely on all kinds of tuples
*/
void
verify_cornerCases()
{
long dummy{555};
InstancePlaceholder<long> marker;
CHECK (dump(lateBindInstance (dummy, tuple<>{})) == "()"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{42})) == "(42)"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{1,2,3})) == "(1,2,3)"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{marker,2,3})) == "(«long»,2,3)"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{1,marker,3})) == "(1,«long»,3)"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{1,2,marker})) == "(1,2,«long»)"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{marker})) == "(«long»)"_expect);
CHECK (dump(lateBindInstance (dummy, tuple{string{"1"}
,"2"
,marker
,tuple(2,3)
, 5.5
})) == "(1,2,«long»,«tuple<int, int>»──(2,3),5.5)"_expect);
}
};
/** Register this test class... */
LAUNCHER (LateBindInstance_test, "unit meta");
}}} // namespace lib::meta::test

View file

@ -28,26 +28,18 @@
#include "lib/test/run.hpp"
#include "lib/test/testdummy.hpp"
#include "lib/thread.hpp"
#include "lib/test/diagnostic-output.hpp"///////////////////TODO
#include <atomic>
#include <chrono>
using test::Test;
using std::atomic_uint;
using std::this_thread::yield;
using std::atomic_bool;
using std::this_thread::sleep_for;
using namespace std::chrono_literals;
namespace lib {
namespace test{
namespace { // test parameters
const uint NUM_THREADS = 200;
const uint REPETITIONS = 10;
}
/*******************************************************************//**
@ -71,11 +63,11 @@ namespace test{
void
demonstrateSimpleUsage()
{
atomic_uint i{0};
launchDetached ("anarchy", [&]{ ++i; });
atomic_bool didRun{false};
launchDetached ("anarchy", [&]{ didRun = true; });
sleep_for(1ms);
CHECK (i == 1); // verify the effect has taken place
CHECK (didRun); // verify the effect has taken place
}
@ -96,17 +88,17 @@ namespace test{
doIt (int extra)
{
watcher.setVal (extra);
SHOW_EXPR(extra)
sleep_for (5ms);
SHOW_EXPR("bye")
}
};
//
// Note the Dummy member allows to watch instance lifecycle
CHECK (0 == Dummy::checksum());
//
launchDetached<TestThread> (&TestThread::doIt, 55);
CHECK (0 < Dummy::checksum());
sleep_for (1ms);
CHECK (55 == Dummy::checksum());
sleep_for (10ms);
CHECK (0 == Dummy::checksum());
}

View file

@ -65187,7 +65187,7 @@
</node>
<node COLOR="#435e98" CREATED="1696529645817" ID="ID_461966137" MODIFIED="1696966078558" TEXT="Varianten/Weiterungen">
<icon BUILTIN="list"/>
<node CREATED="1696529655008" ID="ID_1683255981" MODIFIED="1696529699438" TEXT="Variante-1 : self-managed">
<node COLOR="#435e98" CREATED="1696529655008" ID="ID_1683255981" MODIFIED="1697050739549" TEXT="Variante-1 : self-managed">
<node CREATED="1696529705915" ID="ID_731698797" MODIFIED="1696532569698" TEXT="in der Policy liegt ein unique_ptr">
<richcontent TYPE="NOTE"><html>
<head/>
@ -65348,13 +65348,12 @@
</node>
<node CREATED="1696965973496" ID="ID_1562329556" MODIFIED="1696965986393" TEXT="und eine sonderbare spezial-Policy f&#xe4;llt komplett weg"/>
<node CREATED="1696965999636" ID="ID_1837122164" MODIFIED="1696966015867" TEXT="es gibt keine spezielle Thread-Klasse mehr, die man &#xbb;nicht verwenden&#xab; soll"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1696976539898" ID="ID_57182560" MODIFIED="1696976562311" TEXT="ABER: kann mit diesem Design keine Member-Funktionen in den Thread binden">
<node COLOR="#435e98" CREATED="1696976539898" ID="ID_57182560" MODIFIED="1697050703737" TEXT="ABER: kann mit diesem Design keine Member-Funktionen in den Thread binden">
<linktarget COLOR="#5d6faa" DESTINATION="ID_57182560" ENDARROW="Default" ENDINCLINATION="-230;791;" ID="Arrow_ID_1090153547" SOURCE="ID_92765841" STARTARROW="None" STARTINCLINATION="772;-34;"/>
<icon BUILTIN="broken-line"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696976568054" ID="ID_1651696265" MODIFIED="1696976852099" TEXT="das macht abgeleitete Klassen effektiv sinnlos">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
denn: grade hier f&#252;r den &#187;Autonomous Thread&#171; kommt man von au&#223;en nicht an die Addresse des Objektes ran
@ -65371,60 +65370,50 @@
</li>
</ul>
</body>
</html>
</richcontent>
</html></richcontent>
<linktarget COLOR="#f73362" DESTINATION="ID_1651696265" ENDARROW="Default" ENDINCLINATION="-61;-30;" ID="Arrow_ID_120849360" SOURCE="ID_1670280316" STARTARROW="None" STARTINCLINATION="283;19;"/>
</node>
<node CREATED="1696976853336" ID="ID_372976819" MODIFIED="1696976877733" TEXT="das f&#xfc;hrt die Idee mit einer selbst-verwalteten Allokation ad absurdum">
<icon BUILTIN="smiley-angry"/>
</node>
<node CREATED="1696976940356" ID="ID_232121625" MODIFIED="1696977061323" TEXT="hier r&#xe4;cht sich auch das &#xbb;Zwiebelschalen-Design&#xab;: der Launch-builder ist opaque"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1696978145159" ID="ID_314579443" MODIFIED="1696978153755" TEXT="gibt es &#xfc;berhaupt einen Fix?">
<node COLOR="#435e98" CREATED="1696978145159" ID="ID_314579443" MODIFIED="1697050413021" TEXT="gibt es &#xfc;berhaupt einen Fix?">
<icon BUILTIN="help"/>
<node COLOR="#5b280f" CREATED="1696978155858" ID="ID_229604383" MODIFIED="1696978809501" TEXT="&#xfc;bler Hack: Fake-Allokation + placement-New">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Wenn wir es irgendwie schaffen k&#246;nnten, das Objekt schon auf den Heap zu allozieren, aber zu verhindern, da&#223; es &#8222;fliegt&#8220; (bzw. wirklich konstruiert wird). Dann w&#252;&#223;ten wir die Addresse. Danach w&#252;rden wir ein placement-New in die Allokation machen, und k&#246;nnten diesem <i>tats&#228;chlichen </i>Konstruktor-Aufruf <i>ganz vertr&#228;umt </i>den richtigen Instanz-Pointer mitgeben. Vorraussetzung w&#228;re allerdings, da&#223; sich diese manipulierte Allokation sp&#228;ter ganz gew&#246;hnlich per operator delete wieder entfernen lie&#223;e. Das w&#228;re dann entweder ein fragiler Hack, der sich auf Plattform-Interna abst&#252;tzt, oder es w&#228;re ein custom-operator-new im Stil der 90er-Jahre
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="closed"/>
</node>
<node CREATED="1696978523249" ID="ID_1356286451" MODIFIED="1697023022905" TEXT="einen Platzhalter sp&#xe4;t an die eigentliche Instanz binden">
<richcontent TYPE="NOTE">&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;
Daf&amp;#252;r br&amp;#228;uchte ich eine Stelle, an der die tats&amp;#228;chliche Instanz bereits
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Daf&#252;r br&#228;uchte ich eine Stelle, an der die tats&#228;chliche Instanz bereits
bekannt ist, aber die Invocation nochin generischer Form vorliegt. In
einem solchen Kontext k&amp;#246;nnte man dann einen Marker-Typ erkennen und an
die tats&amp;#228;chliche Instanz binden.
&lt;/p&gt;
&lt;p&gt;
einem solchen Kontext k&#246;nnte man dann einen Marker-Typ erkennen und an
die tats&#228;chliche Instanz binden.
</p>
<p>
&lt;/p&gt;
&lt;p&gt;
&lt;u&gt;&amp;#55357;&amp;#56481; Und in der Tat&lt;/u&gt;: diesen Kontext g&amp;#228;be es in der
</p>
<p>
<u>&#55357;&#56481; Und in der Tat</u>: diesen Kontext g&#228;be es in der
Hilfsfunktion buildLauncher()
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</richcontent>
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#3551b4" DESTINATION="ID_1017963709" ENDARROW="Default" ENDINCLINATION="8;-31;" ID="Arrow_ID_855336150" STARTARROW="None" STARTINCLINATION="-132;6;"/>
<icon BUILTIN="forward"/>
<node CREATED="1696978822594" ID="ID_1410342065" MODIFIED="1696979058894" TEXT="diese L&#xf6;sung w&#xe4;re zuverl&#xe4;ssig, sicher und effizient">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<ul>
<li>
@ -65438,13 +65427,12 @@
</li>
</ul>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1696978865091" ID="ID_1134916435" MODIFIED="1696978893087" TEXT="aber sch&#xf6;n ist sie nicht &#x2014; bestenfalls gut versteckt"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696979065849" ID="ID_1017963709" MODIFIED="1697023014065" TEXT="Umsetzung versuchen">
<node COLOR="#435e98" CREATED="1696979065849" ID="ID_1017963709" MODIFIED="1697050405966" TEXT="Umsetzung versuchen">
<linktarget COLOR="#3551b4" DESTINATION="ID_1017963709" ENDARROW="Default" ENDINCLINATION="8;-31;" ID="Arrow_ID_855336150" SOURCE="ID_1356286451" STARTARROW="None" STARTINCLINATION="-132;6;"/>
<icon BUILTIN="yes"/>
<node CREATED="1697022864978" ID="ID_360386085" MODIFIED="1697022989269" TEXT="im Prinzip &#x201e;ganz einfach&#x201c;">
@ -65457,6 +65445,33 @@
<icon BUILTIN="smiley-angry"/>
</node>
</node>
<node COLOR="#338800" CREATED="1697050416768" ID="ID_1615729676" MODIFIED="1697050582236" TEXT="die Hilfsfunktion ...">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1697050430395" ID="ID_1669320853" MODIFIED="1697050438782" TEXT="extrahieren &#x27f6; lib/function.hpp">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1697050439986" ID="ID_1328885131" MODIFIED="1697050554033" TEXT="k&#xf6;nnte einfacher und generisher sein">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Es gibt keinen Grund, die Ersetzung nur an 2.Stelle im Tupel zu machen; auch ist es nicht notwendig, die Typen irgendwie festzunageln. Allein die Gegenwart des Marker-Typs gen&#252;gt, denn diesen setzt man nur explizit ein. Damit w&#228;re diese Funktion auch auf reine Argument-Tupel anwendbar, nicht nur (wie hier) auf Invocation-Tupel (functor, args...)
</p>
</body>
</html></richcontent>
<icon BUILTIN="yes"/>
</node>
<node COLOR="#338800" CREATED="1697050554898" ID="ID_1031448830" MODIFIED="1697050570497" TEXT="sollte jedwede Kopie vermeiden">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1697050571552" ID="ID_333850507" MODIFIED="1697050580451" TEXT="...und geh&#xf6;rt im Test abgedeckt">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#338800" CREATED="1697050588182" ID="ID_1656504822" MODIFIED="1697050631074" TEXT="Problem damit gel&#xf6;st">
<arrowlink COLOR="#07a068" DESTINATION="ID_1379899935" ENDARROW="Default" ENDINCLINATION="479;18;" ID="Arrow_ID_645882664" STARTARROW="None" STARTINCLINATION="268;31;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
@ -65877,44 +65892,35 @@
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696972878105" ID="ID_880873658" MODIFIED="1696972924189">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
allerdings: abgeleitete Klassen <b>leben gef&#228;hrlich</b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1696972938795" ID="ID_1194001800" MODIFIED="1696972960949" TEXT="wenn im Konstruktor etwas schiefgeht"/>
<node CREATED="1696972967768" ID="ID_1795585703" MODIFIED="1696972989481" TEXT="dann gelingt nicht einmal die Heap-Allokation vollst&#xe4;ndig"/>
<node CREATED="1696973111469" ID="ID_229806295" MODIFIED="1696973186990" TEXT="und der Destruktor blockt und scheitert nicht...">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Ein normaler Thread (und ein Joinable) w&#252;rden den Aufruf des Destruktors verbieten, und damit die Applikation terminieren
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1696972994851" ID="ID_983723928" MODIFIED="1696973016584">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
aber der Thread <i>ist schon &#8222;unterwegs&#8220;</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="clanbomber"/>
</node>
<node COLOR="#435e98" CREATED="1696973047334" ID="ID_1485957927" MODIFIED="1696973196674" TEXT="Mitigation: nicht in die Thread-Funktion eintreten wenn nicht isAlive()">
@ -65923,16 +65929,13 @@
<node CREATED="1696973227990" ID="ID_481018550" MODIFIED="1696976336073" TEXT="aber wenn jemand explizit detach() macht..."/>
<node CREATED="1696973241644" ID="ID_1093970050" MODIFIED="1696973309225" TEXT="eigentlich m&#xfc;&#xdf;te man diesen Fall auf einen &#xbb;Autonomous Thread&#xab; einschr&#xe4;nken">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...aber das gibt mein Framework nicht her
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -66301,14 +66304,22 @@
</node>
</node>
<node CREATED="1696538743210" ID="ID_1370366397" MODIFIED="1696538750437" TEXT="Tests erg&#xe4;nzen">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696538756712" ID="ID_1688404846" MODIFIED="1696541608160" TEXT="ThreadWrapperAutonomous_test">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1696538756712" ID="ID_1688404846" MODIFIED="1697050726434" TEXT="ThreadWrapperAutonomous_test">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1696539571988" ID="ID_791561993" MODIFIED="1696966349054" TEXT="demonstriert: einen Thread komplett abgekoppelt starten">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696966318864" ID="ID_1955344707" MODIFIED="1696966351181" TEXT="sauberes Memory-Management belegen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1696966318864" ID="ID_1955344707" MODIFIED="1697050723749" TEXT="sauberes Memory-Management belegen">
<icon BUILTIN="button_ok"/>
<node CREATED="1696971867646" ID="ID_1915512401" MODIFIED="1696971888573" TEXT="lib/test/testdummy.hpp &#xd83e;&#xdc7a; als Member verwenden"/>
<node COLOR="#435e98" CREATED="1697050651493" ID="ID_92765841" MODIFIED="1697050712336" TEXT="AUA: das mit der member-Funktion geht ja gar nicht">
<arrowlink COLOR="#5d6faa" DESTINATION="ID_57182560" ENDARROW="Default" ENDINCLINATION="-230;791;" ID="Arrow_ID_1090153547" STARTARROW="None" STARTINCLINATION="772;-34;"/>
<icon BUILTIN="broken-line"/>
</node>
<node COLOR="#338800" CREATED="1697050598820" ID="ID_1379899935" MODIFIED="1697050718681" TEXT="Demonstration: kann in der Tat auch eine Member-Funktion aufrufen">
<linktarget COLOR="#07a068" DESTINATION="ID_1379899935" ENDARROW="Default" ENDINCLINATION="479;18;" ID="Arrow_ID_645882664" SOURCE="ID_1656504822" STARTARROW="None" STARTINCLINATION="268;31;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696538825003" ID="ID_1787030725" MODIFIED="1696539451332" TEXT="ThreadWrapperLifecycle_test">