Work in progress

test_intrusive_timer_one_shot not working yet
This commit is contained in:
John Wellbelove 2026-10-23 13:08:10 +01:00
parent d04cc5c296
commit 78bdd671fa
5 changed files with 274 additions and 128 deletions

View File

@ -85,6 +85,7 @@ namespace etl
if (!timer_.is_registered(this))
{
timer_.set_id(get_next_id());
register_list.insert(timer_);
timer_.set_owner(this);
++timer_count;
@ -102,10 +103,12 @@ namespace etl
//*******************************************
bool unregister_timer(etl::timer_node& timer_) ETL_NOEXCEPT
{
lock_policy.lock();
bool result = false;
if (timer_.is_registered(this))
{
lock_policy.lock();
if (timer_.is_active())
{
active_list.remove(&timer_);
@ -113,16 +116,14 @@ namespace etl
register_list.remove(&timer_);
lock_policy.unlock();
--timer_count;
return true;
}
else
{
return false;
result = true;
}
lock_policy.unlock();
return result;
}
//*******************************************
@ -162,6 +163,8 @@ namespace etl
//*******************************************
bool tick(uint32_t count)
{
bool was_processed = false;
if (enabled)
{
if (!lock_policy.is_locked())
@ -202,35 +205,36 @@ namespace etl
// Subtract any remainder from the next due timeout.
active_list.first()->delta -= count;
}
lock_policy.unlock();
// Invoke all of the expired timers.
invoke_expired_timers();
}
return true;
}
lock_policy.unlock();
was_processed = true;
// Invoke all of the expired timers.
invoke_expired_timers();
}
return false;
return was_processed;
}
//*******************************************
/// Starts a timer.
//*******************************************
bool start(etl::timer_node* timer_, bool immediate_ = false) ETL_NOEXCEPT
bool start(etl::timer_node& timer_, bool immediate_ = false) ETL_NOEXCEPT
{
bool is_started = false;
lock_policy.lock();
etl::timer_node* timer = register_list.find(timer_);
if (timer != register_list.end())
if (timer_.is_registered(this))
{
active_list.insert(timer);
is_started = true;
if (!timer_.is_active())
{
active_list.insert(timer_);
is_started = true;
}
}
lock_policy.unlock();
@ -238,7 +242,7 @@ namespace etl
if (is_started && immediate_)
{
// Trigger the timer immediately.
timer->invoke(timer->get_config());
timer_.invoke(timer_.get_config());
}
return is_started;

View File

@ -41,6 +41,13 @@ SOFTWARE.
// Definitions common to intrusive timers.
//*****************************************************************************
#if defined(ETL_IN_UNIT_TEST)
namespace
{
class etl_unit_test_intrusive_timer;
}
#endif
namespace etl
{
namespace private_timer
@ -147,6 +154,10 @@ namespace etl
friend class intrusive_timer;
#if defined(ETL_IN_UNIT_TEST)
friend class etl_unit_test_intrusive_timer;
#endif
//*******************************************
/// Constructor.
//*******************************************
@ -326,16 +337,16 @@ namespace etl
};
//***************************************************************************
/// A timer node that invokes a callback delegate.
/// A timer node that invokes a callback delegate type.
//***************************************************************************
template <typename TCallback>
struct callback_timer_node : public timer_node
struct timer_callback_node : public timer_node
{
//*******************************************
/// Constructor.
//*******************************************
callback_timer_node(TCallback callback_) ETL_NOEXCEPT
: timer_node()
timer_callback_node(TCallback callback_, const etl::timer_config& config_) ETL_NOEXCEPT
: timer_node(config_)
, callback(callback_)
{
}
@ -355,13 +366,13 @@ namespace etl
/// A timer node that invokes a message callback.
//***************************************************************************
template <typename TMessage>
struct message_timer_node : public timer_node
struct timer_message_node : public timer_node
{
//*******************************************
/// Constructor.
//*******************************************
message_timer_node(etl::imessage_router& router_, TMessage message_) ETL_NOEXCEPT
: timer_node()
timer_message_node(const etl::timer_config& config_, etl::imessage_router& router_, TMessage message_) ETL_NOEXCEPT
: timer_node(config_)
, router(&router_)
, message(message_)
{

View File

@ -16,9 +16,9 @@
#include "Config.h"
#include "TestResults.h"
#include "MemoryOutStream.h"
#include<iomanip>
#include<iomanip>
namespace UnitTest
namespace UnitTest
{
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, std::string>::type
@ -178,7 +178,7 @@ namespace UnitTest
if (!(expected == actual))
{
UnitTest::MemoryOutStream stream;
stream << "Expected "
stream << "Expected "
<< DisplayValue(expected) << " but was " << DisplayValue(actual);
results.OnTestFailure(details, stream.GetText());
@ -205,7 +205,7 @@ namespace UnitTest
if (!(expected == actual))
{
UnitTest::MemoryOutStream stream;
stream << std::hex << std::uppercase << std::setfill('0')
stream << std::hex << std::uppercase << std::setfill('0')
<< "Expected 0x" << std::setw(2 * sizeof(Expected)) << (static_cast<typename std::make_unsigned<Expected>::type>(expected) & ~(typename std::make_unsigned<Expected>::type(0)))
<< " but was 0x" << std::setw(2 * sizeof(Actual)) << (static_cast<typename std::make_unsigned<Actual>::type>(actual) & ~(typename std::make_unsigned<Actual>::type(0)));
@ -219,7 +219,7 @@ namespace UnitTest
if (expected == actual)
{
UnitTest::MemoryOutStream stream;
stream << "Expected not equal, but both values are" << actual;
stream << "Expected not equal, but both values are " << actual;
results.OnTestFailure(details, stream.GetText());
}
@ -231,7 +231,7 @@ namespace UnitTest
if (expected == actual)
{
UnitTest::MemoryOutStream stream;
stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2 * sizeof(Actual))
stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2 * sizeof(Actual))
<< "Expected not equal, but both values are " << (actual & ~(typename std::make_unsigned<Actual>::type(0)));
results.OnTestFailure(details, stream.GetText());

View File

@ -81,6 +81,9 @@ namespace
TimerInsertRemoveTest timerInsertRemoveTest;
//***************************************************************************
/// A lock policy that counts the number of times it is locked and unlocked.
/// The lock count should always be 0 or 1.
//***************************************************************************
class LockPolicy : public etl::timer_lock_policy
{
@ -93,16 +96,26 @@ namespace
{
}
//*******************************************
~LockPolicy()
{
assert(count == 0);
}
//*******************************************
void lock() ETL_NOEXCEPT override
{
++count;
assert(count == 1);
}
//*******************************************
void unlock() ETL_NOEXCEPT override
{
--count;
assert(count == 0);
}
//*******************************************
@ -122,6 +135,94 @@ namespace
int count;
};
//*************************************************************************
using CallbackDelegate = etl::delegate<void(const etl::timer_config&)>;
using CallbackTimer = etl::timer_callback_node<CallbackDelegate>;
//*************************************************************************
bool global_callback_called = false;
const etl::timer_config* global_config;
static void global_callback(const etl::timer_config& config)
{
global_callback_called = true;
global_config = &config;
}
//*************************************************************************
class Object
{
public:
Object()
: member_callback_called(false)
, member_config(ETL_NULLPTR)
{
}
void member(const etl::timer_config& config)
{
member_callback_called = true;
member_config = &config;
}
bool member_callback_called = false;
const etl::timer_config* member_config;
};
static Object object;
//*************************************************************************
struct CallbackMessage : public etl::message<1>
{
CallbackMessage(int value_)
: value(value_)
, config(ETL_NULLPTR)
{
}
void set_timer_config(const etl::timer_config& config_)
{
config = &config_;
}
int value;
const etl::timer_config* config;
};
class MessageProcessor : public etl::message_router<MessageProcessor, CallbackMessage>
{
public:
MessageProcessor()
: etl::message_router<MessageProcessor, CallbackMessage>()
, message_callback_called(false)
, message_config(ETL_NULLPTR)
, value(0)
{
}
//*******************************************
void on_receive(const CallbackMessage& msg)
{
message_callback_called = true;
message_config = msg.config;
value = msg.value;
}
//*******************************************
void on_receive_unknown(const etl::imessage& msg) {}
bool message_callback_called;
const etl::timer_config* message_config;
int value;
};
using MessageTimer = etl::timer_message_node<CallbackMessage>;
MessageProcessor message_processor;
CallbackMessage callback_message(42);
SUITE(test_intrusive_timer)
{
//*************************************************************************
@ -219,49 +320,77 @@ namespace
CHECK_EQUAL(0U, lock_policy.get_count());
}
////*************************************************************************
// TEST(test_intrusive_timer_one_shot)
//{
// LockPolicy lock_policy;
//*************************************************************************
TEST(test_intrusive_timer_one_shot)
{
LockPolicy lock_policy;
// etl::intrusive_timer timer_controller(lock_policy);
//
// etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Single_Shot);
// etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Single_Shot);
// etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Single_Shot);
//
// object.tick_list.clear();
// free_tick_list1.clear();
// free_tick_list2.clear();
//
// timer_controller.start(id1);
// timer_controller.start(id3);
// timer_controller.start(id2);
//
// timer_controller.enable(true);
//
// ticks = 0;
//
// const uint32_t step = 1UL;
//
// while (ticks <= 100U)
// {
// ticks += step;
// timer_controller.tick(step);
// }
//
// std::vector<uint64_t> compare1 = {37};
// std::vector<uint64_t> compare2 = {23};
// std::vector<uint64_t> compare3 = {11};
//
// CHECK(object.tick_list.size() != 0);
// CHECK(free_tick_list1.size() != 0);
// CHECK(free_tick_list2.size() != 0);
//
// CHECK_ARRAY_EQUAL(compare1.data(), object.tick_list.data(), compare1.size());
// CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
// CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
//}
etl::intrusive_timer timer_controller(lock_policy);
etl::timer_config config1(100, etl::timer::mode::Single_Shot);
CallbackDelegate callback_delegate1 = CallbackDelegate::create(global_callback);
CallbackTimer callback_timer1(callback_delegate1, config1);
etl::timer_config config2(23, etl::timer::mode::Single_Shot);
CallbackDelegate callback_delegate2 = CallbackDelegate::create<Object, &Object::member, object>();
CallbackTimer callback_timer2(callback_delegate2, config2);
etl::timer_config config3(11, etl::timer::mode::Single_Shot);
MessageTimer callback_timer3(config3, message_processor, callback_message);
CHECK_TRUE(timer_controller.register_timer(callback_timer1));
CHECK_TRUE(timer_controller.register_timer(callback_timer2));
CHECK_TRUE(timer_controller.register_timer(callback_timer3));
// Get the modified id of the timers after registration.
etl::timer::id::type config1_id = callback_timer1.get_id();
etl::timer::id::type config2_id = callback_timer2.get_id();
etl::timer::id::type config3_id = callback_timer3.get_id();
timer_controller.start(callback_timer1);
timer_controller.start(callback_timer3);
timer_controller.start(callback_timer2);
timer_controller.enable(true);
ticks = 0;
const uint32_t step = 1UL;
while (ticks <= 100U)
{
ticks += step;
timer_controller.tick(step);
}
CHECK_TRUE(global_callback_called);
CHECK_EQUAL((int)config1_id, (int)global_config->get_id());
CHECK_EQUAL(config1.get_mode(), global_config->get_mode());
CHECK_EQUAL(config1.get_period(), global_config->get_period());
CHECK_TRUE(object.member_callback_called);
CHECK_EQUAL((int)config2_id, (int)object.member_config->get_id());
CHECK_EQUAL(config2.get_mode(), object.member_config->get_mode());
CHECK_EQUAL(config2.get_period(), object.member_config->get_period());
CHECK_TRUE(message_processor.message_callback_called);
CHECK_EQUAL((int)config3_id, (int)message_processor.message_config->get_id());
CHECK_EQUAL(config3.get_mode(), message_processor.message_config->get_mode());
CHECK_EQUAL(config3.get_period(), message_processor.message_config->get_period());
CHECK_EQUAL(callback_message.value, message_processor.value);
// std::vector<uint64_t> compare1 = {37};
// std::vector<uint64_t> compare2 = {23};
// std::vector<uint64_t> compare3 = {11};
// CHECK(object.tick_list.size() != 0);
// CHECK(free_tick_list1.size() != 0);
// CHECK(free_tick_list2.size() != 0);
// CHECK_ARRAY_EQUAL(compare1.data(), object.tick_list.data(), compare1.size());
// CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
// CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
}
// //*************************************************************************
// TEST(message_timer_one_shot_after_timeout)

View File

@ -33,6 +33,49 @@ SOFTWARE.
namespace
{
//*******************************************
class etl_unit_test_intrusive_timer
{
public:
//*************************************************************************
void register_timer(etl::private_timer::timer_register_list& register_list, etl::timer_node& timer_)
{
timer_.set_id(get_next_id(register_list));
register_list.insert(&timer_);
timer_.set_owner(this);
}
//*******************************************
etl::timer::id::type get_next_id(etl::private_timer::timer_register_list& register_list) ETL_NOEXCEPT
{
etl::timer::id::type next_id = 0; // IDs start at 0
etl::timer_node* timer = register_list.first();
while (timer != register_list.end())
{
if (timer->get_id() == next_id)
{
// Already in use, so increment next_id and continue checking
++next_id;
}
else if (timer->get_id() > next_id)
{
// Found a gap in the IDs, so return the next_id
return next_id;
}
timer = register_list.next(timer);
}
// We never found a gap, so return the next available ID
return next_id;
}
};
etl_unit_test_intrusive_timer test_intrusive_timer;
SUITE(test_timer_lists)
{
//*************************************************************************
@ -55,11 +98,14 @@ namespace
CHECK_TRUE(register_list.first() == register_list.end());
// Add the timers to the list.
register_list.insert(&timer1);
register_list.insert(&timer2);
register_list.insert(&timer3);
register_list.insert(&timer4);
register_list.insert(&timer5);
test_intrusive_timer.register_timer(register_list, timer1);
test_intrusive_timer.register_timer(register_list, timer2);
test_intrusive_timer.register_timer(register_list, timer3);
test_intrusive_timer.register_timer(register_list, timer4);
test_intrusive_timer.register_timer(register_list, timer5);
auto p1 = register_list.first();
auto p2 = &timer1;
CHECK_FALSE(register_list.empty());
CHECK_TRUE(register_list.first() == &timer1);
@ -216,50 +262,6 @@ namespace
CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_config().get_period() - timer2.get_config().get_period(), timer5.get_delta());
// Remove a timer that is not in the list.
active_list.remove(&timer6);
// Search the list to ensure the other timers are still present.
timer1_found = false;
timer2_found = false;
timer3_found = false;
timer4_found = false;
timer5_found = false;
timer = active_list.first();
while (timer != active_list.end())
{
if (timer == &timer1)
{
timer1_found = true;
}
else if (timer == &timer2)
{
timer2_found = true;
}
else if (timer == &timer3)
{
timer3_found = true;
}
else if (timer == &timer4)
{
timer4_found = true;
}
else if (timer == &timer5)
{
timer5_found = true;
}
timer = active_list.next(timer);
}
// Check the deltas are still correct.
CHECK_EQUAL(timer2.get_period(), timer2.get_delta());
CHECK_EQUAL(timer1.get_period() - timer3.get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_period() - timer1.get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_period() - timer2.get_period(), timer5.get_delta());
// Clear the list.
active_list.clear();