Modified the timer config and node classes

This commit is contained in:
John Wellbelove 2026-10-20 18:31:17 +01:00
parent fe1bc0b029
commit cf07fc69b5
8 changed files with 1424 additions and 646 deletions

View File

@ -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 <stdint.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

View File

@ -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 <stdint.h>
//*****************************************************************************
// 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 <typename TCallback>
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

515
include/etl/timer_lists.h Normal file
View File

@ -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

221
include/etl/timer_node.h Normal file
View File

@ -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 <stdint.h>
//*****************************************************************************
// 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 <typename TCallback>
// 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

View File

@ -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

85
test/test_timer_node.cpp Normal file
View File

@ -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

View File

@ -3559,7 +3559,8 @@
<ClInclude Include="..\..\include\etl\generators\variant_pool_cpp03_generator.h" />
<ClInclude Include="..\..\include\etl\index_of_type.h" />
<ClInclude Include="..\..\include\etl\inplace_function.h" />
<ClInclude Include="..\..\include\etl\timer_config.h" />
<ClInclude Include="..\..\include\etl\private\timer_lists.h" />
<ClInclude Include="..\..\include\etl\timer_node.h" />
<ClInclude Include="..\..\include\etl\invoke.h" />
<ClInclude Include="..\..\include\etl\lcm.h" />
<ClInclude Include="..\..\include\etl\math.h" />
@ -11450,7 +11451,8 @@
<ClCompile Include="..\test_task_scheduler.cpp" />
<ClCompile Include="..\test_threshold.cpp" />
<ClCompile Include="..\test_chrono_time_point.cpp" />
<ClCompile Include="..\test_timer_data.cpp" />
<ClCompile Include="..\test_timer_lists.cpp" />
<ClCompile Include="..\test_timer_node.cpp" />
<ClCompile Include="..\test_to_arithmetic.cpp" />
<ClCompile Include="..\test_to_arithmetic_u16.cpp" />
<ClCompile Include="..\test_to_arithmetic_u32.cpp" />

View File

@ -1569,9 +1569,12 @@
<ClInclude Include="..\..\include\etl\format.h">
<Filter>UnitTest++\Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\timer_config.h">
<ClInclude Include="..\..\include\etl\timer_node.h">
<Filter>ETL\Frameworks</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\private\timer_lists.h">
<Filter>ETL\Private</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\test_string_char.cpp">
@ -3842,8 +3845,11 @@
<ClCompile Include="..\test_vector_member_function_pointer_external_buffer.cpp">
<Filter>Tests\Containers</Filter>
</ClCompile>
<ClCompile Include="..\test_timer_data.cpp">
<Filter>Tests\Timer support</Filter>
<ClCompile Include="..\test_timer_node.cpp">
<Filter>Tests\Callback Timers</Filter>
</ClCompile>
<ClCompile Include="..\test_timer_lists.cpp">
<Filter>Tests\Callback Timers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>