From 024b2dd2c59dceb9c7c00b2ac57461fa4f9faf02 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 21 Oct 2026 18:48:15 +0100 Subject: [PATCH] Work in progress --- include/etl/intrusive_callback_timer.h | 401 --------- include/etl/intrusive_timer.h | 452 +++++++++++ include/etl/private/timer_lists.h | 41 +- include/etl/timer_lists.h | 515 ------------ include/etl/timer_node.h | 109 ++- test/test_intrusive_timer.cpp | 1038 ++++++++++++++++++++++++ test/test_timer_lists.cpp | 68 +- test/test_timer_node.cpp | 7 - test/vs2022/etl.vcxproj | 2 + test/vs2022/etl.vcxproj.filters | 6 + 10 files changed, 1629 insertions(+), 1010 deletions(-) delete mode 100644 include/etl/intrusive_callback_timer.h create mode 100644 include/etl/intrusive_timer.h delete mode 100644 include/etl/timer_lists.h create mode 100644 test/test_intrusive_timer.cpp diff --git a/include/etl/intrusive_callback_timer.h b/include/etl/intrusive_callback_timer.h deleted file mode 100644 index f439cfcb..00000000 --- a/include/etl/intrusive_callback_timer.h +++ /dev/null @@ -1,401 +0,0 @@ -/****************************************************************************** -The MIT License(MIT) - -Embedded Template Library. -https://github.com/ETLCPP/etl -https://www.etlcpp.com - -Copyright(c) 2017 John Wellbelove - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files(the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions : - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -******************************************************************************/ - -#ifndef ETL_INTRUSIVE_TIMER_INCLUDED -#define ETL_INTRUSIVE_TIMER_INCLUDED - -#include "platform.h" -#include "algorithm.h" -#include "error_handler.h" -#include "nullptr.h" -#include "static_assert.h" -#include "timer_lists.h" -#include "timer_node.h" - -#include - -namespace etl -{ - //*************************************************************************** - /// Interface for timer lock policy - //*************************************************************************** - class timer_lock_policy - { - public: - - virtual void lock() = 0; - virtual void unlock() = 0; - virtual bool is_locked() = 0; - }; - - //*************************************************************************** - /// Interface for intrusive timer - //*************************************************************************** - class intrusive_timer - { - public: - - typedef etl::delegate timer_update_callback_type; - - //******************************************* - /// Constructor. - //******************************************* - explicit intrusive_timer(timer_lock_policy& lock_policy_) - : lock_policy(lock_policy_) - { - } - - //******************************************* - /// Register a timer. - //******************************************* - etl::timer::id::type register_timer(etl::timer_node& node) - { - return id; - } - - //******************************************* - /// Unregister a timer. - //******************************************* - bool unregister_timer(etl::timer_node& node) - { - bool result = false; - - if (id_ != etl::timer::id::No_Timer) - { - timer_data& timer = timer_array[id_]; - - if (timer.id != etl::timer::id::No_Timer) - { - if (timer.is_active()) - { - lock_policy.lock(); - active_list.remove(timer.id, false); - remove_callback.call_if(timer.id); - lock_policy.unlock(); - } - - // Reset in-place. - new (&timer) timer_data(); - --registered_timers; - - result = true; - } - } - - return result; - } - - //******************************************* - /// Enable/disable the timer. - //******************************************* - void enable(bool state_) - { - enabled = state_; - } - - //******************************************* - /// Get the enable/disable state. - //******************************************* - bool is_running() const - { - return enabled; - } - - //******************************************* - /// Clears the timer of data. - //******************************************* - void clear() - { - lock_policy.lock(); - active_list.clear(); - lock_policy.unlock(); - - for (int i = 0; i < MAX_TIMERS; ++i) - { - ::new (&timer_array[i]) timer_data(); - } - - registered_timers = 0; - } - - //******************************************* - // Called by the timer service to indicate the - // amount of time that has elapsed since the last successful call to 'tick'. - // Returns true if the tick was processed, - // false if not. - //******************************************* - bool tick(uint32_t count) - { - if (enabled) - { - if (!lock_policy.is_locked()) - { - // We have something to do? - bool has_active = !active_list.empty(); - - if (has_active) - { - while (has_active && (count >= active_list.front().delta)) - { - timer_data& timer = active_list.front(); - - count -= timer.delta; - - active_list.remove(timer.id, true); - remove_callback.call_if(timer.id); - - if (timer.repeating) - { - // Reinsert the timer. - timer.delta = timer.period; - active_list.insert(timer.id); - insert_callback.call_if(timer.id); - } - - if (timer.p_callback != ETL_NULLPTR) - { - if (timer.cbk_type == timer_data::C_CALLBACK) - { - // Call the C callback. - reinterpret_cast(timer.p_callback)(); - } - else if (timer.cbk_type == timer_data::IFUNCTION) - { - // Call the function wrapper callback. - (*reinterpret_cast*>(timer.p_callback))(); - } - else if (timer.cbk_type == timer_data::DELEGATE) - { - // Call the delegate callback. - (*reinterpret_cast(timer.p_callback))(); - } - } - - has_active = !active_list.empty(); - } - - if (has_active) - { - // Subtract any remainder from the next due timeout. - active_list.front().delta -= count; - } - } - - return true; - } - } - - return false; - } - - //******************************************* - /// Starts a timer. - //******************************************* - bool start(etl::timer::id::type id_, bool immediate_ = false) - { - bool result = false; - - // Valid timer id? - if (id_ != etl::timer::id::No_Timer) - { - timer_data& timer = timer_array[id_]; - - // Registered timer? - if (timer.id != etl::timer::id::No_Timer) - { - // Has a valid period. - if (timer.period != etl::timer::state::Inactive) - { - ETL_DISABLE_TIMER_UPDATES; - if (timer.is_active()) - { - active_list.remove(timer.id, false); - remove_callback.call_if(timer.id); - } - - timer.delta = immediate_ ? 0 : timer.period; - active_list.insert(timer.id); - insert_callback.call_if(timer.id); - lock_policy.unlock(); - - result = true; - } - } - } - - return result; - } - - //******************************************* - /// Stops a timer. - //******************************************* - bool stop(etl::timer::id::type id_) - { - bool result = false; - - // Valid timer id? - if (id_ != etl::timer::id::No_Timer) - { - timer_data& timer = timer_array[id_]; - - // Registered timer? - if (timer.id != etl::timer::id::No_Timer) - { - if (timer.is_active()) - { - ETL_DISABLE_TIMER_UPDATES; - active_list.remove(timer.id, false); - remove_callback.call_if(timer.id); - lock_policy.unlock(); - } - - result = true; - } - } - - return result; - } - - //******************************************* - /// Sets a timer's period. - //******************************************* - bool set_period(etl::timer::id::type id_, uint32_t period_) - { - if (stop(id_)) - { - timer_array[id_].period = period_; - return true; - } - - return false; - } - - //******************************************* - /// Sets a timer's mode. - //******************************************* - bool set_mode(etl::timer::id::type id_, bool repeating_) - { - if (stop(id_)) - { - timer_array[id_].repeating = repeating_; - return true; - } - - return false; - } - - //******************************************* - /// Check if there is an active timer. - //******************************************* - bool has_active_timer() const - { - return !active_list.empty(); - } - - //******************************************* - /// Get the time to the next timer event. - /// Returns etl::timer::interval::No_Active_Interval if there is no active - /// timer. - //******************************************* - uint32_t time_to_next() const - { - uint32_t delta = static_cast(etl::timer::interval::No_Active_Interval); - - if (has_active_timer()) - { - delta = active_list.front().delta; - } - - return delta; - } - - //******************************************* - /// Checks if a timer is currently active. - /// Returns true if the timer is active, otherwise false. - //******************************************* - bool is_active(etl::timer::id::type id_) const - { - //// Valid timer id? - // if (is_valid_timer_id(id_)) - //{ - // if (has_active_timer()) - // { - // const timer_data& timer = timer_array[id_]; - - // // Registered timer? - // if (timer.id != etl::timer::id::No_Timer) - // { - // return timer.is_active(); - // } - // } - //} - - return false; - } - - ////******************************************* - ///// Set a callback when a timer is inserted on list - ////******************************************* - // void set_insert_callback(event_callback_type insert_) - //{ - // insert_callback = insert_; - // } - - ////******************************************* - ///// Set a callback when a timer is removed from list - ////******************************************* - // void set_remove_callback(event_callback_type remove_) - //{ - // remove_callback = remove_; - // } - - //******************************************* - void clear_insert_callback() - { - insert_callback.clear(); - } - - //******************************************* - void clear_remove_callback() - { - remove_callback.clear(); - } - - private: - - intrusive_timer(const intrusive_timer&) ETL_DELETE; - intrusive_timer& operator=(const intrusive_timer&) ETL_DELETE; - - etl::timer_lock_policy& lock_policy; - etl::private_timer::timer_register_list register_list; - etl::private_timer::timer_active_list active_list; - etl::private_timer::timer_triggered_list triggered_list; - }; -} // namespace etl - -#endif diff --git a/include/etl/intrusive_timer.h b/include/etl/intrusive_timer.h new file mode 100644 index 00000000..c7b24d85 --- /dev/null +++ b/include/etl/intrusive_timer.h @@ -0,0 +1,452 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_INTRUSIVE_TIMER_INCLUDED +#define ETL_INTRUSIVE_TIMER_INCLUDED + +#include "platform.h" +#include "algorithm.h" +#include "delegate.h" +#include "error_handler.h" +#include "nullptr.h" +#include "static_assert.h" +#include "timer_node.h" + +#include "private/timer_lists.h" + +#include + +namespace etl +{ + //*************************************************************************** + /// Interface for timer lock policy + //*************************************************************************** + class timer_lock_policy + { + public: + + virtual void lock() ETL_NOEXCEPT = 0; + virtual void unlock() ETL_NOEXCEPT = 0; + virtual bool is_locked() ETL_NOEXCEPT = 0; + }; + + //*************************************************************************** + /// Interface for intrusive timer + //*************************************************************************** + class intrusive_timer + { + public: + + typedef etl::delegate timer_update_callback_type; + + //******************************************* + /// Constructor. + //******************************************* + explicit intrusive_timer(timer_lock_policy& lock_policy_) ETL_NOEXCEPT + : lock_policy(lock_policy_) + , register_list() + , active_list() + , expired_list() + , enabled(false) + , timer_count(0U) + { + } + + //******************************************* + /// Register a timer. + /// It will not be started until the start() method is called. + //******************************************* + bool register_timer(etl::timer_node& node) ETL_NOEXCEPT + { + etl::timer::id::type id = node.get_id(); + bool result = false; + + if (id != etl::timer::id::No_Timer) + { + lock_policy.lock(); + + // Remove any existing timer with the same id. + active_list.remove(id); + register_list.remove(id); + + // Register the new timer. + register_list.insert(&node); + + lock_policy.unlock(); + + result = true; + ++timer_count; + } + + return result; + } + + //******************************************* + /// Unregister a timer. + /// By id. + //******************************************* + bool unregister_timer(etl::timer::id::type id) ETL_NOEXCEPT + { + bool result = false; + + if (id != etl::timer::id::No_Timer) + { + lock_policy.lock(); + + active_list.remove(id); + register_list.remove(id); + + lock_policy.unlock(); + + result = true; + --timer_count; + } + + return result; + } + + //******************************************* + /// Unregister a timer. + /// By node reference. + //******************************************* + bool unregister_timer(etl::timer_node& node) ETL_NOEXCEPT + { + etl::timer::id::type id = node.get_id(); + + return unregister_timer(id); + } + + //******************************************* + /// Enable/disable the timer. + //******************************************* + void enable(bool state_) ETL_NOEXCEPT + { + enabled = state_; + } + + //******************************************* + /// Get the enable/disable state. + //******************************************* + bool is_running() const ETL_NOEXCEPT + { + return enabled; + } + + //******************************************* + /// Clears the timer of data. + //******************************************* + void clear() ETL_NOEXCEPT + { + lock_policy.lock(); + + active_list.clear(); + register_list.clear(); + + lock_policy.unlock(); + } + + //******************************************* + // Called by the timer service to indicate the + // amount of time that has elapsed since the last successful call to 'tick'. + // Returns true if the tick was processed, + // false if not. + //******************************************* + bool tick(uint32_t count) + { + if (enabled) + { + if (!lock_policy.is_locked()) + { + lock_policy.lock(); + + // We have something to do? + bool has_active = !active_list.empty(); + + if (has_active) + { + // Process timers that have expired. + etl::timer_node* timer = active_list.first(); + + while (has_active && (count >= timer->delta)) + { + count -= timer->delta; + + active_list.remove(timer->get_id()); + + // If the timer is repeating then add it back to the active list. + if (timer->get_mode() == etl::timer::mode::Repeating) + { + // Reinsert the timer. + timer->delta = timer->get_period(); + active_list.insert(timer); + } + + expired_list.insert(timer); + + timer = active_list.first(); + + has_active = !active_list.empty(); + } + + if (has_active) + { + // 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; + } + } + + return false; + } + + //******************************************* + /// Starts a timer. + //******************************************* + bool start(etl::timer::id::type id_, bool immediate_ = false) ETL_NOEXCEPT + { + bool is_started = false; + + lock_policy.lock(); + + etl::timer_node* timer = find_timer(register_list, id_); + + if (timer != register_list.end()) + { + active_list.insert(timer); + is_started = true; + } + + lock_policy.unlock(); + + if (is_started && immediate_) + { + // Trigger the timer immediately. + timer->invoke(timer->get_config()); + } + + return is_started; + } + + //******************************************* + /// Stops a timer. + //******************************************* + bool stop(etl::timer::id::type id_) ETL_NOEXCEPT + { + bool is_stopped = false; + + lock_policy.lock(); + + is_stopped = (stop_impl(id_) != active_list.end()); + + lock_policy.unlock(); + + return is_stopped; + } + + //******************************************* + /// Sets a timer's period. + //******************************************* + bool set_period(etl::timer::id::type id_, uint32_t period_) ETL_NOEXCEPT + { + bool result = false; + + lock_policy.lock(); + + stop_impl(id_); + + etl::timer_node* timer = find_timer(register_list, id_); + + if (timer != active_list.end()) + { + timer->set_period(period_); + result = true; + } + + lock_policy.unlock(); + + return result; + } + + //******************************************* + /// Sets a timer's mode. + //******************************************* + bool set_mode(etl::timer::id::type id_, bool mode_) ETL_NOEXCEPT + { + bool result = false; + + lock_policy.lock(); + + stop_impl(id_); + + etl::timer_node* timer = find_timer(register_list, id_); + + if (timer != active_list.end()) + { + timer->set_mode(mode_); + result = true; + } + + lock_policy.unlock(); + + return result; + } + + //******************************************* + /// Check if there is an active timer. + //******************************************* + bool has_active_timer() const ETL_NOEXCEPT + { + lock_policy.lock(); + bool has_active = !active_list.empty(); + lock_policy.unlock(); + + return has_active; + } + + //******************************************* + /// Get the time to the next timer event. + /// Returns etl::timer::interval::No_Active_Interval if there is no active + /// timer. + //******************************************* + uint32_t time_to_next() const ETL_NOEXCEPT + { + uint32_t delta = static_cast(etl::timer::interval::No_Active_Interval); + + lock_policy.lock(); + + if (!active_list.empty()) + { + delta = active_list.first()->get_delta(); + } + + lock_policy.unlock(); + + return delta; + } + + //******************************************* + /// Checks if a timer is currently active. + /// Returns true if the timer is active, otherwise false. + //******************************************* + bool is_active(etl::timer::id::type id_) const ETL_NOEXCEPT + { + if (id_ != etl::timer::id::No_Timer) + { + lock_policy.lock(); + bool is_active = (find_timer(active_list, id_) != active_list.end()); + lock_policy.unlock(); + + return is_active; + } + + return false; + } + + //******************************************* + /// Returns the number of registered timers. + //******************************************* + size_t number_of_registered_timers() const ETL_NOEXCEPT + { + return timer_count; + } + + private: + + //******************************************* + /// Stops a timer without locking. + /// Returns the timer node if it was found, otherwise returns end(). + //******************************************* + etl::timer_node* stop_impl(etl::timer::id::type id_) ETL_NOEXCEPT + { + return active_list.remove(id_); + } + + //******************************************* + /// Finds a timer in a list by its id. + //******************************************* + template + etl::timer_node* find_timer(TList& timer_list, etl::timer::id::type id_) const ETL_NOEXCEPT + { + etl::timer_node* result = timer_list.end(); + + if (id_ != etl::timer::id::No_Timer) + { + timer_node* timer = timer_list.first(); + + while ((timer != timer_list.end()) && (timer->get_config().get_id() != id_)) + { + timer = timer_list.next(timer); + } + + result = timer; + } + + return result; + } + + //******************************************* + /// Invokes the callbacks for all expired timers. + //******************************************* + void invoke_expired_timers() + { + etl::timer_node* timer = expired_list.first(); + + while (timer != expired_list.end()) + { + const etl::timer_config& config = timer->get_config(); + + timer->invoke(config); + + timer = expired_list.next(timer); + } + + expired_list.clear(); + } + + intrusive_timer(const intrusive_timer&) ETL_DELETE; + intrusive_timer& operator=(const intrusive_timer&) ETL_DELETE; + + etl::timer_lock_policy& lock_policy; + etl::private_timer::timer_register_list register_list; + etl::private_timer::timer_active_list active_list; + etl::private_timer::timer_expired_list expired_list; + + bool enabled; + size_t timer_count; + }; +} // namespace etl + +#endif diff --git a/include/etl/private/timer_lists.h b/include/etl/private/timer_lists.h index 76fb5567..387601a4 100644 --- a/include/etl/private/timer_lists.h +++ b/include/etl/private/timer_lists.h @@ -37,7 +37,7 @@ SOFTWARE. #include //***************************************************************************** -// Definitions common to intrusive timers. +// Definitions used by intrusive_timer. //***************************************************************************** namespace etl @@ -105,7 +105,7 @@ namespace etl etl::timer_node* current = head; etl::timer_node* last = end(); - while ((current->config.id != id) && (current != end())) + while ((current->get_id() != id) && (current != end())) { last = current; current = current->registered_next; @@ -132,8 +132,7 @@ namespace etl // Clear the next pointer of the removed timer. current->registered_next = end(); - return next; - + return current; } //******************************* @@ -229,10 +228,10 @@ namespace etl head = new_timer; new_timer->active_next = end(); } - else if (new_timer->config.delta < head->config.delta) + else if (new_timer->delta < head->delta) { // Goes before the head. - head->config.delta -= new_timer->config.delta; + head->delta -= new_timer->delta; new_timer->active_next = head; head = new_timer; } @@ -243,7 +242,7 @@ namespace etl timer_node* timer = head->active_next; // Adjust the new_timer delta to take account of the head timer's delta. - new_timer->config.delta -= previous_timer->config.delta; + new_timer->delta -= previous_timer->delta; bool inserted = false; @@ -251,21 +250,21 @@ namespace etl { // Find the correct place to insert. // The list is sorted by delta, so we need to find the first timer with a delta greater than the new timer's delta. - if (new_timer->config.delta < timer->config.delta) + if (new_timer->delta < timer->delta) { // Insert after the previous timer. previous_timer->active_next = new_timer; new_timer->active_next = timer; // Adjust the next delta for the new timer's delta. - timer->config.delta -= new_timer->config.delta; + timer->delta -= new_timer->delta; inserted = true; } else { // Adjust the new_timer delta to take account of the last timer's delta. - new_timer->config.delta -= timer->config.delta; + new_timer->delta -= timer->delta; } // Move to the next timer in the list. @@ -299,7 +298,7 @@ namespace etl etl::timer_node* current = head; etl::timer_node* last = end(); - while ((current->config.id != id) && (current != end())) + while ((current->get_id() != id) && (current != end())) { last = current; current = current->active_next; @@ -316,7 +315,7 @@ namespace etl // Adjust the next delta. if (next != end()) { - next->config.delta += current->config.delta; + next->delta += current->delta; } // Remove it from the list. @@ -332,7 +331,7 @@ namespace etl // Clear the next pointer of the removed timer. current->active_next = end(); - return next; + return current; } //******************************* @@ -393,14 +392,14 @@ namespace etl //************************************************************************* /// The list of timers that are registered. //************************************************************************* - class timer_triggered_list + class timer_expired_list { public: //******************************* /// Constructor. //******************************* - timer_triggered_list() ETL_NOEXCEPT + timer_expired_list() ETL_NOEXCEPT : head(end()) , tail(end()) { @@ -419,7 +418,7 @@ namespace etl //******************************* void insert(etl::timer_node* new_timer) ETL_NOEXCEPT { - new_timer->triggered_next = end(); + new_timer->expired_next = end(); if (empty()) { @@ -427,7 +426,7 @@ namespace etl } else { - tail->triggered_next = new_timer; + tail->expired_next = new_timer; } tail = new_timer; @@ -459,7 +458,7 @@ namespace etl { if (timer != end()) { - return timer->triggered_next; + return timer->expired_next; } else { @@ -485,9 +484,9 @@ namespace etl while (head != end()) { - head = timer->triggered_next; - timer->triggered_next = etl::timer_node::unlinked(); - timer = head; + head = timer->expired_next; + timer->expired_next = timer->unlinked(); + timer = head; } tail = end(); diff --git a/include/etl/timer_lists.h b/include/etl/timer_lists.h deleted file mode 100644 index 412468a2..00000000 --- a/include/etl/timer_lists.h +++ /dev/null @@ -1,515 +0,0 @@ -/****************************************************************************** -The MIT License(MIT) - -Embedded Template Library. -https://github.com/ETLCPP/etl -https://www.etlcpp.com - -Copyright(c) 2026 John Wellbelove - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files(the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions : - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -******************************************************************************/ - -#ifndef ETL_TIMER_LISTS_INCLUDED -#define ETL_TIMER_LISTS_INCLUDED - -#include "../platform.h" -#include "../nullptr.h" -#include "../timer_node.h" - -//***************************************************************************** -// Definitions common to intrusive timers. -//***************************************************************************** - -namespace etl -{ - namespace private_timer - { - //************************************************************************* - /// The list of timers that are registered. - /// The list does not do any checking of parameters as it is assumed that the timer service has already done this. - //************************************************************************* - class timer_register_list - { - public: - - //******************************* - timer_register_list() ETL_NOEXCEPT - : head(end()) - , tail(end()) - { - } - - //******************************* - bool empty() const - { - return head == end(); - } - - //******************************* - // Adds the timer in to the list. - //******************************* - void insert(etl::timer_node* new_timer) ETL_NOEXCEPT - { - if (empty()) - { - head = new_timer; - } - else - { - tail->registered_next = new_timer; - } - - tail = new_timer; - new_timer->registered_next = end(); - } - - //******************************* - // Remove the timer from the list. - // Returns the next timer in the list after the removed timer, or nullptr if the removed timer was the last in the list. - //******************************* - etl::timer_node* remove(etl::timer_node* timer) ETL_NOEXCEPT - { - if (timer == head) - { - head = timer->registered_next; - - if (head == end()) - { - tail = end(); - } - - timer->registered_next = ETL_NULLPTR; - - return head; - } - else - { - etl::timer_node* previous = head; - - // Step through the list until we find the timer to remove, or reach the end of the list. - while ((previous->registered_next != end()) && (previous->registered_next != timer)) - { - previous = previous->registered_next; - } - - // If we found the timer, remove it from the list. - if (previous->registered_next == timer) - { - previous->registered_next = timer->registered_next; - - if (timer == tail) - { - tail = previous; - } - - timer->registered_next = end(); - - return previous->registered_next; - } - else - { - // Timer not found in the list. - return end(); - } - } - } - - //******************************* - /// Returns the first timer in the list. - /// If the list is empty, returns end(). - //******************************* - etl::timer_node* first() const ETL_NOEXCEPT - { - return head; - } - - //******************************* - /// Returns the next timer in the list after the given timer. - /// May be end() if the given timer is the last in the list. - /// If the given timer is nullptr, returns end(). - //******************************* - etl::timer_node* next(etl::timer_node* timer) const ETL_NOEXCEPT - { - if (timer != end()) - { - return timer->registered_next; - } - else - { - return end(); - } - } - - //******************************* - /// Returns the end of the list. - /// Always returns &etl::timer_node::sentinal(). - //******************************* - etl::timer_node* const end() const ETL_NOEXCEPT - { - return etl::timer_node::sentinal(); - } - - //******************************* - /// Clear the list of entries. - //******************************* - void clear() - { - etl::timer_node* timer = head; - - while (timer != end()) - { - timer = pop_front(); - } - - head = end(); - tail = end(); - } - - private: - - //******************************* - // Removes the timer at the front of the list. - // Returns the new head of the list, which may be nullptr if the list is now empty. - //******************************* - etl::timer_node* pop_front() ETL_NOEXCEPT - { - etl::timer_node* timer = head; - head = timer->registered_next; - timer->registered_next = timer->unlinked(); - - return head; - } - - etl::timer_node* head; - etl::timer_node* tail; - }; - - //************************************************************************* - /// The list of timers that are active. - /// The list does not do any checking of parameters as it is assumed that the timer service has already done this. - //************************************************************************* - class timer_active_list - { - public: - - //******************************* - timer_active_list() ETL_NOEXCEPT - : head(end()) - { - } - - //******************************* - bool empty() const ETL_NOEXCEPT - { - return head == end(); - } - - //******************************* - // Inserts the timer at the correct delta position - //******************************* - void insert(etl::timer_node* new_timer) ETL_NOEXCEPT - { - if (empty()) - { - // No entries yet. - head = new_timer; - new_timer->active_next = end(); - } - else if (new_timer->config.delta < head->config.delta) - { - // Goes before the head. - head->config.delta -= new_timer->config.delta; - new_timer->active_next = head; - head = new_timer; - } - else - { - // We already have entries, and new_timer doesn't go before head. - timer_node* previous_timer = head; - timer_node* timer = head->active_next; - - // Adjust the new_timer delta to take account of the head timer's delta. - new_timer->config.delta -= previous_timer->config.delta; - - bool inserted = false; - - while (!inserted && (timer != end())) - { - // Find the correct place to insert. - // The list is sorted by delta, so we need to find the first timer with a delta greater than the new timer's delta. - if (new_timer->config.delta < timer->config.delta) - { - // Insert after the previous timer. - previous_timer->active_next = new_timer; - new_timer->active_next = timer; - - // Adjust the next delta for the new timer's delta. - timer->config.delta -= new_timer->config.delta; - - inserted = true; - } - else - { - // Adjust the new_timer delta to take account of the last timer's delta. - new_timer->config.delta -= timer->config.delta; - } - - // Move to the next timer in the list. - previous_timer = timer; - timer = timer->active_next; - } - - if (!inserted) - { - // Reached the end so attach to the tail. - previous_timer->active_next = new_timer; - new_timer->active_next = end(); - } - } - } - - //******************************* - /// Removes the selected timer from the list. - /// No action is taken if the timer is not found. - /// returns the next timer in the list after the removed timer, or end() if the removed timer was the last in the list. - /// The list does not do any checking of parameters as it is assumed that the timer service has already done this. - //******************************* - etl::timer_node* remove(etl::timer_node* timer) ETL_NOEXCEPT - { - // Find the timer in the list. - etl::timer_node* current = head; - etl::timer_node* last = end(); - - while (current != timer && current != end()) - { - last = current; - current = current->active_next; - } - - // Not found? - if (current == end()) - { - return end(); - } - - etl::timer_node* next = current->active_next; - - // Adjust the next delta. - if (next != end()) - { - next->config.delta += current->config.delta; - } - - // Remove it from the list. - if (last == end()) - { - head = next; - } - else - { - last->active_next = next; - } - - // Clear the next pointer of the removed timer. - current->active_next = ETL_NULLPTR; - - return next; - } - - //******************************* - /// Returns the first timer in the list. - /// If the list is empty, returns end(). - //******************************* - etl::timer_node* first() const ETL_NOEXCEPT - { - return head; - } - - //******************************* - /// Returns the next timer in the list after the given timer. - /// May be end() if the given timer is the last in the list. - /// If the given timer is nullptr, returns end(). - //******************************* - etl::timer_node* next(etl::timer_node* timer) const ETL_NOEXCEPT - { - if (timer != end()) - { - return timer->active_next; - } - else - { - return end(); - } - } - - //******************************* - /// Returns the end of the list. - /// Always returns &etl::timer_node::sentinal(). - //******************************* - etl::timer_node* const end() const ETL_NOEXCEPT - { - return etl::timer_node::sentinal(); - } - - //******************************* - /// Clear the list of entries. - //******************************* - void clear() - { - etl::timer_node* timer = head; - - while (timer != end()) - { - timer = pop_front(); - } - - head = end(); - } - - private: - - //******************************* - // Removes the timer at the front of the list. - // Returns the new head of the list, which may be nullptr if the list is now empty. - //******************************* - etl::timer_node* pop_front() ETL_NOEXCEPT - { - etl::timer_node* timer = head; - head = timer->active_next; - timer->active_next = timer->unlinked(); - - return head; - } - - etl::timer_node* head; - }; - - //************************************************************************* - /// The list of timers that are registered. - //************************************************************************* - class timer_triggered_list - { - public: - - //******************************* - timer_triggered_list() ETL_NOEXCEPT - : head(end()) - , tail(end()) - { - } - - //******************************* - bool empty() const - { - return head == end(); - } - - //******************************* - // Adds the timer in to the list. - //******************************* - void insert(etl::timer_node* new_timer) ETL_NOEXCEPT - { - new_timer->triggered_next = end(); - - if (empty()) - { - head = new_timer; - } - else - { - tail->triggered_next = new_timer; - } - - tail = new_timer; - } - - //******************************* - /// Returns the first timer in the list. - /// If the list is empty, returns nullptr. - //******************************* - etl::timer_node* first() const ETL_NOEXCEPT - { - return head; - } - - //******************************* - /// Returns the next timer in the list after the given timer. - /// May be end() if the given timer is the last in the list. - /// If the given timer is nullptr, returns end(). - //******************************* - etl::timer_node* next(etl::timer_node* timer) const ETL_NOEXCEPT - { - if (timer != end()) - { - return timer->triggered_next; - } - else - { - return end(); - } - } - - //******************************* - /// Returns the end of the list. - /// Always returns &etl::timer_node::sentinal(). - //******************************* - etl::timer_node* const end() const ETL_NOEXCEPT - { - return etl::timer_node::sentinal(); - } - - //******************************* - /// Clear the list of entries. - //******************************* - void clear() - { - etl::timer_node* timer = head; - - while (timer != end()) - { - timer = pop_front(); - } - - head = end(); - tail = end(); - } - - private: - - //******************************* - // Removes the timer at the front of the list. - // Returns the new head of the list, which may be nullptr if the list is now empty. - //******************************* - etl::timer_node* pop_front() ETL_NOEXCEPT - { - etl::timer_node* timer = head; - head = timer->triggered_next; - timer->triggered_next = timer->unlinked(); - - return head; - } - - etl::timer_node* head; - etl::timer_node* tail; - }; - } // namespace private_timer -} // namespace etl - -#endif diff --git a/include/etl/timer_node.h b/include/etl/timer_node.h index 69b9912b..4a10114b 100644 --- a/include/etl/timer_node.h +++ b/include/etl/timer_node.h @@ -46,11 +46,12 @@ namespace etl { class timer_register_list; class timer_active_list; - class timer_triggered_list; - - class intrusive_callback_timer; + class timer_expired_list; } // namespace private_timer + class timer_node; + class intrusive_timer; + //*************************************************************************** /// A timer configuration data structure. //*************************************************************************** @@ -60,16 +61,16 @@ namespace etl friend class private_timer::timer_register_list; friend class private_timer::timer_active_list; - friend class private_timer::timer_triggered_list; + friend class private_timer::timer_expired_list; - friend class intrusive_callback_timer; + friend class timer_node; + friend class intrusive_timer; //******************************************* /// Constructor. //******************************************* ETL_CONSTEXPR timer_config(etl::timer::id::type id_, uint32_t period_, etl::timer::mode::type repeating_) ETL_NOEXCEPT : period(period_) - , delta(period_) , mode(repeating_) , id(id_) { @@ -83,14 +84,6 @@ namespace etl return period; } - //******************************************* - /// Returns the time remaining until the timer expires. - //******************************************* - ETL_CONSTEXPR uint32_t get_delta() const ETL_NOEXCEPT - { - return delta; - } - //******************************************* /// Returns the mode of the timer. //******************************************* @@ -113,7 +106,6 @@ namespace etl ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT { period = etl::timer::state::Inactive; - delta = etl::timer::state::Inactive; mode = etl::timer::mode::Repeating; id = etl::timer::id::No_Timer; } @@ -121,7 +113,6 @@ namespace etl private: uint32_t period; ///< The period of the timer. - uint32_t delta; ///< The time remaining until the timer expires. etl::timer::mode::type mode; ///< Whether the timer is repeating or single-shot. etl::timer::id::type id; ///< The id of the timer. }; @@ -138,9 +129,9 @@ namespace etl friend class private_timer::timer_register_list; friend class private_timer::timer_active_list; - friend class private_timer::timer_triggered_list; + friend class private_timer::timer_expired_list; - friend class intrusive_callback_timer; + friend class intrusive_timer; //******************************************* /// Constructor. @@ -148,8 +139,9 @@ namespace etl timer_node(const timer_config& config_) ETL_NOEXCEPT : registered_next(unlinked()) , active_next(unlinked()) - , triggered_next(unlinked()) + , expired_next(unlinked()) , config(config_) + , delta(config_.get_period()) { } @@ -159,8 +151,9 @@ namespace etl timer_node(const timer_node& node_) ETL_NOEXCEPT : registered_next(unlinked()) , active_next(unlinked()) - , triggered_next(unlinked()) + , expired_next(unlinked()) , config(node_.config) + , delta(node_.config.get_period()) { } @@ -172,8 +165,56 @@ namespace etl return config; } + //******************************************* + /// Returns the delta of the timer. + //******************************************* + ETL_CONSTEXPR uint32_t get_delta() const ETL_NOEXCEPT + { + return delta; + } + + //******************************************* + /// Returns the period of the timer. + //******************************************* + ETL_CONSTEXPR uint32_t get_period() const ETL_NOEXCEPT + { + return config.get_period(); + } + + //******************************************* + /// Returns the mode of the timer. + //******************************************* + ETL_CONSTEXPR uint32_t get_mode() const ETL_NOEXCEPT + { + return config.get_mode(); + } + + //******************************************* + /// Returns the id of the timer. + //******************************************* + ETL_CONSTEXPR etl::timer::id::type get_id() const ETL_NOEXCEPT + { + return config.get_id(); + } + private: + //******************************************* + /// Sets the period of the timer. + //******************************************* + void set_period(uint32_t period_) ETL_NOEXCEPT + { + config.period = period_; + } + + //******************************************* + /// Sets the mode of the timer. + //******************************************* + void set_mode(etl::timer::mode::type mode_) ETL_NOEXCEPT + { + config.mode = mode_; + } + //******************************************* /// Returns a nullptr to indicate that the timer is not linked into any list. //******************************************* @@ -204,24 +245,25 @@ namespace etl { registered_next = unlinked(); active_next = unlinked(); - triggered_next = unlinked(); + expired_next = unlinked(); + delta = config.get_period(); } //******************************************* /// Invokes the timer's callback. //******************************************* - virtual void invoke() + virtual void invoke(const etl::timer_config& /*config*/) { // Default implementation does nothing. } timer_node& operator=(const timer_node&) ETL_DELETE; - timer_node* registered_next; ///< The next timer in the registered timer list. nullptr if the timer is not registered. - timer_node* active_next; ///< The next timer in the active timer list. nullptr if the timer is not active. - timer_node* triggered_next; ///< The next timer in the triggered timer list. nullptr if the timer is not triggered. - - timer_config config; ///< The configuration of the timer. + timer_node* registered_next; ///< The next timer in the registered timer list. nullptr if the timer is not registered. + timer_node* active_next; ///< The next timer in the active timer list. nullptr if the timer is not active. + timer_node* expired_next; ///< The next timer in the expired timer list. nullptr if the timer is not expired. + timer_config config; ///< The configuration of the timer. + uint32_t delta; ///< The time remaining until the timer expires. }; //*************************************************************************** @@ -242,9 +284,9 @@ namespace etl //******************************************* /// Invokes the timer's callback. //******************************************* - virtual void invoke() ETL_OVERRIDE + virtual void invoke(const etl::timer_config& config) ETL_OVERRIDE { - callback(); + callback(config); } TCallback callback; ///< The callback delegate for the timer. @@ -259,18 +301,21 @@ namespace etl //******************************************* /// Constructor. //******************************************* - message_timer_node(etl::imessage_router& router_, TMessage message) ETL_NOEXCEPT + message_timer_node(etl::imessage_router& router_, TMessage message_) ETL_NOEXCEPT : timer_node() , router(&router_) + , message(message_) { } //******************************************* /// Invokes the timer's callback. //******************************************* - virtual void invoke() ETL_OVERRIDE + virtual void invoke(const etl::timer_config& config) ETL_OVERRIDE { - callback(); + message.set_timer_config(config); + + router->receive(message); } etl::imessage_router* router; ///< The message router to send the message to. diff --git a/test/test_intrusive_timer.cpp b/test/test_intrusive_timer.cpp new file mode 100644 index 00000000..c35e0314 --- /dev/null +++ b/test/test_intrusive_timer.cpp @@ -0,0 +1,1038 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/error_handler.h" +#include "etl/intrusive_timer.h" +#include "etl/timer.h" +#include "etl/timer_node.h" + +#include +#include +#include +#include + +#if defined(ETL_COMPILER_MICROSOFT) + #include +#endif + +#define REALTIME_TEST 0 + +namespace +{ + uint64_t ticks = 0ULL; + + class TimerInsertRemoveTest + { + public: + + uint32_t inserted; + uint32_t removed; + TimerInsertRemoveTest() + : inserted(0) + , removed(0) + { + } + + void insert_handler(etl::timer::id::type id_) + { + (void)id_; + inserted++; + } + + void remove_handler(etl::timer::id::type id_) + { + (void)id_; + removed++; + } + + void clear(void) + { + inserted = 0; + removed = 0; + } + }; + + TimerInsertRemoveTest timerInsertRemoveTest; + + //*************************************************************************** + class LockPolicy : public etl::timer_lock_policy + { + public: + + //******************************************* + LockPolicy() + : timer_lock_policy() + , count(0) + { + } + + //******************************************* + void lock() ETL_NOEXCEPT override + { + ++count; + } + + //******************************************* + void unlock() ETL_NOEXCEPT override + { + --count; + } + + //******************************************* + bool is_locked() ETL_NOEXCEPT override + { + return (count > 0); + } + + //******************************************* + int get_count() const + { + return count; + } + + private: + + int count; + }; + + SUITE(test_intrusive_timer) + { + //************************************************************************* + TEST(test_intrusive_timer_construction) + { + LockPolicy lock_policy; + + etl::intrusive_timer timer_controller(lock_policy); + + CHECK_FALSE(timer_controller.is_running()); + CHECK_EQUAL(0U, timer_controller.number_of_registered_timers()); + CHECK_EQUAL(0U, lock_policy.get_count()); + } + + //************************************************************************* + TEST(test_intrusive_timer_register_timers) + { + LockPolicy lock_policy; + + etl::intrusive_timer timer_controller(lock_policy); + + etl::timer_node node1(etl::timer_config(etl::timer::id::type(1), 100, etl::timer::mode::Single_Shot)); + etl::timer_node node2(etl::timer_config(etl::timer::id::type(2), 200, etl::timer::mode::Repeating)); + etl::timer_node node3(etl::timer_config(etl::timer::id::type(3), 300, etl::timer::mode::Repeating)); + + CHECK_TRUE(timer_controller.register_timer(node1)); + CHECK_EQUAL(1U, timer_controller.number_of_registered_timers()); + CHECK_TRUE(timer_controller.register_timer(node2)); + CHECK_EQUAL(2U, timer_controller.number_of_registered_timers()); + CHECK_TRUE(timer_controller.register_timer(node3)); + CHECK_EQUAL(3U, timer_controller.number_of_registered_timers()); + CHECK_FALSE(timer_controller.is_running()); + CHECK_EQUAL(0U, lock_policy.get_count()); + } + + //************************************************************************* + TEST(test_intrusive_timer_unregister_timers_by_node) + { + LockPolicy lock_policy; + + etl::intrusive_timer timer_controller(lock_policy); + + etl::timer_node node1(etl::timer_config(etl::timer::id::type(1), 100, etl::timer::mode::Single_Shot)); + etl::timer_node node2(etl::timer_config(etl::timer::id::type(2), 200, etl::timer::mode::Repeating)); + etl::timer_node node3(etl::timer_config(etl::timer::id::type(3), 300, etl::timer::mode::Repeating)); + + timer_controller.register_timer(node1); + timer_controller.register_timer(node2); + timer_controller.register_timer(node3); + + CHECK_TRUE(timer_controller.unregister_timer(node1)); + CHECK_EQUAL(2U, timer_controller.number_of_registered_timers()); + CHECK_TRUE(timer_controller.unregister_timer(node2)); + CHECK_EQUAL(1U, timer_controller.number_of_registered_timers()); + CHECK_TRUE(timer_controller.unregister_timer(node3)); + CHECK_EQUAL(0U, timer_controller.number_of_registered_timers()); + CHECK_FALSE(timer_controller.is_running()); + CHECK_EQUAL(0U, lock_policy.get_count()); + } + + //************************************************************************* + TEST(test_intrusive_timer_unregister_timers_by_id) + { + LockPolicy lock_policy; + + etl::intrusive_timer timer_controller(lock_policy); + + etl::timer_node node1(etl::timer_config(etl::timer::id::type(1), 100, etl::timer::mode::Single_Shot)); + etl::timer_node node2(etl::timer_config(etl::timer::id::type(2), 200, etl::timer::mode::Repeating)); + etl::timer_node node3(etl::timer_config(etl::timer::id::type(3), 300, etl::timer::mode::Repeating)); + + timer_controller.register_timer(node1); + timer_controller.register_timer(node2); + timer_controller.register_timer(node3); + + CHECK_TRUE(timer_controller.unregister_timer(node1.get_config().get_id())); + CHECK_EQUAL(2U, timer_controller.number_of_registered_timers()); + CHECK_TRUE(timer_controller.unregister_timer(node2.get_config().get_id())); + CHECK_EQUAL(1U, timer_controller.number_of_registered_timers()); + CHECK_TRUE(timer_controller.unregister_timer(node3.get_config().get_id())); + CHECK_EQUAL(0U, timer_controller.number_of_registered_timers()); + CHECK_FALSE(timer_controller.is_running()); + } + + //************************************************************************* + TEST(test_intrusive_timer_register_timers_then_modify_them) + { + LockPolicy lock_policy; + + etl::intrusive_timer timer_controller(lock_policy); + + etl::timer_node node1(etl::timer_config(etl::timer::id::type(1), 100, etl::timer::mode::Single_Shot)); + etl::timer_node node2(etl::timer_config(etl::timer::id::type(2), 200, etl::timer::mode::Repeating)); + etl::timer_node node3(etl::timer_config(etl::timer::id::type(3), 300, etl::timer::mode::Repeating)); + + timer_controller.register_timer(node1); + timer_controller.register_timer(node2); + timer_controller.register_timer(node3); + + // Modify the timer periods. + timer_controller.set_period(1, 150U); + timer_controller.set_period(2, 250U); + timer_controller.set_period(3, 350U); + + // Modify the timer modes. + timer_controller.set_mode(1, etl::timer::mode::Repeating); + timer_controller.set_mode(2, etl::timer::mode::Single_Shot); + timer_controller.set_mode(3, etl::timer::mode::Single_Shot); + + CHECK_EQUAL(150U, node1.get_period()); + CHECK_EQUAL(250U, node2.get_period()); + CHECK_EQUAL(350U, node3.get_period()); + + CHECK_EQUAL(etl::timer::mode::Repeating, node1.get_mode()); + CHECK_EQUAL(etl::timer::mode::Single_Shot, node2.get_mode()); + CHECK_EQUAL(etl::timer::mode::Single_Shot, node3.get_mode()); + } + + ////************************************************************************* + //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()); + //} + + // //************************************************************************* + // TEST(message_timer_one_shot_after_timeout) + // { + // etl::intrusive_timer<1> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Single_Shot); + // object.tick_list.clear(); + // + // timer_controller.start(id1); + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 1U; + // + // while (ticks <= 100U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // // Timer should have timed out. + // + // CHECK(timer_controller.set_period(id1, 50)); + // timer_controller.start(id1); + // + // object.tick_list.clear(); + // + // ticks = 0; + // + // while (ticks <= 100U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // // Timer should have timed out. + // + // CHECK_EQUAL(50U, *object.tick_list.data()); + // + // CHECK(timer_controller.unregister_timer(id1)); + // CHECK(!timer_controller.unregister_timer(id1)); + // CHECK(!timer_controller.start(id1)); + // CHECK(!timer_controller.stop(id1)); + // } + // + // //************************************************************************* + // TEST(intrusive_timer_repeating) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // 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 = 1U; + // + // while (ticks <= 100U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {37, 74}; + // std::vector compare2 = {23, 46, 69, 92}; + // std::vector compare3 = {11, 22, 33, 44, 55, 66, 77, 88, 99}; + // + // 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(intrusive_timer_repeating_bigger_step) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // object.tick_list.clear(); + // free_tick_list1.clear(); + // free_tick_list2.clear(); + // + // timer_controller.start(id1); + // timer_controller.start(id3); + // timer_controller.start(id2); + // + // CHECK(!timer_controller.is_running()); + // + // timer_controller.enable(true); + // + // CHECK(timer_controller.is_running()); + // + // ticks = 0; + // + // const uint32_t step = 5U; + // + // while (ticks <= 100U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {40, 75}; + // std::vector compare2 = {25, 50, 70, 95}; + // std::vector compare3 = {15, 25, 35, 45, 55, 70, 80, 90, 100}; + // + // 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(intrusive_timer_repeating_stop_start) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // object.tick_list.clear(); + // free_tick_list1.clear(); + // free_tick_list2.clear(); + // + // timer_controller.start(id3); + // timer_controller.start(id2); + // + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 1U; + // + // while (ticks <= 100U) + // { + // if (ticks == 40) + // { + // timer_controller.start(id1); + // timer_controller.stop(id2); + // } + // + // if (ticks == 80) + // { + // timer_controller.stop(id1); + // timer_controller.start(id2); + // } + // + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {77}; + // std::vector compare2 = {23}; + // std::vector compare3 = {11, 22, 33, 44, 55, 66, 77, 88, 99}; + // + // 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(intrusive_timer_timer_starts_timer_small_step) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback2, 100, etl::timer::mode::Single_Shot); + // etl::timer::id::type id2 = timer_controller.register_timer(member_callback, 10, etl::timer::mode::Single_Shot); + // etl::timer::id::type id3 = timer_controller.register_timer(member_callback, 22, etl::timer::mode::Single_Shot); + // + // (void)id2; + // (void)id3; + // + // object.set_controller(timer_controller); + // + // object.tick_list.clear(); + // + // timer_controller.start(id1); + // + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 1U; + // + // while (ticks <= 200U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {100, 110, 122}; + // + // CHECK(object.tick_list.size() != 0); + // + // CHECK_ARRAY_EQUAL(compare1.data(), object.tick_list.data(), compare1.size()); + // } + // + // //************************************************************************* + // TEST(intrusive_timer_timer_starts_timer_big_step) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback2, 100, etl::timer::mode::Single_Shot); + // etl::timer::id::type id2 = timer_controller.register_timer(member_callback, 10, etl::timer::mode::Single_Shot); + // etl::timer::id::type id3 = timer_controller.register_timer(member_callback, 22, etl::timer::mode::Single_Shot); + // + // (void)id2; + // (void)id3; + // + // object.set_controller(timer_controller); + // + // object.tick_list.clear(); + // + // timer_controller.start(id1); + // + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 3; + // + // while (ticks <= 200U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {102, 111, 123}; + // + // CHECK(object.tick_list.size() != 0); + // + // CHECK_ARRAY_EQUAL(compare1.data(), object.tick_list.data(), compare1.size()); + // } + // + // //************************************************************************* + // TEST(intrusive_timer_repeating_register_unregister) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1; + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // object.tick_list.clear(); + // free_tick_list1.clear(); + // free_tick_list2.clear(); + // + // timer_controller.start(id3); + // timer_controller.start(id2); + // + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 1U; + // + // while (ticks <= 100U) + // { + // if (ticks == 40) + // { + // timer_controller.unregister_timer(id2); + // + // id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // timer_controller.start(id1); + // } + // + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {77}; + // std::vector compare2 = {23}; + // std::vector compare3 = {11, 22, 33, 44, 55, 66, 77, 88, 99}; + // + // 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(intrusive_timer_repeating_clear) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // 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 = 1U; + // + // while (ticks <= 100U) + // { + // ticks += step; + // + // if (ticks == 40) + // { + // timer_controller.clear(); + // } + // + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {37}; + // std::vector compare2 = {23}; + // std::vector compare3 = {11, 22, 33}; + // + // 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(intrusive_timer_delayed_immediate) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // object.tick_list.clear(); + // free_tick_list1.clear(); + // free_tick_list2.clear(); + // + // timer_controller.enable(true); + // + // ticks = 5; + // timer_controller.tick(uint32_t(ticks)); + // + // timer_controller.start(id1, etl::timer::start::Immediate); + // timer_controller.start(id2, etl::timer::start::Immediate); + // timer_controller.start(id3, etl::timer::start::Delayed); + // + // const uint32_t step = 1U; + // + // while (ticks <= 100U) + // { + // ticks += step; + // timer_controller.tick(step); + // } + // + // std::vector compare1 = {6, 42, 79}; + // std::vector compare2 = {6, 28, 51, 74, 97}; + // std::vector compare3 = {16, 27, 38, 49, 60, 71, 82, 93}; + // + // 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(intrusive_timer_one_shot_big_step_short_delay_insert) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(free_callback1, 15, etl::timer::mode::Single_Shot); + // etl::timer::id::type id2 = timer_controller.register_timer(free_callback2, 5, etl::timer::mode::Repeating); + // + // free_tick_list1.clear(); + // free_tick_list2.clear(); + // + // timer_controller.start(id1); + // timer_controller.start(id2); + // + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 11U; + // + // ticks += step; + // timer_controller.tick(step); + // + // ticks += step; + // timer_controller.tick(step); + // + // std::vector compare1 = {22}; + // std::vector compare2 = {11, 11, 22, 22}; + // + // CHECK(free_tick_list1.size() != 0); + // CHECK(free_tick_list2.size() != 0); + // + // CHECK_ARRAY_EQUAL(compare1.data(), free_tick_list1.data(), compare1.size()); + // CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list2.data(), compare2.size()); + // } + // + // //************************************************************************* + // TEST(intrusive_timer_one_shot_empty_list_huge_tick_before_insert) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(free_callback1, 5, etl::timer::mode::Single_Shot); + // + // free_tick_list1.clear(); + // + // timer_controller.start(id1); + // + // timer_controller.enable(true); + // + // ticks = 0; + // + // const uint32_t step = 5U; + // + // for (uint32_t i = 0U; i < step; ++i) + // { + // ++ticks; + // timer_controller.tick(1); + // } + // + // // Huge tick count. + // timer_controller.tick(UINT32_MAX - step + 1); + // + // timer_controller.start(id1); + // + // for (uint32_t i = 0U; i < step; ++i) + // { + // ++ticks; + // timer_controller.tick(1); + // } + // std::vector compare1 = {5, 10}; + // + // CHECK(free_tick_list1.size() != 0); + // + // CHECK_ARRAY_EQUAL(compare1.data(), free_tick_list1.data(), compare1.size()); + // } + // + // //************************************************************************* + // TEST(message_timer_time_to_next_repeating) + // { + // etl::intrusive_timer<3> timer_controller; + // + // etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Repeating); + // etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = timer_controller.register_timer(free_callback2, 11, etl::timer::mode::Repeating); + // + // timer_controller.start(id1); + // timer_controller.start(id3); + // timer_controller.start(id2); + // + // timer_controller.enable(true); + // + // CHECK_EQUAL(11, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(4, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(8, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(1, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(5, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(2, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(2, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(6, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(10, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(3, timer_controller.time_to_next()); + // + // timer_controller.tick(7); + // CHECK_EQUAL(4, timer_controller.time_to_next()); + // } + // + // //************************************************************************* + // TEST(message_timer_time_to_next_with_has_active_timer) + // { + // etl::intrusive_timer<3> timer_controller; + // + // 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); + // + // timer_controller.start(id1); + // timer_controller.start(id3); + // timer_controller.start(id2); + // + // timer_controller.enable(true); + // + // timer_controller.tick(11); + // CHECK_EQUAL(12, timer_controller.time_to_next()); + // CHECK_TRUE(timer_controller.has_active_timer()); + // + // timer_controller.tick(23); + // CHECK_EQUAL(3, timer_controller.time_to_next()); + // CHECK_TRUE(timer_controller.has_active_timer()); + // + // timer_controller.tick(2); + // CHECK_EQUAL(1, timer_controller.time_to_next()); + // CHECK_TRUE(timer_controller.has_active_timer()); + // + // timer_controller.tick(1); + // CHECK_EQUAL(static_cast(etl::timer::interval::No_Active_Interval), timer_controller.time_to_next()); + // CHECK_FALSE(timer_controller.has_active_timer()); + // } + // + // //************************************************************************* + // class test_object + // { + // public: + // + // void call() + // { + // ++called; + // } + // + // size_t called = 0UL; + // }; + // + // using callback_type = etl::iintrusive_timer::callback_type; + // + // TEST(intrusive_timer_call_etl_delegate) + // { + // test_object test_obj; + // callback_type delegate_callback = callback_type::create(test_obj); + // etl::intrusive_timer<1> timer_controller; + // + // timer_controller.enable(true); + // + // etl::timer::id::type id = timer_controller.register_timer(delegate_callback, 5, etl::timer::mode::Single_Shot); + // timer_controller.start(id); + // + // timer_controller.tick(4); + // CHECK(test_obj.called == 0); + // + // timer_controller.tick(2); + // CHECK(test_obj.called == 1); + // } + // + // //************************************************************************* + // TEST(intrusive_timer_is_active) + // { + // timerInsertRemoveTest.clear(); + // etl::intrusive_timer<4> timer_controller; + // + // 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); + // + // timer_controller.set_insert_callback( + // event_callback_type::create()); + // timer_controller.set_remove_callback( + // event_callback_type::create()); + // + // timer_controller.start(id1); + // timer_controller.start(id3); + // timer_controller.start(id2); + // + // CHECK_EQUAL(3, timerInsertRemoveTest.inserted); + // CHECK_EQUAL(0, timerInsertRemoveTest.removed); + // + // timer_controller.enable(true); + // + // CHECK_TRUE(timer_controller.is_active(id1)); + // CHECK_TRUE(timer_controller.is_active(id2)); + // CHECK_TRUE(timer_controller.is_active(id3)); + // + // CHECK_EQUAL(3, timerInsertRemoveTest.inserted); + // CHECK_EQUAL(0, timerInsertRemoveTest.removed); + // + // timer_controller.tick(11); + // CHECK_TRUE(timer_controller.is_active(id1)); + // CHECK_TRUE(timer_controller.is_active(id2)); + // CHECK_FALSE(timer_controller.is_active(id3)); + // + // CHECK_EQUAL(3, timerInsertRemoveTest.inserted); + // CHECK_EQUAL(1, timerInsertRemoveTest.removed); + // + // timer_controller.tick(23 - 11); + // CHECK_TRUE(timer_controller.is_active(id1)); + // CHECK_FALSE(timer_controller.is_active(id2)); + // CHECK_FALSE(timer_controller.is_active(id3)); + // + // CHECK_EQUAL(3, timerInsertRemoveTest.inserted); + // CHECK_EQUAL(2, timerInsertRemoveTest.removed); + // + // timer_controller.tick(37 - 23); + // CHECK_FALSE(timer_controller.is_active(id1)); + // CHECK_FALSE(timer_controller.is_active(id2)); + // CHECK_FALSE(timer_controller.is_active(id3)); + // + // CHECK_EQUAL(3, timerInsertRemoveTest.inserted); + // CHECK_EQUAL(3, timerInsertRemoveTest.removed); + // } + // + // //************************************************************************* + // #if REALTIME_TEST + // + // #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently + // // supported + // #define RAISE_THREAD_PRIORITY SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) + // #define FIX_PROCESSOR_AFFINITY SetThreadAffinityMask(GetCurrentThread(), 1); + // #else + // #define RAISE_THREAD_PRIORITY + // #define FIX_PROCESSOR_AFFINITY + // #endif + // + // etl::intrusive_timer<3> controller; + // + // void timer_event() + // { + // const uint32_t TICK = 1U; + // uint32_t tick = TICK; + // ticks = 1U; + // + // RAISE_THREAD_PRIORITY; + // FIX_PROCESSOR_AFFINITY; + // + // while (ticks <= 1000U) + // { + // std::this_thread::sleep_for(std::chrono::milliseconds(1)); + // + // if (controller.tick(tick)) + // { + // tick = TICK; + // } + // else + // { + // tick += TICK; + // } + // + // ++ticks; + // } + // } + // + // TEST(intrusive_timer_threads) + // { + // FIX_PROCESSOR_AFFINITY; + // + // etl::timer::id::type id1 = controller.register_timer(member_callback, 400, etl::timer::mode::Single_Shot); + // etl::timer::id::type id2 = controller.register_timer(free_function_callback, 100, etl::timer::mode::Repeating); + // etl::timer::id::type id3 = controller.register_timer(free_callback2, 10, etl::timer::mode::Repeating); + // + // object.tick_list.clear(); + // free_tick_list1.clear(); + // free_tick_list2.clear(); + // + // controller.start(id1); + // controller.start(id2); + // // controller.start(id3); + // + // controller.enable(true); + // + // std::thread t1(timer_event); + // + // bool restart_1 = true; + // + // while (ticks <= 1000U) + // { + // if ((ticks > 200U) && (ticks < 500U)) + // { + // controller.stop(id3); + // } + // + // if ((ticks > 600U) && (ticks < 800U)) + // { + // controller.start(id3); + // } + // + // if ((ticks > 500U) && restart_1) + // { + // controller.start(id1); + // restart_1 = false; + // } + // + // std::this_thread::sleep_for(std::chrono::milliseconds(1)); + // } + // + // // Join the thread with the main thread + // t1.join(); + // + // CHECK_EQUAL(2U, object.tick_list.size()); + // CHECK_EQUAL(10U, free_tick_list1.size()); + // CHECK(free_tick_list2.size() < 65U); + // + // // std::vector compare1 = { 400, 900 }; + // // std::vector compare2 = { 100, 200, 300, 400, 500, 600, 700, + // // 800, 900, 1000 }; + // + // 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(), + // // min(compare1.size(), object.tick_list.size())); + // // CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), + // // min(compare2.size(), free_tick_list1.size())); + // } + // #endif + } +} // namespace diff --git a/test/test_timer_lists.cpp b/test/test_timer_lists.cpp index aa767e11..f50cc8af 100644 --- a/test/test_timer_lists.cpp +++ b/test/test_timer_lists.cpp @@ -176,11 +176,11 @@ namespace timer = active_list.next(timer); CHECK_EQUAL(active_list.end(), timer); - CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_config().get_delta()); - CHECK_EQUAL(timer3.get_config().get_period() - timer2.get_config().get_period(), timer3.get_config().get_delta()); - CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_config().get_delta()); - CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_config().get_delta()); - CHECK_EQUAL(timer5.get_config().get_period() - timer3.get_config().get_period(), timer5.get_config().get_delta()); + CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_delta()); + CHECK_EQUAL(timer3.get_config().get_period() - timer2.get_config().get_period(), timer3.get_delta()); + CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_delta()); + CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_delta()); + CHECK_EQUAL(timer5.get_config().get_period() - timer3.get_config().get_period(), timer5.get_delta()); // Remove timer3 from the list. active_list.remove(timer3.get_config().get_id()); @@ -226,10 +226,10 @@ namespace CHECK_TRUE(timer5_found); // Check the deltas are still correct. - CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_config().get_delta()); - CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_config().get_delta()); - CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_config().get_delta()); - CHECK_EQUAL(timer5.get_config().get_period() - timer2.get_config().get_period(), timer5.get_config().get_delta()); + CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_delta()); + CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_delta()); + 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.get_config().get_id()); @@ -270,10 +270,10 @@ namespace } // Check the deltas are still correct. - CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_config().get_delta()); - CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_config().get_delta()); - CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_config().get_delta()); - CHECK_EQUAL(timer5.get_config().get_period() - timer2.get_config().get_period(), timer5.get_config().get_delta()); + 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(); @@ -283,7 +283,7 @@ namespace } //************************************************************************* - TEST(test_timer_triggered_list) + TEST(test_timer_expired_list) { constexpr etl::timer_config timer1_config(1, 100, etl::timer::mode::Repeating); constexpr etl::timer_config timer2_config(2, 65, etl::timer::mode::Repeating); @@ -297,41 +297,41 @@ namespace etl::timer_node timer4{timer4_config}; etl::timer_node timer5{timer5_config}; - etl::private_timer::timer_triggered_list triggered_list; + etl::private_timer::timer_expired_list expired_list; - CHECK_TRUE(triggered_list.empty()); - CHECK_TRUE(triggered_list.first() == triggered_list.end()); + CHECK_TRUE(expired_list.empty()); + CHECK_TRUE(expired_list.first() == expired_list.end()); // Add the timers to the list. - triggered_list.insert(&timer1); - triggered_list.insert(&timer2); - triggered_list.insert(&timer3); - triggered_list.insert(&timer4); - triggered_list.insert(&timer5); + expired_list.insert(&timer1); + expired_list.insert(&timer2); + expired_list.insert(&timer3); + expired_list.insert(&timer4); + expired_list.insert(&timer5); - CHECK_FALSE(triggered_list.empty()); - CHECK_TRUE(triggered_list.first() == &timer1); + CHECK_FALSE(expired_list.empty()); + CHECK_TRUE(expired_list.first() == &timer1); // The timers should be in the order they were added. - etl::timer_node* timer = triggered_list.first(); + etl::timer_node* timer = expired_list.first(); CHECK_EQUAL(&timer1, timer); - timer = triggered_list.next(timer); + timer = expired_list.next(timer); CHECK_EQUAL(&timer2, timer); - timer = triggered_list.next(timer); + timer = expired_list.next(timer); CHECK_EQUAL(&timer3, timer); - timer = triggered_list.next(timer); + timer = expired_list.next(timer); CHECK_EQUAL(&timer4, timer); - timer = triggered_list.next(timer); + timer = expired_list.next(timer); CHECK_EQUAL(&timer5, timer); - timer = triggered_list.next(timer); - CHECK_EQUAL(triggered_list.end(), timer); + timer = expired_list.next(timer); + CHECK_EQUAL(expired_list.end(), timer); // Clear the list. - triggered_list.clear(); + expired_list.clear(); - CHECK_TRUE(triggered_list.empty()); - CHECK_TRUE(triggered_list.first() == triggered_list.end()); + CHECK_TRUE(expired_list.empty()); + CHECK_TRUE(expired_list.first() == expired_list.end()); } } } // namespace diff --git a/test/test_timer_node.cpp b/test/test_timer_node.cpp index 2b9e2043..f243eae9 100644 --- a/test/test_timer_node.cpp +++ b/test/test_timer_node.cpp @@ -39,27 +39,22 @@ namespace { constexpr etl::timer_config config1(1, 100U, etl::timer::mode::Repeating); CHECK_EQUAL(100U, config1.get_period()); - CHECK_EQUAL(100U, config1.get_delta()); CHECK_EQUAL(etl::timer::mode::Repeating, config1.get_mode()); etl::timer_config config2(2, 200U, etl::timer::mode::Single_Shot); CHECK_EQUAL(200U, config2.get_period()); - CHECK_EQUAL(200U, config2.get_delta()); CHECK_EQUAL(etl::timer::mode::Single_Shot, config2.get_mode()); etl::timer_config config3(config2); CHECK_EQUAL(config2.get_period(), config3.get_period()); - CHECK_EQUAL(config2.get_delta(), config3.get_delta()); CHECK_EQUAL(etl::timer::mode::Single_Shot, config2.get_mode()); etl::timer_config config4 = etl::timer_config(4, 500U, etl::timer::mode::Repeating); CHECK_EQUAL(500U, config4.get_period()); - CHECK_EQUAL(500U, config4.get_delta()); CHECK_EQUAL(etl::timer::mode::Repeating, config4.get_mode()); config2.clear(); CHECK_EQUAL(etl::timer::state::Inactive, config2.get_period()); - CHECK_EQUAL(etl::timer::state::Inactive, config2.get_delta()); CHECK_EQUAL(etl::timer::mode::Repeating, config2.get_mode()); } @@ -74,11 +69,9 @@ namespace const etl::timer_config& config1_ref = node1.get_config(); CHECK_EQUAL(config1.get_period(), node1.get_config().get_period()); - CHECK_EQUAL(config1.get_delta(), node1.get_config().get_delta()); CHECK_EQUAL(config1.get_mode(), node1.get_config().get_mode()); CHECK_EQUAL(node1.get_config().get_period(), node2.get_config().get_period()); - CHECK_EQUAL(node1.get_config().get_delta(), node2.get_config().get_delta()); CHECK_EQUAL(node1.get_config().get_mode(), node2.get_config().get_mode()); } } diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index e1f7d8f9..4f6032a8 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3559,6 +3559,7 @@ + @@ -10403,6 +10404,7 @@ + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 9c40a452..daa1347d 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1575,6 +1575,9 @@ ETL\Private + + ETL\Frameworks + @@ -3851,6 +3854,9 @@ Tests\Callback Timers + + Tests\Callback Timers +