Workforce: configuration and initialisation of workers

- use a template parameter to allow for hook into local facilities (Scheduler)
- pass config initialisation down through constructors
This commit is contained in:
Fischlurch 2023-09-07 17:15:25 +02:00
parent cf7c2d1327
commit b8e52d008c
4 changed files with 258 additions and 48 deletions

View file

@ -58,7 +58,9 @@ namespace gear {
// NA::~NA() { }
const size_t WorkForce::FULL_SIZE = util::max (std::thread::hardware_concurrency(), MINIMAL_CONCURRENCY);
/** default value for full computing capacity is to use all (virtual) cores */
const size_t work::Config::COMPUTATION_CAPACITY = util::max (std::thread::hardware_concurrency()
, MINIMAL_CONCURRENCY);

View file

@ -38,12 +38,12 @@
#include "vault/common.hpp"
#include "vault/gear/activity.hpp"
#include "lib/meta/function.hpp"
#include "lib/nocopy.hpp"
//#include "lib/symbol.hpp"
#include "lib/util.hpp"
//#include <string>
#include <functional>
#include <utility>
#include <vector>
#include <thread>
@ -56,30 +56,37 @@ namespace gear {
// using util::isnil;
// using std::string;
using std::move;
// using std::forward;
using std::atomic;
namespace work {
using SIG_WorkFun = activity::Proc(void);
struct Config
{
static const size_t COMPUTATION_CAPACITY;
const size_t EXPECTED_MAX_POOL = 1.5*COMPUTATION_CAPACITY;
};
template<class CONF>
class Runner
: std::thread
, CONF
: CONF
, public std::thread
{
public:
Runner()
: thread{}
Runner (CONF config)
: CONF{move (config)}
, thread{[this]{ pullWork(); }}
{ }
private:
void
pullWork()
{
ASSERT_VALID_SIGNATURE (decltype(CONF::doWork), SIG_WorkFun);
try {
while (true)
{
@ -103,7 +110,7 @@ namespace gear {
return activity::PASS;
}
};
}
}//(End)namespace work
/**
@ -112,32 +119,29 @@ namespace gear {
* @see SomeSystem
* @see NA_test
*/
template<class CONF>
class WorkForce
: util::NonCopyable
{
using WorkFun = std::function<activity::Proc(void)>;
using Pool = std::vector<std::thread>;
using Pool = std::vector<work::Runner<CONF>>;
WorkFun workFun_;
CONF setup_;
Pool workers_;
atomic<bool> halt_{false};
public:
static const size_t FULL_SIZE;
explicit
WorkForce (WorkFun&& fun)
: workFun_{move (fun)}
WorkForce (CONF config)
: setup_{move (config)}
, workers_{}
{
workers_.reserve (1.5*FULL_SIZE);
workers_.reserve (setup_.EXPECTED_MAX_POOL);
}
~WorkForce()
{
try {
deactivate();
awaitShutdown();
}
ERROR_LOG_AND_IGNORE (threadpool, "defunct worker thread")
}
@ -146,16 +150,16 @@ namespace gear {
void
activate (double degree =1.0)
{
halt_ = false;
size_t scale = util::max (size_t(degree*FULL_SIZE), 1u);
size_t scale{setup_.COMPUTATION_CAPACITY};
scale *= degree;
scale = util::max (scale, 1u);
for (uint i = workers_.size(); i < scale; ++i)
workers_.emplace_back ([this]{ pullWork(); });
workers_.emplace_back (setup_);
}
void
deactivate()
awaitShutdown()
{
halt_ = true;
for (auto& w : workers_)
if (w.joinable())
w.join();
@ -163,21 +167,6 @@ namespace gear {
}
private:
void
pullWork()
{
try {
while (true)
{
activity::Proc res = workFun_();
if (halt_ or res != activity::PASS)
break;
}
}
ERROR_LOG_AND_IGNORE (threadpool, "defunct worker thread")
}
};

View file

@ -33,6 +33,7 @@
//#include <utility>
//#include <chrono>
#include <functional>
#include <thread>
using test::Test;
@ -50,6 +51,9 @@ namespace test {
// using lib::time::Offset;
// using lib::time::Time;
namespace {
using WorkFun = std::function<work::SIG_WorkFun>;
}
@ -64,9 +68,18 @@ namespace test {
virtual void
run (Arg)
{
simpleUsage();
walkingDeadline();
setupLalup();
simpleUsage();
verify_pullWork();
verify_workerHalt();
verify_workerSleep();
verify_workerDemote();
verify_finalHook();
verify_detectError();
verify_defaultPool();
verify_scalePool();
verify_countActive();
verify_dtor_blocks();
}
@ -76,8 +89,15 @@ namespace test {
simpleUsage()
{
atomic<uint> check{0};
struct Setup
: work::Config
{
WorkFun doWork;
}
setup;
setup.doWork = [&]{ ++check; return activity::PASS; };
WorkForce wof{[&]{ ++check; return activity::PASS; }};
WorkForce wof{setup};
CHECK (0 == check);
@ -90,18 +110,110 @@ namespace test {
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
walkingDeadline()
verify_pullWork()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
setupLalup()
verify_workerHalt()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_workerSleep()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_workerDemote()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_finalHook()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_detectError()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_defaultPool()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_scalePool()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_countActive()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
verify_dtor_blocks()
{
}
/** @test TODO
* @todo WIP 9/23 define implement
*/
void
walkingDeadline()
{
}
};

View file

@ -79627,7 +79627,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1693952215256" ID="ID_532083956" MODIFIED="1693952226539" TEXT="Implementierung">
<icon BUILTIN="pencil"/>
<node CREATED="1693952247831" ID="ID_1732680738" MODIFIED="1693952374018" TEXT="Work-Function m&#xf6;glichst inline aber statisch konfigurierbar">
<node COLOR="#435e98" CREATED="1693952247831" ID="ID_1732680738" LINK="#ID_391262141" MODIFIED="1694096132122" TEXT="Work-Function m&#xf6;glichst inline aber statisch konfigurierbar">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -79637,7 +79637,24 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
</node>
<node CREATED="1693952387244" ID="ID_713372762" MODIFIED="1693952394788" TEXT="Worker-Lebenszyklus"/>
<node CREATED="1693952387244" ID="ID_713372762" MODIFIED="1693952394788" TEXT="Worker-Lebenszyklus">
<node CREATED="1694096144054" ID="ID_1871831859" MODIFIED="1694096180493" TEXT="Skalieren wird durch ein Bedarfs-Signal ausgel&#xf6;st"/>
<node CREATED="1694096181545" ID="ID_1507356674" MODIFIED="1694096206961" TEXT="wird beim Hochskalieren (schrittweise) erzeugt"/>
<node CREATED="1694096216352" ID="ID_1944988231" MODIFIED="1694096230206" TEXT="bekommt alle weitere Steuerung vom Work-Funktor mitgeteilt"/>
<node CREATED="1694096242144" ID="ID_1798309477" MODIFIED="1694096261145" TEXT="durchl&#xe4;uft bei Inaktivit&#xe4;t einen konfigurierten Schlaf-Zyklus"/>
<node CREATED="1694096262158" ID="ID_1697788425" MODIFIED="1694096536053" TEXT="beendet sich selbt bei l&#xe4;ngerandauernder Inaktivit&#xe4;t"/>
<node CREATED="1694096475945" ID="ID_787243685" MODIFIED="1694096502953" TEXT="ruft vor Ende zuverl&#xe4;ssig eine hook-Funktion auf"/>
<node CREATED="1694096411074" ID="ID_1767238" MODIFIED="1694096454253">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
klinkt sich vor Beenden aus: <font face="Monospaced" color="#6505c4">thread::detach()</font>
</p>
</body>
</html></richcontent>
</node>
</node>
<node CREATED="1693952433774" ID="ID_1630947367" MODIFIED="1693952436665" TEXT="Steuerung">
<node CREATED="1693952446548" ID="ID_311286578" MODIFIED="1693953466417" TEXT="m&#xf6;glichst alle Steuerung durch die Worker selbst ausf&#xfc;hren">
<richcontent TYPE="NOTE"><html>
@ -79751,8 +79768,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
...es w&#228;re denkbar, da&#223; es sich nicht um einen KILL-Switch handelt, sondern um einen Heartbeat, der z.B. aus den tick()-Aufrufen erneuert werden mu&#223;, so da&#223; im Fall einer Verklemmung sich der Scheduler selbst terminiert
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1693960760375" ID="ID_1365275183" MODIFIED="1693960911841" TEXT="der Zugriff darauf bleibt ein Problem">
<richcontent TYPE="NOTE"><html>
@ -79809,6 +79825,59 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694016464368" ID="ID_1059038394" MODIFIED="1694016784845" TEXT="statisch konfigurierbarer Work-Funktor">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#5b280f" CREATED="1694095251355" ID="ID_612425707" MODIFIED="1694095281878" TEXT="geht nicht ohne Laufzeit-Initialisierung">
<icon BUILTIN="stop-sign"/>
<node CREATED="1694095283186" ID="ID_1780985861" MODIFIED="1694095313010" TEXT="C++ ist in dieser Hinsicht sehr konsequent"/>
<node CREATED="1694095347089" ID="ID_1729488090" MODIFIED="1694097381098" TEXT="auch &#x3bb;-captures k&#xf6;nnen niemals in einen statischen Kontext eindringen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
lambda in local class 'vault::gear::test::WorkForce_test::simpleUsage()::Setup' cannot capture variables from the enclosing context
</p>
</body>
</html></richcontent>
</node>
<node COLOR="#5b280f" CREATED="1694095394803" ID="ID_1858576296" MODIFIED="1694095527155" TEXT="das einzige &#x201e;Schlupfloch&#x201c; w&#xe4;re eine statische globale Variable">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...das k&#246;nnte zwar ein statischer Service sein, der dann sogar im Lebenszyklus der Applikation irgendwo konfiguriert wird
</p>
</body>
</html></richcontent>
<icon BUILTIN="button_cancel"/>
</node>
<node CREATED="1694095484695" ID="ID_554742625" MODIFIED="1694095522293" TEXT="tats&#xe4;chlich k&#xf6;nnen wir aber problemlos vom Scheduler-Setup eine Initialisierung durchreichen">
<icon BUILTIN="yes"/>
</node>
</node>
<node COLOR="#435e98" CREATED="1694095531834" ID="ID_391262141" MODIFIED="1694096132120" TEXT="also: ein Config-Objekt (Template-Param)">
<node CREATED="1694095549375" ID="ID_1866942283" MODIFIED="1694095558928" TEXT="bietet damit einen frei w&#xe4;hlbaren statischen Teil"/>
<node CREATED="1694095559445" ID="ID_1320200932" MODIFIED="1694095566536" TEXT="wird aber auch als Init-Parameter durchgegeben"/>
<node CREATED="1694095567147" ID="ID_1657187178" MODIFIED="1694095579833">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
und in jeden zu startenden Thread <b>kopiert</b>
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1694095580490" ID="ID_983915877" MODIFIED="1694095780984" TEXT="und das ist gut so">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
C++ zwingt uns dazu, explizit das zu tun was ohnehin getan werden mu&#223;; da jedoch der <i>Typ </i>der Config per Template-Parameter gew&#228;hlt wird, ist komplettes Inlining m&#246;glich; letztlich wird daher nur ein Pointer auf das Scheduler-Objekt in alle Threads kopiert &#8212; <i>exakt das</i>&#160; was wir brauchen
</p>
</body>
</html></richcontent>
<icon BUILTIN="ksmiletris"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694016479206" ID="ID_365196266" MODIFIED="1694016784845" TEXT="Worker-Threads rufen diesen Work-Funktor immerfort auf">
<icon BUILTIN="flag-yellow"/>
@ -79850,6 +79919,44 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098740892" ID="ID_1850596111" MODIFIED="1694099559955" TEXT="Worker-Verhalten">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098758090" ID="ID_1594595049" MODIFIED="1694099559952" TEXT="verify_pullWork: f&#xfc;hrt den Work-Funktor wiederholt aus">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098814770" ID="ID_495507864" MODIFIED="1694099559955" TEXT="verify_workerHalt: terminiert auf Anweisung">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098789389" ID="ID_1077314922" MODIFIED="1694099559954" TEXT="verify_workerSleep: schl&#xe4;ft auf Anweisung">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098829216" ID="ID_358493866" MODIFIED="1694099559953" TEXT="verify_workerDemote: terminiert nach l&#xe4;ngerem Schlaf">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098852413" ID="ID_1245257433" MODIFIED="1694099559953" TEXT="verify_finalHook">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098904110" ID="ID_1191232452" MODIFIED="1694099559952" TEXT="verify_detectError">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694098957487" ID="ID_143578534" MODIFIED="1694099563792" TEXT="Pool-Verhalten">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694099014497" ID="ID_1877110298" MODIFIED="1694099563791" TEXT="verify_default: default macht nichts">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694099055410" ID="ID_1489311533" MODIFIED="1694099565281" TEXT="verify_scaleUp">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1694099029004" ID="ID_910692339" MODIFIED="1694099041032" TEXT="skaliert auf genau N worker"/>
<node CREATED="1694099061344" ID="ID_758198253" MODIFIED="1694099067644" TEXT="skaliert um einen Worker hoch"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694099080950" ID="ID_542625093" MODIFIED="1694099566207" TEXT="verify_countActive: ermittelt Zahl aktiver Worker">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1694099160252" ID="ID_950338764" MODIFIED="1694099567838" TEXT="verify_dtor_blocks">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
</node>