Library/Application: switch SubsystemRunner_test

This commit is contained in:
Fischlurch 2023-10-03 20:38:16 +02:00
parent d879ae7fbd
commit 6cd16a61a6
4 changed files with 141 additions and 76 deletions

View file

@ -67,17 +67,17 @@ namespace thread{
void
ThreadWrapper::markThreadStart()
ThreadWrapper::markThreadStart (string id)
{
TRACE (thread, "%s", lifecycleMsg ("start...", threadID_).c_str());
TRACE (thread, "%s", lifecycleMsg ("start...", id).c_str());
setThreadName();
}
void
ThreadWrapper::markThreadEnd()
ThreadWrapper::markThreadEnd(string id)
{
TRACE (thread, "%s", lifecycleMsg ("finished.", threadID_).c_str());
TRACE (thread, "%s", lifecycleMsg ("finished.", id).c_str());
}

View file

@ -163,8 +163,8 @@ namespace lib {
/** detect if the currently executing code runs within this thread */
bool invokedWithinThread() const;
void markThreadStart();
void markThreadEnd ();
void markThreadStart(string);
void markThreadEnd (string);
void setThreadName ();
void waitGracePeriod() noexcept;
};
@ -268,9 +268,10 @@ namespace lib {
void
invokeThreadFunction (ARGS&& ...args)
{
Policy::markThreadStart();
string id{Policy::threadID_}; // local copy
Policy::markThreadStart(id);
Policy::perform_thread_function (forward<ARGS> (args)...);
Policy::markThreadEnd();
Policy::markThreadEnd(id);
Policy::handle_end_of_thread();
}
@ -347,7 +348,10 @@ namespace lib {
using ThreadLifecycle::ThreadLifecycle;
/** allow to detach explicitly — independent from thread-function's state
* @warning ensure that thread function only uses storage within its own scope
* @warning this function is borderline dangerous; it might be acceptable
* in a situation where the thread totally manages itself and the
* thread object is maintained in a unique_ptr. You must ensure that
* the thread function only uses storage within its own scope.
*/
void detach() { ThreadLifecycle::handle_end_of_thread(); }
};

View file

@ -21,7 +21,9 @@
* *****************************************************/
/** @file subsystem-runner-test.cpp
** unit test \ref SubsystemRunner_test
** The \ref SubsystemRunner_test performs various scenarios
** regarding start, stop and failure of _Subsystems._ Its primary
** purpose is to cover the \ref SubsystemRunner.
*/
@ -32,22 +34,28 @@
#include "common/option.hpp"
#include "lib/symbol.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
#include "lib/sync-barrier.hpp"
#include "lib/query-util.hpp"
#include "lib/format-cout.hpp"
#include "lib/error.hpp"
#include "lib/util.hpp"
#include "lib/sync.hpp"
#include <functional>
#include <memory>
#include <atomic>
#include <chrono>
using std::bind;
using util::isnil;
using util::cStr;
using test::Test;
using lib::Literal;
using lib::query::extractID;
using vault::Thread;
using lib::Thread;
using std::unique_ptr;
using std::atomic_bool;
using std::this_thread::sleep_for;
using std::chrono::milliseconds;
namespace lumiera {
@ -95,45 +103,48 @@ namespace test {
class MockSys
: public lumiera::Subsys
{
Literal id_;
const string id_;
const string spec_;
volatile bool isUp_;
volatile bool didRun_;
volatile bool started_;
volatile bool termRequest_;
int running_duration_;
atomic_bool isUp_{false};
atomic_bool didRun_{false};
atomic_bool started_{false};
atomic_bool termRequest_{false};
int running_duration_{0};
lib::SyncBarrier barrier_{};
unique_ptr<Thread> thread_{};
bool
shouldStart (lumiera::Option&) override
{
string startSpec (extractID ("start",spec_));
return "true" ==startSpec
|| "fail" ==startSpec
|| "throw"==startSpec;
or "fail" ==startSpec
or "throw"==startSpec;
}
bool
start (lumiera::Option&, Subsys::SigTerm termination) override
{
CHECK (!(isUp_|started_|didRun_), "attempt to start %s twice!", cStr(*this));
CHECK (not (isUp_ or started_ or didRun_), "attempt to start %s twice!", cStr(*this));
string startSpec (extractID ("start",spec_));
CHECK (!isnil (startSpec));
CHECK (not isnil (startSpec));
if ("true"==startSpec) //----simulate successful subsystem start
{
CHECK (!started_);
CHECK (not started_);
Thread (id_, bind (&MockSys::run, this, termination))
.sync(); // run-status handshake
// start »Subsystem operation« in a dedicated thread....
thread_.reset (new Thread{id_, &MockSys::run, this, termination});
barrier_.sync(); //---run-status handshake
CHECK (started_);
}
else
if ("fail"==startSpec) //----not starting, incorrectly reporting success
if ("fail"==startSpec) //---not starting, incorrectly reporting success
return true;
else
if ("throw"==startSpec) //---starting flounders
@ -171,13 +182,15 @@ namespace test {
run (Subsys::SigTerm termination)
{
string runSpec (extractID ("run",spec_));
CHECK (!isnil (runSpec));
CHECK (not isnil (runSpec));
// run-status handshake
started_ = true;
isUp_ = ("true"==runSpec || "throw"==runSpec);
didRun_ = ("false"!=runSpec); // includes "fail" and "throw"
lumiera_thread_sync ();
// coordinate startup with controlling thread
barrier_.sync();
if (isUp_) //-------------actually enter running state for some time
{
@ -186,9 +199,9 @@ namespace test {
INFO (test, "thread %s now running....", cStr(*this));
while (!shouldTerminate())
while (not shouldTerminate())
{
usleep (1000*TICK_DURATION_ms);
sleep_for (milliseconds{TICK_DURATION_ms});
running_duration_ -= TICK_DURATION_ms;
}
@ -221,16 +234,11 @@ namespace test {
public:
MockSys(Literal id, Literal spec)
: id_(id),
spec_(spec),
isUp_(false),
didRun_(false),
started_(false),
termRequest_(false),
running_duration_(0)
: id_(id)
, spec_(spec)
{ }
~MockSys() { }
~MockSys() { }
operator string () const { return "MockSys(\""+id_+"\")"; }
@ -284,14 +292,14 @@ namespace test {
MockSys unit ("one", "start(true), run(true).");
SubsystemRunner runner(dummyOpt);
CHECK (!unit.isRunning());
CHECK (!unit.didRun());
CHECK (not unit.isRunning());
CHECK (not unit.didRun());
runner.maybeRun (unit);
bool emergency = runner.wait();
CHECK (!emergency);
CHECK (!unit.isRunning());
CHECK (not emergency);
CHECK (not unit.isRunning());
CHECK (unit.didRun());
}
@ -317,22 +325,23 @@ namespace test {
SubsystemRunner runner(dummyOpt);
runner.maybeRun (unit1); // this one doesn't start at all, which isn't considered an error
CHECK (not unit1.didRun());
VERIFY_ERROR (TEST, runner.maybeRun (unit2) );
VERIFY_ERROR (LOGIC, runner.maybeRun (unit3) ); // incorrect behaviour trapped
VERIFY_ERROR (LOGIC, runner.maybeRun (unit4) ); // detected that the subsystem didn't come up
usleep (DELAY_FOR_FLOUNDERING_THRAD_ms * 1000); // preempt to allow unit4 to go away
sleep_for (milliseconds{DELAY_FOR_FLOUNDERING_THRAD_ms}); // preempt to allow unit4 to go away
runner.wait();
CHECK (!unit1.isRunning());
CHECK (!unit2.isRunning());
CHECK (!unit3.isRunning());
CHECK (!unit4.isRunning());
CHECK (!unit1.didRun());
CHECK (!unit2.didRun());
CHECK (!unit3.didRun());
CHECK ( unit4.didRun()); // ...but it failed immediately
CHECK (not unit1.isRunning());
CHECK (not unit2.isRunning());
CHECK (not unit3.isRunning());
CHECK (not unit4.isRunning());
CHECK (not unit1.didRun());
CHECK (not unit2.didRun());
CHECK (not unit3.didRun());
CHECK (unit4.didRun()); // ...but it failed immediately
}
@ -347,8 +356,8 @@ namespace test {
runner.maybeRun (unit);
bool emergency = runner.wait();
CHECK (emergency); // emergency state got propagated
CHECK (!unit.isRunning());
CHECK (emergency == true); // emergency state was propagated
CHECK (not unit.isRunning());
CHECK (unit.didRun());
}
@ -376,11 +385,11 @@ namespace test {
bool emergency = runner.wait();
CHECK (!emergency);
CHECK (!unit1.isRunning());
CHECK (!unit2.isRunning());
CHECK (!unit3.isRunning());
CHECK (!unit4.isRunning());
CHECK (not emergency);
CHECK (not unit1.isRunning());
CHECK (not unit2.isRunning());
CHECK (not unit3.isRunning());
CHECK (not unit4.isRunning());
CHECK (unit1.didRun());
CHECK (unit2.didRun());
CHECK (unit3.didRun());
@ -404,21 +413,21 @@ namespace test {
SubsystemRunner runner(dummyOpt);
VERIFY_ERROR (STATE, runner.maybeRun (unit4) ); // failure to bring up prerequisites is detected
CHECK ( unit1.isRunning());
CHECK ( unit2.isRunning());
CHECK (!unit3.isRunning());
CHECK ( unit1.isRunning());
CHECK ( unit2.isRunning());
CHECK (not unit3.isRunning());
// shutdown has been triggered for unit4, but may require some time
bool emergency = runner.wait();
CHECK (!emergency); // no problems with the subsystems actually running...
CHECK (!unit1.isRunning());
CHECK (!unit2.isRunning());
CHECK (!unit3.isRunning());
CHECK (!unit4.isRunning());
CHECK ( unit1.didRun());
CHECK ( unit2.didRun());
CHECK (!unit3.didRun());
CHECK (not emergency); // no problems with the subsystems actually running...
CHECK (not unit1.isRunning());
CHECK (not unit2.isRunning());
CHECK (not unit3.isRunning());
CHECK (not unit4.isRunning());
CHECK ( unit1.didRun());
CHECK ( unit2.didRun());
CHECK (not unit3.didRun());
// can't say for sure if unit4 actually did run
}
};

View file

@ -79295,7 +79295,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1695394188133" ID="ID_1229328590" MODIFIED="1696015383325" TEXT="Umbau">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1695597011139" FOLDED="true" ID="ID_125138411" MODIFIED="1695859127749" TEXT="Kopie des bestehenden Thread-Wrappers umschreiben">
<node COLOR="#338800" CREATED="1695597011139" FOLDED="true" ID="ID_125138411" MODIFIED="1696357528355" TEXT="Kopie des bestehenden Thread-Wrappers umschreiben">
<linktarget COLOR="#6ebe5a" DESTINATION="ID_125138411" ENDARROW="Default" ENDINCLINATION="43;-18;" ID="Arrow_ID_195305087" SOURCE="ID_215388471" STARTARROW="None" STARTINCLINATION="-113;6;"/>
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1695597146529" ID="ID_938038908" MODIFIED="1695948178166" TEXT="std::thread liefert fast alle Funktionalit&#xe4;t">
@ -79531,7 +79531,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1695597337839" FOLDED="true" ID="ID_1776858144" MODIFIED="1695859098234" TEXT="detach() / join-Differenzierung">
<node COLOR="#338800" CREATED="1695597337839" FOLDED="true" ID="ID_1776858144" MODIFIED="1696357531093" TEXT="detach() / join-Differenzierung">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1695647408544" ID="ID_805151848" MODIFIED="1695651308867" TEXT="isValid() bzw. bool-Check">
<icon BUILTIN="button_ok"/>
@ -79956,6 +79956,54 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696357544203" FOLDED="true" ID="ID_1208501979" MODIFIED="1696358077856" TEXT="detach() API?">
<icon BUILTIN="clanbomber"/>
<node CREATED="1696357565724" ID="ID_332128317" MODIFIED="1696358075753" TEXT="ziemlich gef&#xe4;hrlich &#x2014; eigentlich sollte es das nicht geben">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Hier zeigt sich ein Widerspruch in den Konzepten selber an
</p>
<ul>
<li>
ein &#187;launch-only&#171;-Thread sollte sich eigentlich komplett abkoppeln
</li>
<li>
aber andererseits soll das Thead-Objekt auch den zugeh&#246;rigen State kapseln, mu&#223; also irgendwo existieren
</li>
</ul>
</body>
</html></richcontent>
</node>
<node CREATED="1696357587969" ID="ID_857278701" MODIFIED="1696357619392" TEXT="&#xf6;ffnet eine &#xbb;Hintert&#xfc;r&#xab; : mann kann detac() aufrufen und dann das Thread-Objekt l&#xf6;schen">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1696357622045" ID="ID_1238132894" MODIFIED="1696357864016" TEXT="genau das brauche ich f&#xfc;r das bestehende Design vom Session-Thread (DispatcherLoop)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
dieses habe ich als RAII-Objekt angelegt, und der zugeh&#246;rige Unique-Ptr markiert gleichzeitig den Lifecycle-State; das hat zur Konsequenz, da&#223; der Session-Thread am Ende <i>selber sein eigenes Objekt zerst&#246;ren mu&#223;</i>
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#8c91b2" DESTINATION="ID_876148747" ENDARROW="Default" ENDINCLINATION="521;-2055;" ID="Arrow_ID_638014906" STARTARROW="None" STARTINCLINATION="295;9;"/>
<icon BUILTIN="smiley-oh"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#190f69" CREATED="1696357865684" FOLDED="true" ID="ID_1810679254" MODIFIED="1696357973110" TEXT="Aua: die threadID_ wird f&#xfc;r das Shutdown-Log gebraucht">
<icon BUILTIN="stop-sign"/>
<node CREATED="1696357887177" ID="ID_1835825008" MODIFIED="1696357893004" TEXT="also daf&#xfc;r eine lokale Kopie"/>
<node CREATED="1696357893592" ID="ID_1813261494" MODIFIED="1696357915937" TEXT="die threadImpl_ ist eigentlich auch dangling">
<icon BUILTIN="smily_bad"/>
</node>
<node CREATED="1696357936249" ID="ID_854400443" MODIFIED="1696357959467" TEXT="aber sie wird zuletzt vom Thread-Objekt gebraucht"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1695597475293" FOLDED="true" ID="ID_1100470933" MODIFIED="1695859224044" TEXT="Aufruf der Thread-Funktion">
<icon BUILTIN="button_ok"/>
@ -80829,7 +80877,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1696029465122" ID="ID_1515483274" MODIFIED="1696029516437" TEXT="subsystem-runner-test.cpp (/zLumi/tests/core/application)"/>
<node COLOR="#338800" CREATED="1696029465122" ID="ID_1515483274" MODIFIED="1696357491529" TEXT="SubsystemRunner_test">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1696029465124" ID="ID_1556068052" MODIFIED="1696029514685" TEXT="typed-counter-test.cpp (/zLumi/tests/basics)"/>
<node CREATED="1696029465120" ID="ID_1705540772" MODIFIED="1696029510893" TEXT="call-queue-test.cpp (/zLumi/tests/basics)"/>
<node CREATED="1696029465120" ID="ID_1782746519" MODIFIED="1696029465120" TEXT="bus-term-test.cpp (/zLumi/tests/stage)"/>
@ -80846,8 +80896,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node CREATED="1696029465121" ID="ID_786350998" MODIFIED="1696029583638" TEXT="gtk-lumiera.cpp"/>
<node CREATED="1696029465122" ID="ID_871343805" MODIFIED="1696029593893" TEXT="output-director.cpp"/>
<node CREATED="1696029465122" ID="ID_556208204" MODIFIED="1696171137865" TEXT="steam-dispatcher.cpp">
<node COLOR="#338800" CREATED="1696029465122" FOLDED="true" ID="ID_556208204" MODIFIED="1696358985114" TEXT="steam-dispatcher.cpp">
<linktarget COLOR="#71be6f" DESTINATION="ID_556208204" ENDARROW="Default" ENDINCLINATION="1038;-401;" ID="Arrow_ID_1257115855" SOURCE="ID_1718267866" STARTARROW="None" STARTINCLINATION="602;40;"/>
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1696039703074" ID="ID_796238065" MODIFIED="1696171045349" TEXT="Refactoring wie &#xfc;blich">
<icon BUILTIN="button_ok"/>
<node CREATED="1696039723008" ID="ID_932291373" MODIFIED="1696039728651" TEXT="Thread wird ein Member-Feld"/>
@ -80885,6 +80936,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</p>
</body>
</html></richcontent>
<linktarget COLOR="#8c91b2" DESTINATION="ID_876148747" ENDARROW="Default" ENDINCLINATION="521;-2055;" ID="Arrow_ID_638014906" SOURCE="ID_1238132894" STARTARROW="None" STARTINCLINATION="295;9;"/>
<icon BUILTIN="help"/>
<node CREATED="1696172561670" ID="ID_620791350" MODIFIED="1696172587782" TEXT="running state &#x2259; DispatcherLoop-Objekt existiert"/>
<node COLOR="#435e98" CREATED="1696172623600" ID="ID_1471623002" MODIFIED="1696175317007" TEXT="damit wollte ich eine sichere Kopplung herstellen">
@ -92333,8 +92385,8 @@ class Something
<node CREATED="1694795411496" ID="ID_560488135" MODIFIED="1694795415392" TEXT="Expanding / Collapsing"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696109921291" ID="ID_853481552" LINK="https://issues.lumiera.org/ticket/1341" MODIFIED="1696110190077" TEXT="#110 und #1341 Clarify Error handling scheme">
<linktarget COLOR="#fdcfce" DESTINATION="ID_853481552" ENDARROW="Default" ENDINCLINATION="-959;56;" ID="Arrow_ID_275745687" SOURCE="ID_1712639748" STARTARROW="None" STARTINCLINATION="-1085;66;"/>
<linktarget COLOR="#fdcfce" DESTINATION="ID_853481552" ENDARROW="Default" ENDINCLINATION="-959;56;" ID="Arrow_ID_1749710199" SOURCE="ID_1320347721" STARTARROW="None" STARTINCLINATION="-1130;86;"/>
<linktarget COLOR="#fdcfce" DESTINATION="ID_853481552" ENDARROW="Default" ENDINCLINATION="-959;56;" ID="Arrow_ID_275745687" SOURCE="ID_1712639748" STARTARROW="None" STARTINCLINATION="-1085;66;"/>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694440562933" ID="ID_1529688085" LINK="https://issues.lumiera.org/ticket/1338" MODIFIED="1694795034628" TEXT="#1338 Non-standard Play-processing">