to cover the identified use-cases a wide variety of functors must be accepted and adapted appropriately. A special twist arises from the fact that the complete thread-wrapper component stack works without RTTI; a derived class can not access the thread-wrapper internals while the policy component to handle those hooks can not directly downcast to some derived user provided class. But obviously at usage site it can be expected to access both realms from such a callback. The solution is to detect the argument type of the given functor and to build a two step path for a safe static cast.
156 lines
4.7 KiB
C++
156 lines
4.7 KiB
C++
/*
|
|
ThreadWrapperLifecycle(Test) - verify lifecycle aspects of the thread wrapper
|
|
|
|
Copyright (C) Lumiera.org
|
|
2023, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
* *****************************************************/
|
|
|
|
/** @file thread-wrapper-lifecycle-test.cpp
|
|
** unit test \ref ThreadWrapperLifecycle_test
|
|
*/
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
|
#include "lib/thread.hpp"
|
|
#include "lib/iter-explorer.hpp"
|
|
#include "lib/scoped-collection.hpp"
|
|
#include "lib/test/microbenchmark.hpp"
|
|
#include "lib/test/diagnostic-output.hpp"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
|
|
using test::Test;
|
|
using lib::explore;
|
|
using std::atomic_uint;
|
|
using std::this_thread::yield;
|
|
using std::this_thread::sleep_for;
|
|
using namespace std::chrono_literals;
|
|
using std::chrono::system_clock;
|
|
|
|
|
|
namespace lib {
|
|
namespace test{
|
|
|
|
namespace { // test parameters
|
|
|
|
const uint NUM_THREADS = 200;
|
|
const uint REPETITIONS = 10;
|
|
|
|
using CLOCK_SCALE = std::micro; // Results are in µ-sec
|
|
}
|
|
|
|
|
|
/*******************************************************************//**
|
|
* @test verify lifecycle behaviour of threads managed by thread-wrapper.
|
|
* @see thread.hpp
|
|
* @see ThreadWrapperBackground_test
|
|
* @see ThreadWrapperJoin_test
|
|
*/
|
|
class ThreadWrapperLifecycle_test : public Test
|
|
{
|
|
|
|
virtual void
|
|
run (Arg)
|
|
{
|
|
defaultWrapperLifecycle();
|
|
verifyThreadLifecycleHooks();
|
|
demonstrateExplicitThreadLifecycle();
|
|
}
|
|
|
|
|
|
/** @test demonstrate terms of lifecycle for the default case */
|
|
void
|
|
defaultWrapperLifecycle()
|
|
{
|
|
using Dur = std::chrono::duration<double, CLOCK_SCALE>;
|
|
using Point = system_clock::time_point;
|
|
Point threadStart;
|
|
Point afterCtor;
|
|
Thread thread("lifecycle", [&]{
|
|
threadStart = system_clock::now();
|
|
});
|
|
afterCtor = system_clock::now();
|
|
while (thread) yield();
|
|
|
|
double offset = Dur{threadStart - afterCtor}.count();
|
|
SHOW_EXPR(offset)
|
|
CHECK (offset > 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* @test verify a special setup to start a thread explicitly and to track
|
|
* the thread's lifecycle state.
|
|
*/
|
|
void
|
|
verifyThreadLifecycleHooks()
|
|
{
|
|
atomic_uint stage{0};
|
|
ThreadHookable thread{ThreadHookable::Launch([]{ sleep_for (5ms); })
|
|
.threadID("hooked thread")
|
|
.atStart([&]{ stage = 1; })
|
|
.atExit ([&]{ stage = 2; })};
|
|
|
|
CHECK (thread);
|
|
CHECK (0 == stage);
|
|
|
|
sleep_for (1ms);
|
|
CHECK (thread);
|
|
CHECK (1 == stage);
|
|
|
|
while (thread) yield();
|
|
CHECK (not thread);
|
|
CHECK (2 == stage);
|
|
}
|
|
|
|
|
|
/**
|
|
* @test verify a special setup to start a thread explicitly and to track
|
|
* the thread's lifecycle state.
|
|
*/
|
|
void
|
|
demonstrateExplicitThreadLifecycle()
|
|
{
|
|
UNIMPLEMENTED ("demonstrate state change");
|
|
struct TestThread
|
|
: Thread
|
|
{
|
|
using Thread::Thread;
|
|
|
|
uint local{0};
|
|
|
|
void
|
|
doIt (uint a, uint b) ///< the actual operation running in a separate thread
|
|
{
|
|
uint sum = a + b;
|
|
// sleep_for (microseconds{sum}); // Note: explicit random delay before local store
|
|
local = sum;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
LAUNCHER (ThreadWrapperLifecycle_test, "function common");
|
|
|
|
|
|
|
|
}} // namespace lib::test
|