diff --git a/src/vault/gear/work-force.hpp b/src/vault/gear/work-force.hpp index 5d43129ae..526179dbb 100644 --- a/src/vault/gear/work-force.hpp +++ b/src/vault/gear/work-force.hpp @@ -24,8 +24,8 @@ /** @file work-force.hpp ** A pool of workers for multithreaded rendering. ** - ** @see ////TODO_test usage example - ** @see scheduler.cpp implementation + ** @see work-force-test.cpp + ** @see scheduler-commutator.hpp usage as part of the scheduler ** ** @todo WIP-WIP-WIP 6/2023 »Playback Vertical Slice« ** @@ -71,6 +71,15 @@ namespace gear { 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) + /** + * Base for configuration of the worker pool. + * In real usage, a subclass of #Config is used, + * which additionally defines the two required functors + * - `doWork` : perform a piece of work and return control code + * - `finalHook` : callback invoked at work thread termination + * Obviously these two functors are defined in a way to call into + * the actual implementation of work management (i.e. the Scheduler). + */ struct Config { static const size_t COMPUTATION_CAPACITY; @@ -80,6 +89,7 @@ namespace gear { const size_t DISMISS_CYCLES = 100; }; + /** Individual worker thread: repeatedly pulls the `doWork` functor */ template class Runner @@ -148,11 +158,15 @@ namespace gear { }//(End)namespace work - /** + + + /*************************************//** * Pool of worker threads for rendering. - * - * @see SomeSystem - * @see NA_test + * @note the \tparam CONF configuration/policy base must define + * - `doWork` - the _work-functor_ (with #SIG_WorkFun) + * - `finalHook` - called at thread exit + * @see WorkForce_test + * @see SchedulerCommutator */ template class WorkForce @@ -165,7 +179,6 @@ namespace gear { public: - WorkForce (CONF config) : setup_{move (config)} , workers_{} @@ -220,8 +233,6 @@ namespace gear { unConst(workers_).remove_if([](auto& w){ return not w.joinable(); }); return workers_.size(); } - - private: }; diff --git a/tests/vault/gear/work-force-test.cpp b/tests/vault/gear/work-force-test.cpp index 8be546297..3471287bf 100644 --- a/tests/vault/gear/work-force-test.cpp +++ b/tests/vault/gear/work-force-test.cpp @@ -216,7 +216,7 @@ namespace test { .withSleepPeriod (10ms)}; wof.incScale(); - sleep_for(10us); + sleep_for(50us); CHECK (1 == check); @@ -241,11 +241,11 @@ namespace test { .dismissAfter(5)}; wof.incScale(); - sleep_for(20us); + sleep_for(50us); CHECK (1 == check); - sleep_for(10ms); + sleep_for(12ms); CHECK (2 == check); // after one wait cycle, one further invocation sleep_for(100ms); @@ -286,6 +286,34 @@ namespace test { void verify_detectError() { + atomic check{0}; + atomic errors{0}; + WorkForce wof{setup ([&]{ + if (++check == 555) + throw error::State("evil"); + return activity::PASS; + }) + .withFinalHook([&](bool isFailure) + { + if (isFailure) + ++errors; + })}; + + CHECK (0 == check); + CHECK (0 == errors); + + wof.incScale(); + wof.incScale(); + wof.incScale(); + + sleep_for(10us); + CHECK (3 == wof.size()); + CHECK (0 < check); + CHECK (0 == errors); + + sleep_for(200ms); // wait for the programmed disaster + CHECK (2 == wof.size()); + CHECK (1 == errors); } @@ -354,22 +382,51 @@ namespace test { - /** @test TODO - * @todo WIP 9/23 ⟶ define ⟶ implement + /** @test verify that the WorkForce dtor waits for all active threads to disappear + * - use a work-functor which keeps all workers blocked + * - start the WorkForce within a separate thread + * - in this separate thread, cause the WorkForce destructor to be called + * - in the outer (controlling thread) release the work-functor blocking + * - at this point, all workers return, detect shutdown and terminate */ void verify_dtor_blocks() { - } - - - - /** @test TODO - * @todo WIP 9/23 ⟶ define ⟶ implement - */ - void - walkingDeadline() - { + atomic trapped{true}; + auto blockingWork = [&]{ + while (trapped) + /* spin */; + return activity::PASS; + }; + + atomic pool_scaled_up{false}; + atomic shutdown_done{false}; + + std::thread operate{[&]{ + {// nested scope... + WorkForce wof{setup (blockingWork)}; + + wof.activate(); + sleep_for(10ms); + CHECK (wof.size() == work::Config::COMPUTATION_CAPACITY); + pool_scaled_up = true; + } // WorkForce goes out of scope => dtor called + + // when reaching this point, dtor has terminated + shutdown_done = true; + operate.detach(); + }}; + + CHECK (operate.joinable()); // operate-thread is in running state + sleep_for(100ms); + + CHECK (pool_scaled_up); + CHECK (not shutdown_done); // all workers are trapped in the work-functor + // thus the destructor can't dismantle the pool + trapped = false; + sleep_for(20ms); + CHECK (shutdown_done); + CHECK (not operate.joinable()); // operate-thread has detached and terminated } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index e30b6b82f..56e217462 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79637,7 +79637,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -79655,8 +79656,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + @@ -79743,8 +79746,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -79829,7 +79832,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79864,8 +79867,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -79950,7 +79953,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -80039,8 +80042,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -80058,8 +80061,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -80081,8 +80084,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + + +