From 1d625a01e0041b6c8a3b4dac2d4db4e7bf9b64a9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 29 Sep 2023 21:38:22 +0200 Subject: [PATCH] Library: complete and modernise ThreadWrapperJoin_test Since the decision was taken to retain support for this special feature, and even extend it to allow passing values, the additional functionality should be documented in the test. Doing so also highlighted subtle problems with argument binding. --- src/lib/thread.hpp | 4 +- tests/library/thread-wrapper-join-test.cpp | 103 +++++++++++++-------- tests/library/thread-wrapper-test.cpp | 2 +- wiki/thinkPad.ichthyo.mm | 31 +++++-- 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/lib/thread.hpp b/src/lib/thread.hpp index bda68fe5b..9c7ef8f9c 100644 --- a/src/lib/thread.hpp +++ b/src/lib/thread.hpp @@ -127,7 +127,7 @@ namespace lib { namespace thread {// Thread-wrapper base implementation... - using lib::meta::typeStr; + using lib::meta::typeSymbol; using std::forward; using std::decay_t; using std::invoke_result_t; @@ -313,7 +313,7 @@ namespace lib { template ThreadLifecycle (RES (SUB::*memFun) (ARGS...), ARGS ...args) - : ThreadLifecycle{util::joinDash (typeStr(), args...) + : ThreadLifecycle{util::joinDash (typeSymbol(), args...) ,std::move (memFun) ,static_cast (this) ,forward (args)... } diff --git a/tests/library/thread-wrapper-join-test.cpp b/tests/library/thread-wrapper-join-test.cpp index 1ba9acd68..015510fc8 100644 --- a/tests/library/thread-wrapper-join-test.cpp +++ b/tests/library/thread-wrapper-join-test.cpp @@ -27,35 +27,37 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" - #include "lib/thread.hpp" #include "lib/error.hpp" -#include +#include -using std::bind; using test::Test; - +using std::this_thread::sleep_for; +using namespace std::chrono_literals; namespace lib { namespace test { - using lumiera::error::LUMIERA_ERROR_LOGIC; + using error::LUMIERA_ERROR_LOGIC; namespace { - const int DESTRUCTION_CODE = 23; + const int DESTRUCTION_CODE = -23; - LUMIERA_ERROR_DEFINE(SPECIAL, "grandiose exception"); + LUMIERA_ERROR_DEFINE(SPECIAL, "007 exception"); + + #define Type(_EXPR_) lib::test::showType() } /***********************************************************************//** - * @test use the Lumiera Vault to create some new threads, additionally - * synchronising with these child threads and waiting for termination. - * - * @see vault::Thread - * @see threads.h + * @test verify the special flavour of the thread-wrapper, allowing to block + * waiting on thread end and then pass result or error state. + * @see thread.hpp + * @see Result_test + * @see ThreadWrapper_test + * @see SyncLocking_test */ class ThreadWrapperJoin_test : public Test { @@ -63,52 +65,79 @@ namespace test { virtual void run (Arg) { - simpleUse (); - wrongUse (); + simpleUse(); + returnValue(); + detectFailure(); + joinOnceOnly(); } - volatile int aValue_; ///< state to be modified by the other thread - void + simpleUse() + { + ThreadJoinable theThread{"test join-1" + ,[]{ sleep_for (10ms); }}; + CHECK (theThread); + theThread.join(); // blocks until thread-function has terminated + CHECK (not theThread); + } + + + + int theAction (int secretValue) ///< to be run in a new thread... { - usleep (100000); // pause 100ms prior to modifying + sleep_for (100ms); // pause 100ms prior to modifying if (DESTRUCTION_CODE == secretValue) - LUMIERA_ERROR_SET(test, SPECIAL, 0); + throw error::External{"special agent detected" + , LUMIERA_ERROR_SPECIAL}; else - aValue_ = secretValue+42; - - + return secretValue+42; } void - simpleUse () + returnValue() { - aValue_=0; - int mySecret = (rand() % 1000) - 500; + int mySecret = rand() % 1000; - ThreadJoinable newThread("test Thread joining-1" - , bind (&ThreadWrapperJoin_test::theAction, this, mySecret) - ); - newThread.join(); // blocks until theAction() is done - - CHECK (aValue_ == mySecret+42); + ThreadJoinable theThread{"test join-2" + ,&ThreadWrapperJoin_test::theAction + , this, mySecret}; + + // Note: join() passes the result value captured in the thread + CHECK (mySecret+42 == theThread.join()); } void - wrongUse () + detectFailure() { - ThreadJoinable newThread("test Thread joining-2" - , bind (&ThreadWrapperJoin_test::theAction, this, 1234) - ); - newThread.join(); // blocks until theAction() is done + ThreadJoinable theThread{"test join-3" + ,&ThreadWrapperJoin_test::theAction + , this, DESTRUCTION_CODE}; + + // join() actually returns a proxy... + auto res = theThread.join(); + CHECK (Type(res) == "Result"_expect); - VERIFY_ERROR(LOGIC, newThread.join() ); - VERIFY_ERROR(LOGIC, newThread.join() ); + // can detect that the thread was aborted with an exception + CHECK (not res.isValid()); + VERIFY_ERROR(SPECIAL, res.maybeThrow() ); + VERIFY_ERROR(SPECIAL, (int)res ); + } + + + void + joinOnceOnly () + { + ThreadJoinable theThread{"joining-4" + ,[]{ sleep_for (10ms); }}; + theThread.join(); + + VERIFY_ERROR(LOGIC, theThread.join() ); + VERIFY_ERROR(LOGIC, theThread.join() ); } }; diff --git a/tests/library/thread-wrapper-test.cpp b/tests/library/thread-wrapper-test.cpp index 38ded9666..8eb8088ae 100644 --- a/tests/library/thread-wrapper-test.cpp +++ b/tests/library/thread-wrapper-test.cpp @@ -124,7 +124,7 @@ namespace test{ { uint x = rand() % 1000; globalSum += (i + x); - threads.emplace (&TestThread::doIt, uint{i}, uint{x}); + threads.emplace (&TestThread::doIt, i, x); } // Note: bind to member function, copying arguments while (explore(threads).has_any()) diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 69d813c69..5538263d1 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79026,9 +79026,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + @@ -79263,8 +79266,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -79949,8 +79952,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -80017,16 +80020,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - + + - - + + + + + + + + +