You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
4.0 KiB
133 lines
4.0 KiB
// License: Apache 2.0. See LICENSE file in root directory.
|
|
// Copyright(c) 2021 Intel Corporation. All Rights Reserved.
|
|
|
|
#include <unit-tests/test.h>
|
|
#include <rsutils/time/timer.h>
|
|
#include <rsutils/concurrency/concurrency.h>
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <iostream>
|
|
|
|
using namespace rsutils::time;
|
|
|
|
// We use this function as a CPU stress test function
|
|
int fibo( int num )
|
|
{
|
|
if( num < 2 )
|
|
return 1;
|
|
return fibo( num - 1 ) + fibo( num - 2 );
|
|
}
|
|
|
|
TEST_CASE( "dispatcher main flow" )
|
|
{
|
|
dispatcher d(3);
|
|
std::atomic_bool run = { false };
|
|
auto func = [&](dispatcher::cancellable_timer c)
|
|
{
|
|
c.try_sleep(std::chrono::seconds(1));
|
|
run = true;
|
|
};
|
|
|
|
d.start();
|
|
REQUIRE(d.empty());
|
|
// We want to make sure that if we invoke some functions, the dispatcher is not empty.
|
|
// We add 2 functions that take some time so that even if the first one pop,
|
|
// the second will still be in the queue and it will not be empty
|
|
d.invoke(func);
|
|
d.invoke(func);
|
|
REQUIRE_FALSE(d.empty());
|
|
REQUIRE(d.flush());
|
|
REQUIRE(run);
|
|
d.stop();
|
|
}
|
|
|
|
TEST_CASE( "invoke and wait" )
|
|
{
|
|
dispatcher d(2);
|
|
|
|
std::atomic_bool run = { false };
|
|
auto func = [&](dispatcher::cancellable_timer c)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
run = true;
|
|
};
|
|
|
|
d.start();
|
|
stopwatch sw;
|
|
d.invoke_and_wait(func, []() {return false; }, true);
|
|
REQUIRE( sw.get_elapsed() > std::chrono::seconds( 3 ) ); // verify we get here only after the function call ended
|
|
d.stop();
|
|
}
|
|
|
|
TEST_CASE("verify stop() not consuming high CPU usage")
|
|
{
|
|
// using shared_ptr because no copy constructor is allowed for a dispatcher.
|
|
std::vector<std::shared_ptr<dispatcher>> dispatchers;
|
|
|
|
for (int i = 0 ; i < 32; ++i)
|
|
{
|
|
dispatchers.push_back(std::make_shared<dispatcher>(10));
|
|
}
|
|
|
|
for (auto &&dispatcher : dispatchers)
|
|
{
|
|
dispatcher->start();
|
|
}
|
|
|
|
for (auto&& dispatcher : dispatchers)
|
|
{
|
|
dispatcher->stop();
|
|
}
|
|
|
|
|
|
// Allow some time for all threads to do some work
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
|
|
stopwatch sw;
|
|
|
|
// Do some stress work
|
|
REQUIRE(fibo(40) == 165580141);
|
|
// Verify the stress test did not take too long.
|
|
// We had an issue that stop() call cause a high CPU usage and therefore other operations stall,
|
|
// This test took > 9 seconds on an 8 cores PC, after the fix it took ~1.5 sec on 1 core run (on release configuration).
|
|
// We allow 9 seconds to support debug configuration as well
|
|
REQUIRE( sw.get_elapsed() < std::chrono::seconds( 9 ) );
|
|
}
|
|
|
|
TEST_CASE("stop() notify flush to finish")
|
|
{
|
|
// On this test we check that if during a flush() another thread call stop(),
|
|
// than the flush CV will be triggered to exit and not wait a full timeout
|
|
dispatcher dispatcher( 10 );
|
|
dispatcher.start();
|
|
|
|
stopwatch sw;
|
|
std::atomic_bool dispatched_end_verifier{ false };
|
|
dispatcher.invoke( [&]( dispatcher::cancellable_timer c ) {
|
|
//std::cout << "Sleeping from inside invoke" << std::endl;
|
|
std::this_thread::sleep_for( std::chrono::seconds( 3 ) );
|
|
//std::cout << "Sleeping from inside invoke - Done" << std::endl;
|
|
dispatched_end_verifier = true;
|
|
} );
|
|
|
|
// Make sure the above invoke function is dispatched
|
|
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
|
|
|
std::thread stop_thread( [&]() {
|
|
// Make sure we postpone the stop to after the flush call
|
|
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
|
//std::cout << "Stopping dispatcher" << std::endl;
|
|
dispatcher.stop();
|
|
} );
|
|
|
|
auto timeout = std::chrono::seconds(5);
|
|
//std::cout << "Flushing dispatcher" << std::endl;
|
|
CHECK(!dispatcher.flush(timeout));
|
|
// We expect that flush will be triggered by stop only after the dispatched function end.
|
|
CHECK(dispatched_end_verifier);
|
|
//std::cout << "Flushing is done" << std::endl;
|
|
CHECK( sw.get_elapsed() < timeout );
|
|
stop_thread.join();
|
|
}
|