Workforce: invoke a exit hook prior to worker termination

...essential for clean-up work, especially to drop
claimed resources reliably, even in case of error.
This commit is contained in:
Fischlurch 2023-09-09 02:31:16 +02:00
parent dd62240900
commit 9ccdfa24f7
3 changed files with 65 additions and 29 deletions

View file

@ -61,15 +61,15 @@ namespace gear {
// using std::forward;
using std::atomic;
using util::unConst;
using std::chrono_literals::operator ""ms; /////////////WIP
namespace work {
using std::chrono::milliseconds;
using std::chrono_literals::operator ""ms;
using SIG_WorkFun = activity::Proc(void);
using SIG_WorkFun = activity::Proc(void); ///< config should define callable to perform work
using SIG_FinalHook = void(bool); ///< config should define callable invoked at exit (argument: is error)
struct Config
{
@ -80,6 +80,7 @@ namespace gear {
const size_t DISMISS_CYCLES = 100;
};
/** Individual worker thread: repeatedly pulls the `doWork` functor */
template<class CONF>
class Runner
: CONF
@ -99,24 +100,34 @@ namespace gear {
void
pullWork()
{
ASSERT_VALID_SIGNATURE (decltype(CONF::doWork), SIG_WorkFun);
try {
while (true)
{
activity::Proc res = CONF::doWork();
if (emergency.load (std::memory_order_relaxed))
break;
if (res == activity::WAIT)
res = idleWait();
else
idleCycles = 0;
if (res != activity::PASS)
break;
}
ASSERT_VALID_SIGNATURE (decltype(CONF::doWork), SIG_WorkFun);
ASSERT_VALID_SIGNATURE (decltype(CONF::finalHook), SIG_FinalHook);
bool regularExit{false};
try /* ================ pull work ===================== */
{
while (true)
{
activity::Proc res = CONF::doWork();
if (emergency.load (std::memory_order_relaxed))
break;
if (res == activity::WAIT)
res = idleWait();
else
idleCycles = 0;
if (res != activity::PASS)
break;
}
regularExit = true;
}
ERROR_LOG_AND_IGNORE (threadpool, "defunct worker thread")
////////////////////////////////////////////////////////////////////////////OOO very important to have a reliable exit-hook here!!!
try /* ================ thread-exit hook ============== */
{
CONF::finalHook (not regularExit);
}
ERROR_LOG_AND_IGNORE (threadpool, "failure in thread-exit hook")
thread::detach();
}
@ -199,10 +210,8 @@ namespace gear {
{
for (auto& w : workers_)
w.emergency.store(true, std::memory_order_relaxed);
using namespace std::chrono_literals; ///////////////////////7///WIP
do
std::this_thread::sleep_for(10ms);
while (0 < size());
while (0 < size())
std::this_thread::sleep_for(setup_.IDLE_WAIT);
}
size_t

View file

@ -57,6 +57,7 @@ namespace test {
namespace {
using WorkFun = std::function<work::SIG_WorkFun>;
using FinalFun = std::function<work::SIG_FinalHook>;
template<class FUN>
auto
@ -66,6 +67,7 @@ namespace test {
: work::Config
{
WorkFun doWork;
FinalFun finalHook = [](bool){ /*NOP*/ };
milliseconds IDLE_WAIT = work::Config::IDLE_WAIT;
size_t DISMISS_CYCLES = work::Config::DISMISS_CYCLES;
@ -74,6 +76,13 @@ namespace test {
: doWork{std::forward<FUN> (workFun)}
{ }
Setup&&
withFinalHook (FinalFun finalFun)
{
finalHook = move (finalFun);
return move(*this);
}
Setup&&
withSleepPeriod (std::chrono::milliseconds millis)
{
@ -246,12 +255,27 @@ namespace test {
/** @test TODO
* @todo WIP 9/23 define implement
/** @test verify invocation of a thread-termination callback
*/
void
verify_finalHook()
{
atomic<uint> check{0};
atomic<activity::Proc> control{activity::PASS};
WorkForce wof{setup([&]{ return activity::Proc(control); })
.withFinalHook([&](bool){ ++check; })};
CHECK (0 == check);
wof.activate();
sleep_for(10ms);
CHECK (wof.size() == work::Config::COMPUTATION_CAPACITY);
CHECK (0 == check);
control = activity::HALT;
sleep_for(10ms);
CHECK (0 == wof.size());
CHECK (check == work::Config::COMPUTATION_CAPACITY);
}

View file

@ -79925,12 +79925,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node COLOR="#338800" CREATED="1694016479206" ID="ID_365196266" MODIFIED="1694212927107" TEXT="Worker-Threads rufen diesen Work-Funktor immerfort auf">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694016497043" ID="ID_1506723557" MODIFIED="1694016784845" TEXT="R&#xfc;ckgabewert des Work-Funktors steuert das Worker-Thread-Verhalten">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1694016497043" ID="ID_1506723557" MODIFIED="1694218954889" TEXT="R&#xfc;ckgabewert des Work-Funktors steuert das Worker-Thread-Verhalten">
<icon BUILTIN="button_ok"/>
<node CREATED="1694016513937" ID="ID_246253866" MODIFIED="1694016826450" TEXT="PASS &#x27f9; gleich erneut weiter"/>
<node CREATED="1694016533934" ID="ID_597986426" MODIFIED="1694016555143" TEXT="WAIT &#x27f9; Idle-Sequenz"/>
<node CREATED="1694016562284" ID="ID_1026875594" MODIFIED="1694016570999" TEXT="HALT &#x27f9; Thread terminieren"/>
</node>
<node COLOR="#338800" CREATED="1694218976808" ID="ID_1525105143" MODIFIED="1694218991055" TEXT="Worker-Threads rufen zuverl&#xe4;ssig einen exit-Hook auf">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1694016646687" ID="ID_784932678" MODIFIED="1694212938476" TEXT="Speicher f&#xfc;r Worker-Threads w&#xe4;hrend deren Lebensdauer halten">
<icon BUILTIN="button_ok"/>
</node>
@ -80052,8 +80055,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1694212971758" ID="ID_321316770" MODIFIED="1694212980437" TEXT="brauche Konfigurierbarkeit f&#xfc;r Test"/>
<node CREATED="1694212996114" ID="ID_279149518" MODIFIED="1694213012580" TEXT="...denn im Realbetrieb wird man l&#xe4;nger warten"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098852413" ID="ID_1245257433" MODIFIED="1694099559953" TEXT="verify_finalHook">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1694098852413" ID="ID_1245257433" MODIFIED="1694218949291" TEXT="verify_finalHook">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098904110" ID="ID_1191232452" MODIFIED="1694099559952" TEXT="verify_detectError">
<icon BUILTIN="flag-yellow"/>