Library: draft a SyncBarrier latch

Intended as replacement for the Mutex/ConditionVar based barrier
built into the exiting Lumiera thread handling framework and used
to ensure safe hand-over of a bound functor into the starting new
thread. The standard requires a comparable guarantee for the C++17
concurrency framework, expressed as a "synchronizes_with" assertion
along the lines of the Atomics framework.

While in most cases dedicated synchronisation is thus not required
anymore when swtiching to C++17, some special extended use cases
remain to be addressed, where the complete initialisation of
further support framework must be ensured.

With C++20 this would be easy to achieve with a std::latch, so we
need a simple workaround for the time being. After consideration of
the typical use case, I am aiming at a middle ground in terms of
performance, by using a yield-wait until satisfying the latch condition.
This commit is contained in:
Fischlurch 2023-09-22 21:55:53 +02:00
parent 416895b5b2
commit 6735857f3b
6 changed files with 397 additions and 28 deletions

View file

@ -61,6 +61,7 @@
** - in a similar vein, the *filter operation* binds a predicate to decide about using or discarding data
** - in concert, expand- and transform operation allow to build hierarchy evaluation algorithms without exposing
** any knowledge regarding the concrete hierarchy used and explored as data source.
** - further special convenience adaptors and _terminal functions_ are provided.
**
** In itself, the IterExplorer is an iterator with implementation defined type (all operations being inlined).
** But it is possible to package this structure behind a conventional iteration interface with virtual functions.
@ -1586,6 +1587,20 @@ namespace lib {
return SRC {move(*this)};
}
/**
* _terminal builder_ to invoke a functor for side effect on the complete pipeline.
* @note exhausts and discards the pipeline itself
*/
template<class FUN>
void
foreach (FUN&& consumer)
{
auto consumeFun = iter_explorer::_FunTraits<FUN,SRC>::adaptFunctor (forward<FUN> (consumer));
SRC& pipeline = *this;
for ( ; pipeline; ++pipeline)
consumeFun (pipeline);
}
/** _terminal builder_ to pour and materialise all results from this Pipeline.
* @tparam CON a STL compliant container to store generated values (defaults to `vector`)
* @return new instance of the target container, filled with all values

85
src/lib/sync-barrier.hpp Normal file
View file

@ -0,0 +1,85 @@
/*
SYNC-BARRIER.hpp - N-fold synchronisation point with yield-wait
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 sync-barrier.hpp
** A N-fold synchronisation latch using yield-wait until fulfilment.
** Semantically, this barrier is similar to std::latch (introduced with C++20).
** It is intended for one-time use and blocks any invocation until N invocations occurred.
** The »wait« however is implemented as a spinning loop with `std::this_thread::yield`,
** which places this implementation into a middle ground between a full-fledged blocking
** lock and a busy spinning log. The `yield()` call is expected to de-prioritise the current
** thread behind all other threads currently amenable for active execution, without actually
** placing the thread into sleeping state. The coordination of the latch relies on Atomics.
** @see SyncBarrier_test
** @see steam::control::DispatcherLoop
** @todo as of 9/2023 it remains to be seen if this facility is just a pre-C++20 workaround;
** otherwise it may present distinct performance characteristics than std::latch,
** possibly also a slightly more abstracted (and thus clearer) usage API.
*/
#ifndef LIB_SYNC_BARRIER_H
#define LIB_SYNC_BARRIER_H
#include "lib/error.hpp"
#include "lib/nocopy.hpp"
//#include "include/logging.h"
//#include "lib/meta/function.hpp"
//#include "lib/result.hpp"
//#include <utility>
#include <atomic>
namespace lib {
// using lib::Literal;
// namespace error = lumiera::error;
// using error::LERR_(STATE);
// using error::LERR_(EXTERNAL);
/**
* @todo write type comment
*/
class SyncBarrier
: util::NonCopyable
{
public:
explicit
SyncBarrier (size_t nFold =2)
{ }
void
sync()
{
UNIMPLEMENTED ("count-down latch logic");
}
};
} // namespace lib
#endif /*LIB_SYNC_BARRIER_H*/

View file

@ -0,0 +1,148 @@
/*
SyncBarrier(Test) - verify yield-waiting synchronisation latch
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 sync-barrier-test.cpp
** unit test \ref SyncBarrier_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
//#include "lib/thread.hpp"
#include "lib/sync-barrier.hpp"
#include "lib/iter-explorer.hpp"
#include "lib/util-foreach.hpp"
#include "lib/error.hpp"
//#include <utility>
#include <chrono>
#include <thread>
#include <atomic>
#include <array>
using test::Test;
using util::and_all;
using lib::explore;
using std::atomic_uint;
using std::array;
using std::this_thread::sleep_for;
using namespace std::chrono_literals;
namespace lib {
namespace test {
namespace {
const uint NUM_THREADS = 1024;
atomic_uint stage1{0};
atomic_uint stage2{0};
atomic_uint finish{0};
SyncBarrier interThread{NUM_THREADS};
SyncBarrier afterThread{NUM_THREADS+1};
/**
* A test thread to perform a summation protocol including synchronisation points
* - build a compound sum of random numbers in the first stage
* - wait for the compound sum to build up completely
* - book in the compound sum plus a further random number
*/
class TestThread
: std::thread ////////////////////////////////////////////////////////////////////OOO TOD-oh
{
public:
TestThread()
: thread{[&]()
{ //-STAGE-1------------------------------
localSum = rand() % 1000; // generate local value
stage1.fetch_add (localSum); // book in local value
interThread.sync(); // wait for all other threads to have booked in
//-STAGE-2------------------------------
localSum += stage1; // pick up compounded sum from STAGE-1
localSum += rand() % 1000; // add further local value for STAGE-2
stage2.fetch_add (localSum); // book in local sum
afterThread.sync(); // wait for other threads and supervisor
finish.fetch_add(1); // mark completion of this thread
thread::detach(); //////////////////////////////////////////////OOO Wech-oh
}}
{ }
uint localSum; // *deliberately* not initialised to avoid race
bool isRunning() const { return thread::joinable(); } ///////////////////////OOO Wack-oh
};
/** sum up all `localSum` fields from all TestThread instances in a container */
template<class CON>
uint
sumLocals (CON const& threads)
{
uint sum{0};
explore (threads)
.foreach ([&](TestThread const& t){ sum += t.localSum; });
return sum;
}
}
/*******************************************************************//**
* @test verify N-fold synchronisation points by multi-thread loat-test.
*
* @see lib::SyncBarrier
* @see steam::control::DispatcherLoop
*/
class SyncBarrier_test : public Test
{
virtual void
run (Arg)
{
array<TestThread,NUM_THREADS> threads;
CHECK (0 == finish);
CHECK (and_all (threads, [](auto& t){ return t.isRunning(); }));
afterThread.sync();
sleep_for (1ms); // give the threads a chance to terminate
CHECK (NUM_THREADS == finish); // all threads have passed out....
CHECK (0 < stage1);
CHECK (stage1 < stage2);
CHECK (stage2 > sumLocals(threads));
CHECK (stage2 == sumLocals(threads) + NUM_THREADS*stage1); // this holds only if all threads waited to get the complete stage1 sum
}
};
/** Register this test class... */
LAUNCHER (SyncBarrier_test, "function common");
}} // namespace lib::test

View file

