From cf07fc69b5244f3af376de0153d79c13f5e3e898 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 20 Oct 2026 18:31:17 +0100 Subject: [PATCH] Modified the timer config and node classes --- include/etl/private/timer_lists.h | 518 +++++++++++++++ include/etl/timer_config.h | 588 ------------------ include/etl/timer_lists.h | 515 +++++++++++++++ include/etl/timer_node.h | 221 +++++++ ...st_timer_data.cpp => test_timer_lists.cpp} | 125 ++-- test/test_timer_node.cpp | 85 +++ test/vs2022/etl.vcxproj | 6 +- test/vs2022/etl.vcxproj.filters | 12 +- 8 files changed, 1424 insertions(+), 646 deletions(-) create mode 100644 include/etl/private/timer_lists.h delete mode 100644 include/etl/timer_config.h create mode 100644 include/etl/timer_lists.h create mode 100644 include/etl/timer_node.h rename test/{test_timer_data.cpp => test_timer_lists.cpp} (60%) create mode 100644 test/test_timer_node.cpp diff --git a/include/etl/private/timer_lists.h b/include/etl/private/timer_lists.h new file mode 100644 index 00000000..13186f91 --- /dev/null +++ b/include/etl/private/timer_lists.h @@ -0,0 +1,518 @@ +/****************************************************************************** +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.h" +#include "../timer_node.h" + +#include + +//***************************************************************************** +// 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_config.h b/include/etl/timer_config.h deleted file mode 100644 index 5ee34670..00000000 --- a/include/etl/timer_config.h +++ /dev/null @@ -1,588 +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_DATA_INCLUDED -#define ETL_TIMER_DATA_INCLUDED - -#include "platform.h" -#include "nullptr.h" -#include "timer.h" - -#include - -//***************************************************************************** -// Definitions common to intrusive timers. -//***************************************************************************** - -namespace etl -{ - //*************************************************************************** - /// Common definitions for the intrusive timer framework. - //*************************************************************************** - class timer_config - { - public: - - friend class timer_register_list; - friend class timer_active_list; - friend class timer_triggered_list; - friend class intrusive_callback_timer; - - //******************************************* - timer_config() ETL_NOEXCEPT - : period(etl::timer::state::Inactive) - , delta(etl::timer::state::Inactive) - , mode(etl::timer::mode::Repeating) - , registered_next(ETL_NULLPTR) - , active_next(ETL_NULLPTR) - , triggered_next(ETL_NULLPTR) - { - } - - //******************************************* - timer_config(uint32_t period_, etl::timer::mode::type repeating_) ETL_NOEXCEPT - : period(period_) - , delta(period_) - , mode(repeating_) - , registered_next(ETL_NULLPTR) - , active_next(ETL_NULLPTR) - , triggered_next(ETL_NULLPTR) - { - } - - //******************************************* - uint32_t get_period() const ETL_NOEXCEPT - { - return period; - } - - //******************************************* - uint32_t get_delta() const ETL_NOEXCEPT - { - return delta; - } - - //******************************************* - uint32_t get_mode() const ETL_NOEXCEPT - { - return mode; - } - - //******************************************* - void clear() ETL_NOEXCEPT - { - period = etl::timer::state::Inactive; - delta = etl::timer::state::Inactive; - mode = etl::timer::mode::Repeating; - registered_next = ETL_NULLPTR; - active_next = ETL_NULLPTR; - triggered_next = ETL_NULLPTR; - } - - private: - - uint32_t period; ///< The period of the timer. - uint32_t delta; ///< The time remaining until the timer expires. - timer_config* registered_next; ///< The next timer in the registered timer list. nullptr if the timer is not registered. - timer_config* active_next; ///< The next timer in the active timer list. nullptr if the timer is not active. - timer_config* triggered_next; ///< The next timer in the triggered timer list. nullptr if the timer is not triggered. - etl::timer::mode::type mode; ///< Whether the timer is repeating or single-shot. - }; - - //*************************************************************************** - /// A timer data structure that includes a callback delegate. - //*************************************************************************** - template - struct callback_timer_config : public timer_config - { - callback_timer_config(etl::timer::id::type id_, TCallback callback_) ETL_NOEXCEPT - : timer_config() - , callback(callback_) - { - } - - TCallback callback; ///< The callback delegate for the timer. - }; - - //************************************************************************* - /// The list of timers that are registered. - //************************************************************************* - class timer_register_list - { - public: - - //******************************* - timer_register_list() ETL_NOEXCEPT - : head(ETL_NULLPTR) - , tail(ETL_NULLPTR) - { - } - - //******************************* - bool empty() const - { - return head == ETL_NULLPTR; - } - - //******************************* - // Adds the timer in to the list. - //******************************* - void insert(etl::timer_config* new_timer) ETL_NOEXCEPT - { - if (empty()) - { - head = new_timer; - } - else if (head == new_timer) - { - - } - else - { - // Remove any existing timer. - etl::timer_config* previous = head; - - // Step through the list until we find the timer to remove, or reach the end of the list. - while ((previous->registered_next != ETL_NULLPTR) && (previous->registered_next != new_timer)) - { - previous = previous->registered_next; - } - - // If we found the timer, remove it from the list. - if (previous->registered_next == new_timer) - { - } - - tail->registered_next = new_timer; - } - - tail = new_timer; - } - - //******************************* - // 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_config* remove(etl::timer_config* timer) ETL_NOEXCEPT - { - if (timer == head) - { - head = timer->registered_next; - - if (head == ETL_NULLPTR) - { - tail = ETL_NULLPTR; - } - - timer->registered_next = ETL_NULLPTR; - - return head; - } - else - { - etl::timer_config* previous = head; - - // Step through the list until we find the timer to remove, or reach the end of the list. - while ((previous->registered_next != ETL_NULLPTR) && (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 = ETL_NULLPTR; - - return previous->registered_next; - } - else - { - // Timer not found in the list. - return ETL_NULLPTR; - } - } - } - - //******************************* - /// Returns the first timer in the list. - /// If the list is empty, returns nullptr. - //******************************* - etl::timer_config* first() const ETL_NOEXCEPT - { - return head; - } - - //******************************* - /// Returns the next timer in the list after the given timer. - /// May be nullptr if the given timer is the last in the list. - /// If the given timer is nullptr, returns nullptr. - //******************************* - etl::timer_config* next(etl::timer_config* timer) const ETL_NOEXCEPT - { - if (timer != ETL_NULLPTR) - { - return timer->registered_next; - } - else - { - return ETL_NULLPTR; - } - } - - //******************************* - /// Clear the list of entries. - //******************************* - void clear() - { - etl::timer_config* timer = head; - - while (timer != ETL_NULLPTR) - { - timer = pop_front(); - } - - head = ETL_NULLPTR; - tail = ETL_NULLPTR; - } - - 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_config* pop_front() ETL_NOEXCEPT - { - etl::timer_config* timer = head; - head = timer->registered_next; - timer->registered_next = ETL_NULLPTR; - - return head; - } - - etl::timer_config* head; - etl::timer_config* tail; - }; - - //************************************************************************* - /// The list of timers that are active. - //************************************************************************* - class timer_active_list - { - public: - - //******************************* - timer_active_list() ETL_NOEXCEPT - : head(ETL_NULLPTR) - { - } - - //******************************* - bool empty() const ETL_NOEXCEPT - { - return head == ETL_NULLPTR; - } - - //******************************* - // Inserts the timer at the correct delta position - //******************************* - void insert(etl::timer_config* new_timer) ETL_NOEXCEPT - { - if (empty()) - { - // No entries yet. - head = new_timer; - new_timer->active_next = ETL_NULLPTR; - } - else if (new_timer->delta < head->delta) - { - // Goes before the head. - head->delta -= new_timer->delta; - new_timer->active_next = head; - head = new_timer; - } - else - { - // We already have entries, and new_timer doesn't go before head. - timer_config* previous_timer = head; - timer_config* timer = head->active_next; - - // Adjust the new_timer delta to take account of the head timer's delta. - new_timer->delta -= previous_timer->delta; - - bool inserted = false; - - while (!inserted && (timer != ETL_NULLPTR)) - { - // 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->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->delta -= new_timer->delta; - - inserted = true; - } - else - { - // Adjust the new_timer delta to take account of the last timer's delta. - new_timer->delta -= timer->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 = ETL_NULLPTR; - } - } - } - - //******************************* - // 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 nullptr if the removed timer was the last in the list. - //******************************* - etl::timer_config* remove(etl::timer_config* timer) ETL_NOEXCEPT - { - // Find the timer in the list. - etl::timer_config* current = head; - etl::timer_config* last = ETL_NULLPTR; - - while (current != timer && current != ETL_NULLPTR) - { - last = current; - current = current->active_next; - } - - // Not found? - if (current == ETL_NULLPTR) - { - return ETL_NULLPTR; - } - - etl::timer_config* next = current->active_next; - - // Adjust the next delta. - if (next != ETL_NULLPTR) - { - next->delta += current->delta; - } - - // Remove it from the list. - if (last == ETL_NULLPTR) - { - 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 nullptr. - //******************************* - etl::timer_config* first() const ETL_NOEXCEPT - { - return head; - } - - //******************************* - /// Returns the next timer in the list after the given timer. - /// If the given timer is nullptr, returns nullptr. - //******************************* - etl::timer_config* next(etl::timer_config* timer) const ETL_NOEXCEPT - { - if (timer != ETL_NULLPTR) - { - return timer->active_next; - } - else - { - return ETL_NULLPTR; - } - } - - //******************************* - /// Clear the list of entries. - //******************************* - void clear() - { - etl::timer_config* timer = head; - - while (timer != ETL_NULLPTR) - { - timer = pop_front(); - } - - head = ETL_NULLPTR; - } - - 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_config* pop_front() ETL_NOEXCEPT - { - etl::timer_config* timer = head; - head = timer->active_next; - timer->active_next = ETL_NULLPTR; - - return head; - } - - etl::timer_config* head; - }; - - //************************************************************************* - /// The list of timers that are registered. - //************************************************************************* - class timer_triggered_list - { - public: - - //******************************* - timer_triggered_list() ETL_NOEXCEPT - : head(ETL_NULLPTR) - , tail(ETL_NULLPTR) - { - } - - //******************************* - bool empty() const - { - return head == ETL_NULLPTR; - } - - //******************************* - // Adds the timer in to the list. - //******************************* - void insert(etl::timer_config* new_timer) ETL_NOEXCEPT - { - new_timer->triggered_next = ETL_NULLPTR; - - 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_config* first() const ETL_NOEXCEPT - { - return head; - } - - //******************************* - /// Returns the next timer in the list after the given timer. - /// May be nullptr if the given timer is the last in the list. - /// If the given timer is nullptr, returns nullptr. - //******************************* - etl::timer_config* next(etl::timer_config* timer) const ETL_NOEXCEPT - { - if (timer != ETL_NULLPTR) - { - return timer->triggered_next; - } - else - { - return ETL_NULLPTR; - } - } - - //******************************* - /// Clear the list of entries. - //******************************* - void clear() - { - etl::timer_config* timer = head; - - while (timer != ETL_NULLPTR) - { - timer = pop_front(); - } - - head = ETL_NULLPTR; - tail = ETL_NULLPTR; - } - - 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_config* pop_front() ETL_NOEXCEPT - { - etl::timer_config* timer = head; - head = timer->triggered_next; - timer->triggered_next = ETL_NULLPTR; - - return head; - } - - etl::timer_config* head; - etl::timer_config* tail; - }; -} // namespace etl - -#endif diff --git a/include/etl/timer_lists.h b/include/etl/timer_lists.h new file mode 100644 index 00000000..412468a2 --- /dev/null +++ b/include/etl/timer_lists.h @@ -0,0 +1,515 @@ +/****************************************************************************** +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 new file mode 100644 index 00000000..dbc0899e --- /dev/null +++ b/include/etl/timer_node.h @@ -0,0 +1,221 @@ +/****************************************************************************** +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_NODE_INCLUDED +#define ETL_TIMER_NODE_INCLUDED + +#include "platform.h" +#include "nullptr.h" +#include "timer.h" + +#include + +//***************************************************************************** +// Definitions common to intrusive timers. +//***************************************************************************** + +namespace etl +{ + namespace private_timer + { + class timer_register_list; + class timer_active_list; + class timer_triggered_list; + + class intrusive_callback_timer; + } // namespace private_timer + + //*************************************************************************** + /// A timer configuration data structure. + //*************************************************************************** + class timer_config + { + public: + + friend class private_timer::timer_register_list; + friend class private_timer::timer_active_list; + friend class private_timer::timer_triggered_list; + + friend class intrusive_callback_timer; + + //******************************************* + /// Constructor. + //******************************************* + ETL_CONSTEXPR timer_config(uint32_t period_, etl::timer::mode::type repeating_) ETL_NOEXCEPT + : period(period_) + , delta(period_) + , mode(repeating_) + { + } + + //******************************************* + /// Returns the period of the timer. + //******************************************* + ETL_CONSTEXPR uint32_t get_period() const ETL_NOEXCEPT + { + 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. + //******************************************* + ETL_CONSTEXPR uint32_t get_mode() const ETL_NOEXCEPT + { + return mode; + } + + //******************************************* + /// Clears the timer configuration. + //******************************************* + ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT + { + period = etl::timer::state::Inactive; + delta = etl::timer::state::Inactive; + mode = etl::timer::mode::Repeating; + } + + 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. + }; + + //*************************************************************************** + /// A timer data structure that can be linked into the timer lists. + /// Contains a timer configuration. + //*************************************************************************** + class timer_node + { + public: + + friend class private_timer::timer_register_list; + friend class private_timer::timer_active_list; + friend class private_timer::timer_triggered_list; + + friend class intrusive_callback_timer; + + //******************************************* + /// Constructor. + //******************************************* + timer_node(const timer_config& config_) ETL_NOEXCEPT + : registered_next(unlinked()) + , active_next(unlinked()) + , triggered_next(unlinked()) + , config(config_) + { + } + + //******************************************* + /// Copy constructor. + //******************************************* + timer_node(const timer_node& node_) ETL_NOEXCEPT + : registered_next(unlinked()) + , active_next(unlinked()) + , triggered_next(unlinked()) + , config(node_.config) + { + } + + //******************************************* + /// Returns the configuration of the timer. + //******************************************* + const timer_config& get_config() const ETL_NOEXCEPT + { + return config; + } + + private: + + //******************************************* + /// Returns a nullptr to indicate that the timer is not linked into any list. + //******************************************* + static etl::timer_node* unlinked() ETL_NOEXCEPT + { + return ETL_NULLPTR; + } + + //*************************************************************************** + /// Returns a reference to the sentinel timer node used to mark the end of + /// the intrusive timer lists. + /// Implemented as a function-local static so that there is only ever one + /// instance, regardless of how many translation units include this header. + /// This avoids needing C++17 inline variables, so it remains compatible + /// with C++03 and C++11. + //*************************************************************************** + static etl::timer_node* sentinal() ETL_NOEXCEPT + { + static etl::timer_node timer_sentinel(etl::timer_config(0, 0)); + + return &timer_sentinel; + } + + //******************************************* + /// Clear the next pointers. + //******************************************* + void clear() ETL_NOEXCEPT + { + registered_next = unlinked(); + active_next = unlinked(); + triggered_next = unlinked(); + } + + 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. + }; + + ////*************************************************************************** + ///// A timer data structure that includes a callback delegate. + ////*************************************************************************** + // template + // struct callback_timer_config : public timer_node + //{ + // callback_timer_config(TCallback callback_) ETL_NOEXCEPT + // : timer_config() + // , callback(callback_) + // { + // } + + // TCallback callback; ///< The callback delegate for the timer. + //}; +} // namespace etl + +#endif diff --git a/test/test_timer_data.cpp b/test/test_timer_lists.cpp similarity index 60% rename from test/test_timer_data.cpp rename to test/test_timer_lists.cpp index 167c4a82..6e7b3c86 100644 --- a/test/test_timer_data.cpp +++ b/test/test_timer_lists.cpp @@ -28,25 +28,32 @@ SOFTWARE. #include "unit_test_framework.h" -#include "etl/timer_config.h" +#include "etl/timer_node.h" +#include "etl/private/timer_lists.h" namespace { - SUITE(test_timer_config) + SUITE(test_timer_lists) { //************************************************************************* - TEST(test_register_list) + TEST(test_timer_register_list) { - etl::timer_config timer1; - etl::timer_config timer2; - etl::timer_config timer3; - etl::timer_config timer4; - etl::timer_config timer5; + constexpr etl::timer_config timer1_config(100, etl::timer::mode::Repeating); + constexpr etl::timer_config timer2_config(65, etl::timer::mode::Repeating); + constexpr etl::timer_config timer3_config(85, etl::timer::mode::Repeating); + constexpr etl::timer_config timer4_config(130, etl::timer::mode::Repeating); + constexpr etl::timer_config timer5_config(85, etl::timer::mode::Repeating); - etl::timer_register_list register_list; + etl::timer_node timer1{timer1_config}; + etl::timer_node timer2{timer2_config}; + etl::timer_node timer3{timer3_config}; + etl::timer_node timer4{timer4_config}; + etl::timer_node timer5{timer5_config}; + + etl::private_timer::timer_register_list register_list; CHECK_TRUE(register_list.empty()); - CHECK_TRUE(register_list.first() == ETL_NULLPTR); + CHECK_TRUE(register_list.first() == register_list.end()); // Add the timers to the list. register_list.insert(&timer1); @@ -58,7 +65,7 @@ namespace CHECK_FALSE(register_list.empty()); CHECK_TRUE(register_list.first() == &timer1); - etl::timer_config* timer = register_list.first(); + etl::timer_node* timer = register_list.first(); // The timers should be in the order they were added. CHECK_EQUAL(&timer1, timer); @@ -71,7 +78,7 @@ namespace timer = register_list.next(timer); CHECK_EQUAL(&timer5, timer); timer = register_list.next(timer); - CHECK_EQUAL(ETL_NULLPTR, timer); + CHECK_EQUAL(register_list.end(), timer); // Remove timer3 from the list. register_list.remove(&timer3); @@ -85,7 +92,7 @@ namespace timer = register_list.first(); - while (timer != ETL_NULLPTR) + while (timer != register_list.end()) { if (timer == &timer1) { @@ -121,23 +128,30 @@ namespace register_list.clear(); CHECK_TRUE(register_list.empty()); - CHECK_TRUE(register_list.first() == ETL_NULLPTR); + CHECK_TRUE(register_list.first() == register_list.end()); } //************************************************************************* - TEST(test_active_list) + TEST(test_timer_active_list) { - etl::timer_config timer1(100, etl::timer::mode::Repeating); - etl::timer_config timer2(65, etl::timer::mode::Repeating); - etl::timer_config timer3(85, etl::timer::mode::Repeating); - etl::timer_config timer4(130, etl::timer::mode::Repeating); - etl::timer_config timer5(85, etl::timer::mode::Repeating); - etl::timer_config timer6(85, etl::timer::mode::Repeating); + constexpr etl::timer_config timer1_config(100, etl::timer::mode::Repeating); + constexpr etl::timer_config timer2_config(65, etl::timer::mode::Repeating); + constexpr etl::timer_config timer3_config(85, etl::timer::mode::Repeating); + constexpr etl::timer_config timer4_config(130, etl::timer::mode::Repeating); + constexpr etl::timer_config timer5_config(85, etl::timer::mode::Repeating); + constexpr etl::timer_config timer6_config(85, etl::timer::mode::Repeating); - etl::timer_active_list active_list; + etl::timer_node timer1(timer1_config); + etl::timer_node timer2(timer2_config); + etl::timer_node timer3(timer3_config); + etl::timer_node timer4(timer4_config); + etl::timer_node timer5(timer5_config); + etl::timer_node timer6(timer6_config); + + etl::private_timer::timer_active_list active_list; CHECK_TRUE(active_list.empty()); - CHECK_TRUE(active_list.first() == ETL_NULLPTR); + CHECK_TRUE(active_list.first() == active_list.end()); active_list.insert(&timer1); active_list.insert(&timer2); @@ -148,7 +162,7 @@ namespace CHECK_FALSE(active_list.empty()); CHECK_TRUE(active_list.first() == &timer2); - etl::timer_config* timer = active_list.first(); + etl::timer_node* timer = active_list.first(); // The timers should be in the order of their deltas. CHECK_EQUAL(&timer2, timer); @@ -161,14 +175,13 @@ namespace timer = active_list.next(timer); CHECK_EQUAL(&timer4, timer); timer = active_list.next(timer); - CHECK_EQUAL(ETL_NULLPTR, timer); - - CHECK_EQUAL(timer2.get_period(), timer2.get_delta()); - CHECK_EQUAL(timer3.get_period() - timer2.get_period(), timer3.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() - timer3.get_period(), timer5.get_delta()); + 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()); // Remove timer3 from the list. active_list.remove(&timer3); @@ -181,7 +194,7 @@ namespace timer = active_list.first(); - while (timer != ETL_NULLPTR) + while (timer != active_list.end()) { if (timer == &timer1) { @@ -214,10 +227,10 @@ namespace CHECK_TRUE(timer5_found); // 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()); + 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()); // Remove a timer that is not in the list. active_list.remove(&timer6); @@ -231,7 +244,7 @@ namespace timer = active_list.first(); - while (timer != ETL_NULLPTR) + while (timer != active_list.end()) { if (timer == &timer1) { @@ -258,31 +271,37 @@ namespace } // 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()); + 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()); // Clear the list. active_list.clear(); CHECK_TRUE(active_list.empty()); - CHECK_TRUE(active_list.first() == ETL_NULLPTR); + CHECK_TRUE(active_list.first() == active_list.end()); } //************************************************************************* - TEST(test_triggered_list) + TEST(test_timer_triggered_list) { - etl::timer_config timer1; - etl::timer_config timer2; - etl::timer_config timer3; - etl::timer_config timer4; - etl::timer_config timer5; + constexpr etl::timer_config timer1_config(100, etl::timer::mode::Repeating); + constexpr etl::timer_config timer2_config(65, etl::timer::mode::Repeating); + constexpr etl::timer_config timer3_config(85, etl::timer::mode::Repeating); + constexpr etl::timer_config timer4_config(130, etl::timer::mode::Repeating); + constexpr etl::timer_config timer5_config(85, etl::timer::mode::Repeating); - etl::timer_triggered_list triggered_list; + etl::timer_node timer1{timer1_config}; + etl::timer_node timer2{timer2_config}; + etl::timer_node timer3{timer3_config}; + etl::timer_node timer4{timer4_config}; + etl::timer_node timer5{timer5_config}; + + etl::private_timer::timer_triggered_list triggered_list; CHECK_TRUE(triggered_list.empty()); - CHECK_TRUE(triggered_list.first() == ETL_NULLPTR); + CHECK_TRUE(triggered_list.first() == triggered_list.end()); // Add the timers to the list. triggered_list.insert(&timer1); @@ -295,7 +314,7 @@ namespace CHECK_TRUE(triggered_list.first() == &timer1); // The timers should be in the order they were added. - etl::timer_config* timer = triggered_list.first(); + etl::timer_node* timer = triggered_list.first(); CHECK_EQUAL(&timer1, timer); timer = triggered_list.next(timer); @@ -307,13 +326,13 @@ namespace timer = triggered_list.next(timer); CHECK_EQUAL(&timer5, timer); timer = triggered_list.next(timer); - CHECK_EQUAL(ETL_NULLPTR, timer); + CHECK_EQUAL(triggered_list.end(), timer); // Clear the list. triggered_list.clear(); CHECK_TRUE(triggered_list.empty()); - CHECK_TRUE(triggered_list.first() == ETL_NULLPTR); + CHECK_TRUE(triggered_list.first() == triggered_list.end()); } } } // namespace diff --git a/test/test_timer_node.cpp b/test/test_timer_node.cpp new file mode 100644 index 00000000..d3d6d520 --- /dev/null +++ b/test/test_timer_node.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** +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/timer_node.h" + +namespace +{ + SUITE(test_timer_config_and_node) + { + //************************************************************************* + TEST(test_timer_config) + { + constexpr etl::timer_config config1(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(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(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()); + } + + //************************************************************************* + TEST(test_timer_node) + { + constexpr etl::timer_config config1(100U, etl::timer::mode::Repeating); + + etl::timer_node node1(config1); + etl::timer_node node2(node1); + + 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()); + } + } +} // namespace diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 9b217851..e1f7d8f9 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3559,7 +3559,8 @@ - + + @@ -11450,7 +11451,8 @@ - + + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 7002ecda..9c40a452 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1569,9 +1569,12 @@ UnitTest++\Header Files - + ETL\Frameworks + + ETL\Private + @@ -3842,8 +3845,11 @@ Tests\Containers - - Tests\Timer support + + Tests\Callback Timers + + + Tests\Callback Timers