diff --git a/src/lib/thread.cpp b/src/lib/thread.cpp index 8c18db741..8d1a1bf9c 100644 --- a/src/lib/thread.cpp +++ b/src/lib/thread.cpp @@ -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()); } diff --git a/src/lib/thread.hpp b/src/lib/thread.hpp index 12a62b6e5..eb3560f10 100644 --- a/src/lib/thread.hpp +++ b/src/lib/thread.hpp @@ -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)...); - 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(); } }; diff --git a/tests/core/application/subsystem-runner-test.cpp b/tests/core/application/subsystem-runner-test.cpp index 7c0315997..ac88a2e41 100644 --- a/tests/core/application/subsystem-runner-test.cpp +++ b/tests/core/application/subsystem-runner-test.cpp @@ -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 +#include +#include +#include -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_{}; 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 } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 2eeb6b5fe..a78cfcc72 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79295,7 +79295,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79531,7 +79531,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79956,6 +79956,54 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ Hier zeigt sich ein Widerspruch in den Konzepten selber an +

+
    +
  • + ein »launch-only«-Thread sollte sich eigentlich komplett abkoppeln +
  • +
  • + aber andererseits soll das Thead-Objekt auch den zugehörigen State kapseln, muß also irgendwo existieren +
  • +
+ +
+
+ + + + + + + + + +

+ dieses habe ich als RAII-Objekt angelegt, und der zugehörige Unique-Ptr markiert gleichzeitig den Lifecycle-State; das hat zur Konsequenz, daß der Session-Thread am Ende selber sein eigenes Objekt zerstören muß +

+ +
+ + +
+ + + + + + + + + @@ -80829,7 +80877,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + @@ -80846,8 +80896,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -80885,6 +80936,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+ @@ -92333,8 +92385,8 @@ class Something - +