@ -20,6 +20,10 @@
* *****************************************************/
/** @file thread-wrapper-self-recognition-test.cpp
** unit test \ref ThreadWrapperSelfRecognitionTest_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"

View file

@ -378,7 +378,7 @@ namespace test {
CHECK (!and_all (coll, [] (uint elm) { return 1 < elm; }));
CHECK ( has_any (coll, [] (uint elm) { return 0 < elm; }));
CHECK ( has_any (coll, [] (uint elm) { return elm >= NUM_ELMS; }));
CHECK ( has_any (coll, [] (uint elm) { return elm == NUM_ELMS; }));
CHECK (!has_any (coll, [] (uint elm) { return elm > NUM_ELMS; }));
}

View file

@ -79616,6 +79616,13 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1695335516996" ID="ID_1023062994" MODIFIED="1695335550147" TEXT="der normale Thread w&#xfc;rde sich dann stets am Ende selbst detachen"/>
<node CREATED="1695335555337" ID="ID_712387888" MODIFIED="1695335682482" TEXT="damit f&#xe4;llt ein Anla&#xdf; f&#xfc;r Fl&#xfc;chtigkeitsfehler weg"/>
<node CREATED="1695335705339" ID="ID_164690747" MODIFIED="1695335719461" TEXT="pa&#xdf;t damit zusammen, da&#xdf; wir auch einen try-catch-Block einbauen"/>
<node CREATED="1695392987999" ID="ID_109089425" MODIFIED="1695393002077" TEXT="wird (derzeit und absehbar) nur f&#xfc;r Tests verwendet">
<icon BUILTIN="info"/>
<node CREATED="1695393029161" ID="ID_1726147640" MODIFIED="1695393053763" TEXT="und daf&#xfc;r ist auch die Fehler-Weitergabe sinnvoll"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695393054345" ID="ID_582803458" MODIFIED="1695393067033" TEXT="also auch dieses Zusatz-Feature erhalten">
<icon BUILTIN="yes"/>
</node>
</node>
</node>
<node CREATED="1695313818614" ID="ID_470584516" MODIFIED="1695313837839" TEXT="Thread::syncPoint() / sync()">
<node CREATED="1695315415143" ID="ID_1102877346" MODIFIED="1695315425412" TEXT="bisherige Verwendungen">
@ -79641,8 +79648,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
sonst k&#246;nnte der Fall nicht zuverl&#228;ssig getestet werden, da&#223; der Test-Runner selber kurz nach dem Start stirbt
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1695315325887" ID="ID_625375733" MODIFIED="1695315328199" TEXT="SessionCommandFunction_test">
@ -79663,42 +79669,73 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="idea"/>
<node CREATED="1695334637793" ID="ID_681204225" MODIFIED="1695334721438" TEXT="spinning ohne yield w&#xe4;re gef&#xe4;hrlich im allgemeinen Fall">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
ein Spinlock sollte man nur verwenden, wenn man sicher sein kann, da&#223; man (a) fast nicht warten mu&#223; und (b) die CPU f&#252;r sich hat. Andernfalls kann das schlimmstenfalls zum Verhungern des ganzen Systems f&#252;hren...
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1695334757953" ID="ID_1716551234" MODIFIED="1695334788969">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Mutex + ConditionVar ist auch <i>gar nicht so schlecht</i>&#160;<font size="2">(aber noch schwergewichtiger)</font>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1695334869074" ID="ID_938671107" MODIFIED="1695335156463" TEXT="yield() gef&#xe4;llt mir &#x2014; weil es typischerweise genau um einen Thread-Schedule-delay geht">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Der Sprachstandard garantiert bereits, da&#223; der Thread startklar ist, wenn der std::thread-Konstruktor verlassen wird. Allerdings kann es immer passieren, da&#223; dummerweise der OS-Scheduler grade Anderes mit dieser CPU vor hat &#8212; und genau dann laufen wir in die Situation, vor der diese Sync-Barriere sch&#252;tzen soll; d.h. <i>sp&#228;testens nach einer Schedule-Runde</i>&#160;sollten alle Threads einmal vorbeigekommen sein.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<arrowlink COLOR="#6493ca" DESTINATION="ID_1673241488" ENDARROW="Default" ENDINCLINATION="210;16;" ID="Arrow_ID_2380452" STARTARROW="None" STARTINCLINATION="10;28;"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695391836532" ID="ID_373841600" MODIFIED="1695410342100" TEXT="lib::SyncBarrier implementieren">
<icon BUILTIN="flag-pink"/>
<node CREATED="1695391870343" ID="ID_500508570" MODIFIED="1695392284932" TEXT="bewu&#xdf;t nur zur Einmal-Verwendung">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Wiederverwendung nach automatischem <i>Reset nach Erf&#252;llung </i>ist in manchen Situationen verdammt t&#252;ckisch; ohnehin stellt ein Latch stets eine Deadlock-Gefahr dar.
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1695391893285" ID="ID_33727698" MODIFIED="1695392105718" TEXT="k&#xf6;nnte ein Vorgriff sein auf std::latch">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...darauf l&#228;uft es n&#228;mlich hinaus; wenn wir tats&#228;chlich auf C++20 sind, sollte die genaue API-Semantik und das Laufzeitverhalten bewertet werden, um zu entscheiden, ob beide L&#246;sungen &#228;quivalent sind...
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1695392297550" ID="ID_1673241488" MODIFIED="1695392410883" TEXT="Entscheidung: verwende yield-wait">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Letztlich eine Entscheidung nach Bauchgef&#252;hl und ohne konkreten Anhaltspunkt; die Entscheidung kann erst &#252;berpr&#252;ft werden, wenn es belastbare Einsatz-Szenarien gibt
</p>
</body>
</html></richcontent>
<linktarget COLOR="#6493ca" DESTINATION="ID_1673241488" ENDARROW="Default" ENDINCLINATION="210;16;" ID="Arrow_ID_2380452" SOURCE="ID_938671107" STARTARROW="None" STARTINCLINATION="10;28;"/>
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695392426479" ID="ID_281891239" MODIFIED="1695394808033" TEXT="Test per Load-Test">
<arrowlink COLOR="#2b3fa9" DESTINATION="ID_1220273122" ENDARROW="Default" ENDINCLINATION="169;-7;" ID="Arrow_ID_1739726561" STARTARROW="None" STARTINCLINATION="15;204;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
@ -79715,16 +79752,13 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-pink"/>
<node CREATED="1695339498617" ID="ID_1329617125" MODIFIED="1695339543619" TEXT="umgezogene Tests gebrochen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
solange sie noch auf den alten Code gehen, der den Threadpool voraussetzt (welcher aber f&#252;r Library-Tests nicht zugelinkt wird)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1695339558729" ID="ID_1181374984" MODIFIED="1695339576171" TEXT="SyncLocking_test"/>
<node CREATED="1695339549851" ID="ID_1030547189" MODIFIED="1695339553543" TEXT="SyncClasslock_test"/>
<node CREATED="1695339568255" ID="ID_288867443" MODIFIED="1695339574891" TEXT="ThreadWrapper_test"/>
@ -79748,16 +79782,13 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="yes"/>
<node CREATED="1695336524821" ID="ID_625640883" MODIFIED="1695336587478" TEXT="Begr&#xfc;ndung: es ist kein &#xbb;Vault-Feature&#xab; mehr">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Denn wir betreiben hier nun keine System-Einrichtungen mehr, keinen Resource-Collector und keinen Threadpool. Es handelt sich lediglich um Adapter &#252;ber der Std-Lib
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1695336588389" ID="ID_1245830716" MODIFIED="1695336597664" TEXT="au&#xdf;erdem erleichtert das den Schwenk"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695340272418" ID="ID_1240703198" MODIFIED="1695340308005" TEXT="Code aus namespace vault zur&#xfc;ckbauen">
@ -79771,6 +79802,57 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394188133" ID="ID_1229328590" MODIFIED="1695394203632" TEXT="Umbau">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394205547" ID="ID_1719816317" MODIFIED="1695394259544" TEXT="Tests umstellen und modernisieren">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394270093" ID="ID_585437655" MODIFIED="1695394271819" TEXT="ThreadWrapper_test">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695394288052" ID="ID_1407826381" MODIFIED="1695395666211" TEXT="Kritik: der testet nicht punktgenau">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Dieser Test ist zwar <i>pfiffig</i>&#160;geschrieben, aber er testet eigentlich den Sync/Monitor, nicht den Thread-Wrapper. In der aktuellen Form ist er sogar nahe an der Thema-Verfehlung, weil &#252;berhaupt nicht getestet wird,&#160; <i>da&#223; mehrere Threads gestartet wurden und gelaufen sind</i>
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#fedec1" DESTINATION="ID_1532807851" ENDARROW="Default" ENDINCLINATION="416;-16;" ID="Arrow_ID_1646166026" STARTARROW="None" STARTINCLINATION="479;36;"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394418004" ID="ID_1846217863" MODIFIED="1695394504468" TEXT="testen: ein Aufruf pro Thread hat stattgefunden">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394519627" ID="ID_776125132" MODIFIED="1695394528873" TEXT="testen: individuelle Argument&#xfc;bergabe">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394665822" ID="ID_881973960" MODIFIED="1695394668399" TEXT="ThreadWrapperJoin_test">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1695394703974" ID="ID_1918931115" MODIFIED="1695394714328" TEXT="erscheint bereits ad&#xe4;quat">
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695394715296" ID="ID_1135505355" MODIFIED="1695394724096" TEXT="pr&#xfc;fen: ggfs Fehlercode durchgeben?">
<icon BUILTIN="help"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394744028" ID="ID_1755648327" MODIFIED="1695394746744" TEXT="ThreadWrapperSelfRecognitionTest_test">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695394753483" ID="ID_1338410455" MODIFIED="1695410354252" TEXT="SyncBarrier_test">
<icon BUILTIN="flag-pink"/>
<node CREATED="1695394763010" ID="ID_1220273122" MODIFIED="1695394801458" TEXT="neuer Test f&#xfc;r neue (interims-) Implementierung">
<linktarget COLOR="#2b3fa9" DESTINATION="ID_1220273122" ENDARROW="Default" ENDINCLINATION="169;-7;" ID="Arrow_ID_1739726561" SOURCE="ID_281891239" STARTARROW="None" STARTINCLINATION="15;204;"/>
<icon BUILTIN="info"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695394861413" ID="ID_1334580079" MODIFIED="1695394870676" TEXT="ggfs auch gleich Performance-Test vorbereiten">
<icon BUILTIN="help"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394237210" ID="ID_11553358" MODIFIED="1695394251486" TEXT="Applikation umstellen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1693789156806" ID="ID_128127845" MODIFIED="1693789195596" TEXT="Monitor erhalten und umschreiben">
@ -79786,6 +79868,41 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1695337787989" ID="ID_154693007" MODIFIED="1695337808761" TEXT="ich errinnere mich, das nebenbei mit eingebaut zu haben..."/>
<node CREATED="1695337809498" ID="ID_789251448" MODIFIED="1695337825212" TEXT="inzwischen gibt es da diverse bessere M&#xf6;glichkeiten (mit Futures)"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395131329" ID="ID_1209350338" MODIFIED="1695395161594" TEXT="Umstellen der bestehenden Wrapper auf C++17">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395162340" ID="ID_188901559" MODIFIED="1695395168717" TEXT="Pr&#xfc;fen auf m&#xf6;gliche Vereinfachungen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395186529" ID="ID_110802115" MODIFIED="1695395202288" TEXT="Tests &#xfc;berpr&#xfc;fen und modernisieren">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395205001" ID="ID_881035953" MODIFIED="1695395208312" TEXT="SyncLocking_test">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695395218517" ID="ID_1241448952" MODIFIED="1695395351970" TEXT="Kritik: ist zwar fordernd, aber nicht klar">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Der Test setzt das Locking zwar erheblich unter Druck, aber er ist so komplex, da&#223; man sich einarbeiten mu&#223;, um ihn &#252;berhaupt zu verstehen
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395397765" ID="ID_1532807851" MODIFIED="1695395652825" TEXT="Idee: stattdessen den Checker aus ThreadWrapper_test verwenden">
<linktarget COLOR="#fedec1" DESTINATION="ID_1532807851" ENDARROW="Default" ENDINCLINATION="416;-16;" ID="Arrow_ID_1646166026" SOURCE="ID_1407826381" STARTARROW="None" STARTINCLINATION="479;36;"/>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395436073" ID="ID_735920726" MODIFIED="1695395466899" TEXT="w&#xfc;nschenswert: mit / ohne - Test">
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395469746" ID="ID_1750044155" MODIFIED="1695395500761" TEXT="den &#xbb;ohne&#xab;-Test so auslegen, da&#xdf; er (fast) immer scheitert">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695395590676" ID="ID_1887210552" MODIFIED="1695395616570" TEXT="Wirksamkeit ist direkt im Test dokumentiert">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>