From 78bdd671fa264e3a61dfb82fb8fe9852e26c1bb4 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 23 Oct 2026 13:08:10 +0100 Subject: [PATCH] Work in progress test_intrusive_timer_one_shot not working yet --- include/etl/intrusive_timer.h | 52 +++++---- include/etl/timer_node.h | 25 ++-- test/UnitTest++/Checks.h | 12 +- test/test_intrusive_timer.cpp | 213 +++++++++++++++++++++++++++------- test/test_timer_lists.cpp | 100 ++++++++-------- 5 files changed, 274 insertions(+), 128 deletions(-) diff --git a/include/etl/intrusive_timer.h b/include/etl/intrusive_timer.h index d36bae8f..77a563a8 100644 --- a/include/etl/intrusive_timer.h +++ b/include/etl/intrusive_timer.h @@ -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; diff --git a/include/etl/timer_node.h b/include/etl/timer_node.h index 67bf3b7e..994fb2ee 100644 --- a/include/etl/timer_node.h +++ b/include/etl/timer_node.h @@ -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 - 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 - 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_) { diff --git a/test/UnitTest++/Checks.h b/test/UnitTest++/Checks.h index 0d13c4d9..9c64fa79 100644 --- a/test/UnitTest++/Checks.h +++ b/test/UnitTest++/Checks.h @@ -16,9 +16,9 @@ #include "Config.h" #include "TestResults.h" #include "MemoryOutStream.h" -#include +#include -namespace UnitTest +namespace UnitTest { template typename std::enable_if::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::type>(expected) & ~(typename std::make_unsigned::type(0))) << " but was 0x" << std::setw(2 * sizeof(Actual)) << (static_cast::type>(actual) & ~(typename std::make_unsigned::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::type(0))); results.OnTestFailure(details, stream.GetText()); diff --git a/test/test_intrusive_timer.cpp b/test/test_intrusive_timer.cpp index e15d93c3..b393160a 100644 --- a/test/test_intrusive_timer.cpp +++ b/test/test_intrusive_timer.cpp @@ -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; + using CallbackTimer = etl::timer_callback_node; + + //************************************************************************* + 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 + { + public: + + MessageProcessor() + : etl::message_router() + , 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; + + 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 compare1 = {37}; - // std::vector compare2 = {23}; - // std::vector 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(); + 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 compare1 = {37}; + // std::vector compare2 = {23}; + // std::vector 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) diff --git a/test/test_timer_lists.cpp b/test/test_timer_lists.cpp index ca0d19f1..f513102c 100644 --- a/test/test_timer_lists.cpp +++ b/test/test_timer_lists.cpp @@ -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();