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

// 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();
}