Work in progress

This commit is contained in:
John Wellbelove 2026-10-21 18:48:15 +01:00
parent b9456c0b62
commit 024b2dd2c5
10 changed files with 1629 additions and 1010 deletions

View File

@ -1,401 +0,0 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2017 John Wellbelove
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#ifndef ETL_INTRUSIVE_TIMER_INCLUDED
#define ETL_INTRUSIVE_TIMER_INCLUDED
#include "platform.h"
#include "algorithm.h"
#include "error_handler.h"
#include "nullptr.h"
#include "static_assert.h"
#include "timer_lists.h"
#include "timer_node.h"
#include <stdint.h>
namespace etl
{
//***************************************************************************
/// Interface for timer lock policy
//***************************************************************************
class timer_lock_policy
{
public:
virtual void lock() = 0;
virtual void unlock() = 0;
virtual bool is_locked() = 0;
};
//***************************************************************************
/// Interface for intrusive timer
//***************************************************************************
class intrusive_timer
{
public:
typedef etl::delegate<void(etl::timer_data::id::type, uint32_t delta)> timer_update_callback_type;
//*******************************************
/// Constructor.
//*******************************************
explicit intrusive_timer(timer_lock_policy& lock_policy_)
: lock_policy(lock_policy_)
{
}
//*******************************************
/// Register a timer.
//*******************************************
etl::timer::id::type register_timer(etl::timer_node& node)
{
return id;
}
//*******************************************
/// Unregister a timer.
//*******************************************
bool unregister_timer(etl::timer_node& node)
{
bool result = false;
if (id_ != etl::timer::id::No_Timer)
{
timer_data& timer = timer_array[id_];
if (timer.id != etl::timer::id::No_Timer)
{
if (timer.is_active())
{
lock_policy.lock();
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
lock_policy.unlock();
}
// Reset in-place.
new (&timer) timer_data();
--registered_timers;
result = true;
}
}
return result;
}
//*******************************************
/// Enable/disable the timer.
//*******************************************
void enable(bool state_)
{
enabled = state_;
}
//*******************************************
/// Get the enable/disable state.
//*******************************************
bool is_running() const
{
return enabled;
}
//*******************************************
/// Clears the timer of data.
//*******************************************
void clear()
{
lock_policy.lock();
active_list.clear();
lock_policy.unlock();
for (int i = 0; i < MAX_TIMERS; ++i)
{
::new (&timer_array[i]) timer_data();
}
registered_timers = 0;
}
//*******************************************
// Called by the timer service to indicate the
// amount of time that has elapsed since the last successful call to 'tick'.
// Returns true if the tick was processed,
// false if not.
//*******************************************
bool tick(uint32_t count)
{
if (enabled)
{
if (!lock_policy.is_locked())
{
// We have something to do?
bool has_active = !active_list.empty();
if (has_active)
{
while (has_active && (count >= active_list.front().delta))
{
timer_data& timer = active_list.front();
count -= timer.delta;
active_list.remove(timer.id, true);
remove_callback.call_if(timer.id);
if (timer.repeating)
{
// Reinsert the timer.
timer.delta = timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
}
if (timer.p_callback != ETL_NULLPTR)
{
if (timer.cbk_type == timer_data::C_CALLBACK)
{
// Call the C callback.
reinterpret_cast<void (*)()>(timer.p_callback)();
}
else if (timer.cbk_type == timer_data::IFUNCTION)
{
// Call the function wrapper callback.
(*reinterpret_cast<etl::ifunction<void>*>(timer.p_callback))();
}
else if (timer.cbk_type == timer_data::DELEGATE)
{
// Call the delegate callback.
(*reinterpret_cast<callback_type*>(timer.p_callback))();
}
}
has_active = !active_list.empty();
}
if (has_active)
{
// Subtract any remainder from the next due timeout.
active_list.front().delta -= count;
}
}
return true;
}
}
return false;
}
//*******************************************
/// Starts a timer.
//*******************************************
bool start(etl::timer::id::type id_, bool immediate_ = false)
{
bool result = false;
// Valid timer id?
if (id_ != etl::timer::id::No_Timer)
{
timer_data& timer = timer_array[id_];
// Registered timer?
if (timer.id != etl::timer::id::No_Timer)
{
// Has a valid period.
if (timer.period != etl::timer::state::Inactive)
{
ETL_DISABLE_TIMER_UPDATES;
if (timer.is_active())
{
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
}
timer.delta = immediate_ ? 0 : timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
lock_policy.unlock();
result = true;
}
}
}
return result;
}
//*******************************************
/// Stops a timer.
//*******************************************
bool stop(etl::timer::id::type id_)
{
bool result = false;
// Valid timer id?
if (id_ != etl::timer::id::No_Timer)
{
timer_data& timer = timer_array[id_];
// Registered timer?
if (timer.id != etl::timer::id::No_Timer)
{
if (timer.is_active())
{
ETL_DISABLE_TIMER_UPDATES;
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
lock_policy.unlock();
}
result = true;
}
}
return result;
}
//*******************************************
/// Sets a timer's period.
//*******************************************
bool set_period(etl::timer::id::type id_, uint32_t period_)
{
if (stop(id_))
{
timer_array[id_].period = period_;
return true;
}
return false;
}
//*******************************************
/// Sets a timer's mode.
//*******************************************
bool set_mode(etl::timer::id::type id_, bool repeating_)
{
if (stop(id_))
{
timer_array[id_].repeating = repeating_;
return true;
}
return false;
}
//*******************************************
/// Check if there is an active timer.
//*******************************************
bool has_active_timer() const
{
return !active_list.empty();
}
//*******************************************
/// Get the time to the next timer event.
/// Returns etl::timer::interval::No_Active_Interval if there is no active
/// timer.
//*******************************************
uint32_t time_to_next() const
{
uint32_t delta = static_cast<uint32_t>(etl::timer::interval::No_Active_Interval);
if (has_active_timer())
{
delta = active_list.front().delta;
}
return delta;
}
//*******************************************
/// Checks if a timer is currently active.
/// Returns <b>true</b> if the timer is active, otherwise <b>false</b>.
//*******************************************
bool is_active(etl::timer::id::type id_) const
{
//// Valid timer id?
// if (is_valid_timer_id(id_))
//{
// if (has_active_timer())
// {
// const timer_data& timer = timer_array[id_];
// // Registered timer?
// if (timer.id != etl::timer::id::No_Timer)
// {
// return timer.is_active();
// }
// }
//}
return false;
}
////*******************************************
///// Set a callback when a timer is inserted on list
////*******************************************
// void set_insert_callback(event_callback_type insert_)
//{
// insert_callback = insert_;
// }
////*******************************************
///// Set a callback when a timer is removed from list
////*******************************************
// void set_remove_callback(event_callback_type remove_)
//{
// remove_callback = remove_;
// }
//*******************************************
void clear_insert_callback()
{
insert_callback.clear();
}
//*******************************************
void clear_remove_callback()
{
remove_callback.clear();
}
private:
intrusive_timer(const intrusive_timer&) ETL_DELETE;
intrusive_timer& operator=(const intrusive_timer&) ETL_DELETE;
etl::timer_lock_policy& lock_policy;
etl::private_timer::timer_register_list register_list;
etl::private_timer::timer_active_list active_list;
etl::private_timer::timer_triggered_list triggered_list;
};
} // namespace etl
#endif

View File

@ -0,0 +1,452 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2017 John Wellbelove
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#ifndef ETL_INTRUSIVE_TIMER_INCLUDED
#define ETL_INTRUSIVE_TIMER_INCLUDED
#include "platform.h"
#include "algorithm.h"
#include "delegate.h"
#include "error_handler.h"
#include "nullptr.h"
#include "static_assert.h"
#include "timer_node.h"
#include "private/timer_lists.h"
#include <stdint.h>
namespace etl
{
//***************************************************************************
/// Interface for timer lock policy
//***************************************************************************
class timer_lock_policy
{
public:
virtual void lock() ETL_NOEXCEPT = 0;
virtual void unlock() ETL_NOEXCEPT = 0;
virtual bool is_locked() ETL_NOEXCEPT = 0;
};
//***************************************************************************
/// Interface for intrusive timer
//***************************************************************************
class intrusive_timer
{
public:
typedef etl::delegate<void(etl::timer::id::type, uint32_t delta)> timer_update_callback_type;
//*******************************************
/// Constructor.
//*******************************************
explicit intrusive_timer(timer_lock_policy& lock_policy_) ETL_NOEXCEPT
: lock_policy(lock_policy_)
, register_list()
, active_list()
, expired_list()
, enabled(false)
, timer_count(0U)
{
}
//*******************************************
/// Register a timer.
/// It will not be started until the start() method is called.
//*******************************************
bool register_timer(etl::timer_node& node) ETL_NOEXCEPT
{
etl::timer::id::type id = node.get_id();
bool result = false;
if (id != etl::timer::id::No_Timer)
{
lock_policy.lock();
// Remove any existing timer with the same id.
active_list.remove(id);
register_list.remove(id);
// Register the new timer.
register_list.insert(&node);
lock_policy.unlock();
result = true;
++timer_count;
}
return result;
}
//*******************************************
/// Unregister a timer.
/// By id.
//*******************************************
bool unregister_timer(etl::timer::id::type id) ETL_NOEXCEPT
{
bool result = false;
if (id != etl::timer::id::No_Timer)
{
lock_policy.lock();
active_list.remove(id);
register_list.remove(id);
lock_policy.unlock();
result = true;
--timer_count;
}
return result;
}
//*******************************************
/// Unregister a timer.
/// By node reference.
//*******************************************
bool unregister_timer(etl::timer_node& node) ETL_NOEXCEPT
{
etl::timer::id::type id = node.get_id();
return unregister_timer(id);
}
//*******************************************
/// Enable/disable the timer.
//*******************************************
void enable(bool state_) ETL_NOEXCEPT
{
enabled = state_;
}
//*******************************************
/// Get the enable/disable state.
//*******************************************
bool is_running() const ETL_NOEXCEPT
{
return enabled;
}
//*******************************************
/// Clears the timer of data.
//*******************************************
void clear() ETL_NOEXCEPT
{
lock_policy.lock();
active_list.clear();
register_list.clear();
lock_policy.unlock();
}
//*******************************************
// Called by the timer service to indicate the
// amount of time that has elapsed since the last successful call to 'tick'.
// Returns true if the tick was processed,
// false if not.
//*******************************************
bool tick(uint32_t count)
{
if (enabled)
{
if (!lock_policy.is_locked())
{
lock_policy.lock();
// We have something to do?
bool has_active = !active_list.empty();
if (has_active)
{
// Process timers that have expired.
etl::timer_node* timer = active_list.first();
while (has_active && (count >= timer->delta))
{
count -= timer->delta;
active_list.remove(timer->get_id());
// If the timer is repeating then add it back to the active list.
if (timer->get_mode() == etl::timer::mode::Repeating)
{
// Reinsert the timer.
timer->delta = timer->get_period();
active_list.insert(timer);
}
expired_list.insert(timer);
timer = active_list.first();
has_active = !active_list.empty();
}
if (has_active)
{
// Subtract any remainder from the next due timeout.
active_list.first()->delta -= count;
}
lock_policy.unlock();
// Invoke all of the expired timers.
invoke_expired_timers();
}
return true;
}
}
return false;
}
//*******************************************
/// Starts a timer.
//*******************************************
bool start(etl::timer::id::type id_, bool immediate_ = false) ETL_NOEXCEPT
{
bool is_started = false;
lock_policy.lock();
etl::timer_node* timer = find_timer(register_list, id_);
if (timer != register_list.end())
{
active_list.insert(timer);
is_started = true;
}
lock_policy.unlock();
if (is_started && immediate_)
{
// Trigger the timer immediately.
timer->invoke(timer->get_config());
}
return is_started;
}
//*******************************************
/// Stops a timer.
//*******************************************
bool stop(etl::timer::id::type id_) ETL_NOEXCEPT
{
bool is_stopped = false;
lock_policy.lock();
is_stopped = (stop_impl(id_) != active_list.end());
lock_policy.unlock();
return is_stopped;
}
//*******************************************
/// Sets a timer's period.
//*******************************************
bool set_period(etl::timer::id::type id_, uint32_t period_) ETL_NOEXCEPT
{
bool result = false;
lock_policy.lock();
stop_impl(id_);
etl::timer_node* timer = find_timer(register_list, id_);
if (timer != active_list.end())
{
timer->set_period(period_);
result = true;
}
lock_policy.unlock();
return result;
}
//*******************************************
/// Sets a timer's mode.
//*******************************************
bool set_mode(etl::timer::id::type id_, bool mode_) ETL_NOEXCEPT
{
bool result = false;
lock_policy.lock();
stop_impl(id_);
etl::timer_node* timer = find_timer(register_list, id_);
if (timer != active_list.end())
{
timer->set_mode(mode_);
result = true;
}
lock_policy.unlock();
return result;
}
//*******************************************
/// Check if there is an active timer.
//*******************************************
bool has_active_timer() const ETL_NOEXCEPT
{
lock_policy.lock();
bool has_active = !active_list.empty();
lock_policy.unlock();
return has_active;
}
//*******************************************
/// Get the time to the next timer event.
/// Returns etl::timer::interval::No_Active_Interval if there is no active
/// timer.
//*******************************************
uint32_t time_to_next() const ETL_NOEXCEPT
{
uint32_t delta = static_cast<uint32_t>(etl::timer::interval::No_Active_Interval);
lock_policy.lock();
if (!active_list.empty())
{
delta = active_list.first()->get_delta();
}
lock_policy.unlock();
return delta;
}
//*******************************************
/// Checks if a timer is currently active.
/// Returns <b>true</b> if the timer is active, otherwise <b>false</b>.
//*******************************************
bool is_active(etl::timer::id::type id_) const ETL_NOEXCEPT
{
if (id_ != etl::timer::id::No_Timer)
{
lock_policy.lock();
bool is_active = (find_timer(active_list, id_) != active_list.end());
lock_policy.unlock();
return is_active;
}
return false;
}
//*******************************************
/// Returns the number of registered timers.
//*******************************************
size_t number_of_registered_timers() const ETL_NOEXCEPT
{
return timer_count;
}
private:
//*******************************************
/// Stops a timer without locking.
/// Returns the timer node if it was found, otherwise returns end().
//*******************************************
etl::timer_node* stop_impl(etl::timer::id::type id_) ETL_NOEXCEPT
{
return active_list.remove(id_);
}
//*******************************************
/// Finds a timer in a list by its id.
//*******************************************
template <typename TList>
etl::timer_node* find_timer(TList& timer_list, etl::timer::id::type id_) const ETL_NOEXCEPT
{
etl::timer_node* result = timer_list.end();
if (id_ != etl::timer::id::No_Timer)
{
timer_node* timer = timer_list.first();
while ((timer != timer_list.end()) && (timer->get_config().get_id() != id_))
{
timer = timer_list.next(timer);
}
result = timer;
}
return result;
}
//*******************************************
/// Invokes the callbacks for all expired timers.
//*******************************************
void invoke_expired_timers()
{
etl::timer_node* timer = expired_list.first();
while (timer != expired_list.end())
{
const etl::timer_config& config = timer->get_config();
timer->invoke(config);
timer = expired_list.next(timer);
}
expired_list.clear();
}
intrusive_timer(const intrusive_timer&) ETL_DELETE;
intrusive_timer& operator=(const intrusive_timer&) ETL_DELETE;
etl::timer_lock_policy& lock_policy;
etl::private_timer::timer_register_list register_list;
etl::private_timer::timer_active_list active_list;
etl::private_timer::timer_expired_list expired_list;
bool enabled;
size_t timer_count;
};
} // namespace etl
#endif

View File

@ -37,7 +37,7 @@ SOFTWARE.
#include <stdint.h>
//*****************************************************************************
// Definitions common to intrusive timers.
// Definitions used by intrusive_timer.
//*****************************************************************************
namespace etl
@ -105,7 +105,7 @@ namespace etl
etl::timer_node* current = head;
etl::timer_node* last = end();
while ((current->config.id != id) && (current != end()))
while ((current->get_id() != id) && (current != end()))
{
last = current;
current = current->registered_next;
@ -132,8 +132,7 @@ namespace etl
// Clear the next pointer of the removed timer.
current->registered_next = end();
return next;
return current;
}
//*******************************
@ -229,10 +228,10 @@ namespace etl
head = new_timer;
new_timer->active_next = end();
}
else if (new_timer->config.delta < head->config.delta)
else if (new_timer->delta < head->delta)
{
// Goes before the head.
head->config.delta -= new_timer->config.delta;
head->delta -= new_timer->delta;
new_timer->active_next = head;
head = new_timer;
}
@ -243,7 +242,7 @@ namespace etl
timer_node* timer = head->active_next;
// Adjust the new_timer delta to take account of the head timer's delta.
new_timer->config.delta -= previous_timer->config.delta;
new_timer->delta -= previous_timer->delta;
bool inserted = false;
@ -251,21 +250,21 @@ namespace etl
{
// Find the correct place to insert.
// The list is sorted by delta, so we need to find the first timer with a delta greater than the new timer's delta.
if (new_timer->config.delta < timer->config.delta)
if (new_timer->delta < timer->delta)
{
// Insert after the previous timer.
previous_timer->active_next = new_timer;
new_timer->active_next = timer;
// Adjust the next delta for the new timer's delta.
timer->config.delta -= new_timer->config.delta;
timer->delta -= new_timer->delta;
inserted = true;
}
else
{
// Adjust the new_timer delta to take account of the last timer's delta.
new_timer->config.delta -= timer->config.delta;
new_timer->delta -= timer->delta;
}
// Move to the next timer in the list.
@ -299,7 +298,7 @@ namespace etl
etl::timer_node* current = head;
etl::timer_node* last = end();
while ((current->config.id != id) && (current != end()))
while ((current->get_id() != id) && (current != end()))
{
last = current;
current = current->active_next;
@ -316,7 +315,7 @@ namespace etl
// Adjust the next delta.
if (next != end())
{
next->config.delta += current->config.delta;
next->delta += current->delta;
}
// Remove it from the list.
@ -332,7 +331,7 @@ namespace etl
// Clear the next pointer of the removed timer.
current->active_next = end();
return next;
return current;
}
//*******************************
@ -393,14 +392,14 @@ namespace etl
//*************************************************************************
/// The list of timers that are registered.
//*************************************************************************
class timer_triggered_list
class timer_expired_list
{
public:
//*******************************
/// Constructor.
//*******************************
timer_triggered_list() ETL_NOEXCEPT
timer_expired_list() ETL_NOEXCEPT
: head(end())
, tail(end())
{
@ -419,7 +418,7 @@ namespace etl
//*******************************
void insert(etl::timer_node* new_timer) ETL_NOEXCEPT
{
new_timer->triggered_next = end();
new_timer->expired_next = end();
if (empty())
{
@ -427,7 +426,7 @@ namespace etl
}
else
{
tail->triggered_next = new_timer;
tail->expired_next = new_timer;
}
tail = new_timer;
@ -459,7 +458,7 @@ namespace etl
{
if (timer != end())
{
return timer->triggered_next;
return timer->expired_next;
}
else
{
@ -485,9 +484,9 @@ namespace etl
while (head != end())
{
head = timer->triggered_next;
timer->triggered_next = etl::timer_node::unlinked();
timer = head;
head = timer->expired_next;
timer->expired_next = timer->unlinked();
timer = head;
}
tail = end();

View File

@ -1,515 +0,0 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 John Wellbelove
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#ifndef ETL_TIMER_LISTS_INCLUDED
#define ETL_TIMER_LISTS_INCLUDED
#include "../platform.h"
#include "../nullptr.h"
#include "../timer_node.h"
//*****************************************************************************
// Definitions common to intrusive timers.
//*****************************************************************************
namespace etl
{
namespace private_timer
{
//*************************************************************************
/// The list of timers that are registered.
/// The list does not do any checking of parameters as it is assumed that the timer service has already done this.
//*************************************************************************
class timer_register_list
{
public:
//*******************************
timer_register_list() ETL_NOEXCEPT
: head(end())
, tail(end())
{
}
//*******************************
bool empty() const
{
return head == end();
}
//*******************************
// Adds the timer in to the list.
//*******************************
void insert(etl::timer_node* new_timer) ETL_NOEXCEPT
{
if (empty())
{
head = new_timer;
}
else
{
tail->registered_next = new_timer;
}
tail = new_timer;
new_timer->registered_next = end();
}
//*******************************
// Remove the timer from the list.
// Returns the next timer in the list after the removed timer, or nullptr if the removed timer was the last in the list.
//*******************************
etl::timer_node* remove(etl::timer_node* timer) ETL_NOEXCEPT
{
if (timer == head)
{
head = timer->registered_next;
if (head == end())
{
tail = end();
}
timer->registered_next = ETL_NULLPTR;
return head;
}
else
{
etl::timer_node* previous = head;
// Step through the list until we find the timer to remove, or reach the end of the list.
while ((previous->registered_next != end()) && (previous->registered_next != timer))
{
previous = previous->registered_next;
}
// If we found the timer, remove it from the list.
if (previous->registered_next == timer)
{
previous->registered_next = timer->registered_next;
if (timer == tail)
{
tail = previous;
}
timer->registered_next = end();
return previous->registered_next;
}
else
{
// Timer not found in the list.
return end();
}
}
}
//*******************************
/// Returns the first timer in the list.
/// If the list is empty, returns end().
//*******************************
etl::timer_node* first() const ETL_NOEXCEPT
{
return head;
}
//*******************************
/// Returns the next timer in the list after the given timer.
/// May be end() if the given timer is the last in the list.
/// If the given timer is nullptr, returns end().
//*******************************
etl::timer_node* next(etl::timer_node* timer) const ETL_NOEXCEPT
{
if (timer != end())
{
return timer->registered_next;
}
else
{
return end();
}
}
//*******************************
/// Returns the end of the list.
/// Always returns &etl::timer_node::sentinal().
//*******************************
etl::timer_node* const end() const ETL_NOEXCEPT
{
return etl::timer_node::sentinal();
}
//*******************************
/// Clear the list of entries.
//*******************************
void clear()
{
etl::timer_node* timer = head;
while (timer != end())
{
timer = pop_front();
}
head = end();
tail = end();
}
private:
//*******************************
// Removes the timer at the front of the list.
// Returns the new head of the list, which may be nullptr if the list is now empty.
//*******************************
etl::timer_node* pop_front() ETL_NOEXCEPT
{
etl::timer_node* timer = head;
head = timer->registered_next;
timer->registered_next = timer->unlinked();
return head;
}
etl::timer_node* head;
etl::timer_node* tail;
};
//*************************************************************************
/// The list of timers that are active.
/// The list does not do any checking of parameters as it is assumed that the timer service has already done this.
//*************************************************************************
class timer_active_list
{
public:
//*******************************
timer_active_list() ETL_NOEXCEPT
: head(end())
{
}
//*******************************
bool empty() const ETL_NOEXCEPT
{
return head == end();
}
//*******************************
// Inserts the timer at the correct delta position
//*******************************
void insert(etl::timer_node* new_timer) ETL_NOEXCEPT
{
if (empty())
{
// No entries yet.
head = new_timer;
new_timer->active_next = end();
}
else if (new_timer->config.delta < head->config.delta)
{
// Goes before the head.
head->config.delta -= new_timer->config.delta;
new_timer->active_next = head;
head = new_timer;
}
else
{
// We already have entries, and new_timer doesn't go before head.
timer_node* previous_timer = head;
timer_node* timer = head->active_next;
// Adjust the new_timer delta to take account of the head timer's delta.
new_timer->config.delta -= previous_timer->config.delta;
bool inserted = false;
while (!inserted && (timer != end()))
{
// Find the correct place to insert.
// The list is sorted by delta, so we need to find the first timer with a delta greater than the new timer's delta.
if (new_timer->config.delta < timer->config.delta)
{
// Insert after the previous timer.
previous_timer->active_next = new_timer;
new_timer->active_next = timer;
// Adjust the next delta for the new timer's delta.
timer->config.delta -= new_timer->config.delta;
inserted = true;
}
else
{
// Adjust the new_timer delta to take account of the last timer's delta.
new_timer->config.delta -= timer->config.delta;
}
// Move to the next timer in the list.
previous_timer = timer;
timer = timer->active_next;
}
if (!inserted)
{
// Reached the end so attach to the tail.
previous_timer->active_next = new_timer;
new_timer->active_next = end();
}
}
}
//*******************************
/// Removes the selected timer from the list.
/// No action is taken if the timer is not found.
/// returns the next timer in the list after the removed timer, or end() if the removed timer was the last in the list.
/// The list does not do any checking of parameters as it is assumed that the timer service has already done this.
//*******************************
etl::timer_node* remove(etl::timer_node* timer) ETL_NOEXCEPT
{
// Find the timer in the list.
etl::timer_node* current = head;
etl::timer_node* last = end();
while (current != timer && current != end())
{
last = current;
current = current->active_next;
}
// Not found?
if (current == end())
{
return end();
}
etl::timer_node* next = current->active_next;
// Adjust the next delta.
if (next != end())
{
next->config.delta += current->config.delta;
}
// Remove it from the list.
if (last == end())
{
head = next;
}
else
{
last->active_next = next;
}
// Clear the next pointer of the removed timer.
current->active_next = ETL_NULLPTR;
return next;
}
//*******************************
/// Returns the first timer in the list.
/// If the list is empty, returns end().
//*******************************
etl::timer_node* first() const ETL_NOEXCEPT
{
return head;
}
//*******************************
/// Returns the next timer in the list after the given timer.
/// May be end() if the given timer is the last in the list.
/// If the given timer is nullptr, returns end().
//*******************************
etl::timer_node* next(etl::timer_node* timer) const ETL_NOEXCEPT
{
if (timer != end())
{
return timer->active_next;
}
else
{
return end();
}
}
//*******************************
/// Returns the end of the list.
/// Always returns &etl::timer_node::sentinal().
//*******************************
etl::timer_node* const end() const ETL_NOEXCEPT
{
return etl::timer_node::sentinal();
}
//*******************************
/// Clear the list of entries.
//*******************************
void clear()
{
etl::timer_node* timer = head;
while (timer != end())
{
timer = pop_front();
}
head = end();
}
private:
//*******************************
// Removes the timer at the front of the list.
// Returns the new head of the list, which may be nullptr if the list is now empty.
//*******************************
etl::timer_node* pop_front() ETL_NOEXCEPT
{
etl::timer_node* timer = head;
head = timer->active_next;
timer->active_next = timer->unlinked();
return head;
}
etl::timer_node* head;
};
//*************************************************************************
/// The list of timers that are registered.
//*************************************************************************
class timer_triggered_list
{
public:
//*******************************
timer_triggered_list() ETL_NOEXCEPT
: head(end())
, tail(end())
{
}
//*******************************
bool empty() const
{
return head == end();
}
//*******************************
// Adds the timer in to the list.
//*******************************
void insert(etl::timer_node* new_timer) ETL_NOEXCEPT
{
new_timer->triggered_next = end();
if (empty())
{
head = new_timer;
}
else
{
tail->triggered_next = new_timer;
}
tail = new_timer;
}
//*******************************
/// Returns the first timer in the list.
/// If the list is empty, returns nullptr.
//*******************************
etl::timer_node* first() const ETL_NOEXCEPT
{
return head;
}
//*******************************
/// Returns the next timer in the list after the given timer.
/// May be end() if the given timer is the last in the list.
/// If the given timer is nullptr, returns end().
//*******************************
etl::timer_node* next(etl::timer_node* timer) const ETL_NOEXCEPT
{
if (timer != end())
{
return timer->triggered_next;
}
else
{
return end();
}
}
//*******************************
/// Returns the end of the list.
/// Always returns &etl::timer_node::sentinal().
//*******************************
etl::timer_node* const end() const ETL_NOEXCEPT
{
return etl::timer_node::sentinal();
}
//*******************************
/// Clear the list of entries.
//*******************************
void clear()
{
etl::timer_node* timer = head;
while (timer != end())
{
timer = pop_front();
}
head = end();
tail = end();
}
private:
//*******************************
// Removes the timer at the front of the list.
// Returns the new head of the list, which may be nullptr if the list is now empty.
//*******************************
etl::timer_node* pop_front() ETL_NOEXCEPT
{
etl::timer_node* timer = head;
head = timer->triggered_next;
timer->triggered_next = timer->unlinked();
return head;
}
etl::timer_node* head;
etl::timer_node* tail;
};
} // namespace private_timer
} // namespace etl
#endif

View File

@ -46,11 +46,12 @@ namespace etl
{
class timer_register_list;
class timer_active_list;
class timer_triggered_list;
class intrusive_callback_timer;
class timer_expired_list;
} // namespace private_timer
class timer_node;
class intrusive_timer;
//***************************************************************************
/// A timer configuration data structure.
//***************************************************************************
@ -60,16 +61,16 @@ namespace etl
friend class private_timer::timer_register_list;
friend class private_timer::timer_active_list;
friend class private_timer::timer_triggered_list;
friend class private_timer::timer_expired_list;
friend class intrusive_callback_timer;
friend class timer_node;
friend class intrusive_timer;
//*******************************************
/// Constructor.
//*******************************************
ETL_CONSTEXPR timer_config(etl::timer::id::type id_, uint32_t period_, etl::timer::mode::type repeating_) ETL_NOEXCEPT
: period(period_)
, delta(period_)
, mode(repeating_)
, id(id_)
{
@ -83,14 +84,6 @@ namespace etl
return period;
}
//*******************************************
/// Returns the time remaining until the timer expires.
//*******************************************
ETL_CONSTEXPR uint32_t get_delta() const ETL_NOEXCEPT
{
return delta;
}
//*******************************************
/// Returns the mode of the timer.
//*******************************************
@ -113,7 +106,6 @@ namespace etl
ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT
{
period = etl::timer::state::Inactive;
delta = etl::timer::state::Inactive;
mode = etl::timer::mode::Repeating;
id = etl::timer::id::No_Timer;
}
@ -121,7 +113,6 @@ namespace etl
private:
uint32_t period; ///< The period of the timer.
uint32_t delta; ///< The time remaining until the timer expires.
etl::timer::mode::type mode; ///< Whether the timer is repeating or single-shot.
etl::timer::id::type id; ///< The id of the timer.
};
@ -138,9 +129,9 @@ namespace etl
friend class private_timer::timer_register_list;
friend class private_timer::timer_active_list;
friend class private_timer::timer_triggered_list;
friend class private_timer::timer_expired_list;
friend class intrusive_callback_timer;
friend class intrusive_timer;
//*******************************************
/// Constructor.
@ -148,8 +139,9 @@ namespace etl
timer_node(const timer_config& config_) ETL_NOEXCEPT
: registered_next(unlinked())
, active_next(unlinked())
, triggered_next(unlinked())
, expired_next(unlinked())
, config(config_)
, delta(config_.get_period())
{
}
@ -159,8 +151,9 @@ namespace etl
timer_node(const timer_node& node_) ETL_NOEXCEPT
: registered_next(unlinked())
, active_next(unlinked())
, triggered_next(unlinked())
, expired_next(unlinked())
, config(node_.config)
, delta(node_.config.get_period())
{
}
@ -172,8 +165,56 @@ namespace etl
return config;
}
//*******************************************
/// Returns the delta of the timer.
//*******************************************
ETL_CONSTEXPR uint32_t get_delta() const ETL_NOEXCEPT
{
return delta;
}
//*******************************************
/// Returns the period of the timer.
//*******************************************
ETL_CONSTEXPR uint32_t get_period() const ETL_NOEXCEPT
{
return config.get_period();
}
//*******************************************
/// Returns the mode of the timer.
//*******************************************
ETL_CONSTEXPR uint32_t get_mode() const ETL_NOEXCEPT
{
return config.get_mode();
}
//*******************************************
/// Returns the id of the timer.
//*******************************************
ETL_CONSTEXPR etl::timer::id::type get_id() const ETL_NOEXCEPT
{
return config.get_id();
}
private:
//*******************************************
/// Sets the period of the timer.
//*******************************************
void set_period(uint32_t period_) ETL_NOEXCEPT
{
config.period = period_;
}
//*******************************************
/// Sets the mode of the timer.
//*******************************************
void set_mode(etl::timer::mode::type mode_) ETL_NOEXCEPT
{
config.mode = mode_;
}
//*******************************************
/// Returns a nullptr to indicate that the timer is not linked into any list.
//*******************************************
@ -204,24 +245,25 @@ namespace etl
{
registered_next = unlinked();
active_next = unlinked();
triggered_next = unlinked();
expired_next = unlinked();
delta = config.get_period();
}
//*******************************************
/// Invokes the timer's callback.
//*******************************************
virtual void invoke()
virtual void invoke(const etl::timer_config& /*config*/)
{
// Default implementation does nothing.
}
timer_node& operator=(const timer_node&) ETL_DELETE;
timer_node* registered_next; ///< The next timer in the registered timer list. nullptr if the timer is not registered.
timer_node* active_next; ///< The next timer in the active timer list. nullptr if the timer is not active.
timer_node* triggered_next; ///< The next timer in the triggered timer list. nullptr if the timer is not triggered.
timer_config config; ///< The configuration of the timer.
timer_node* registered_next; ///< The next timer in the registered timer list. nullptr if the timer is not registered.
timer_node* active_next; ///< The next timer in the active timer list. nullptr if the timer is not active.
timer_node* expired_next; ///< The next timer in the expired timer list. nullptr if the timer is not expired.
timer_config config; ///< The configuration of the timer.
uint32_t delta; ///< The time remaining until the timer expires.
};
//***************************************************************************
@ -242,9 +284,9 @@ namespace etl
//*******************************************
/// Invokes the timer's callback.
//*******************************************
virtual void invoke() ETL_OVERRIDE
virtual void invoke(const etl::timer_config& config) ETL_OVERRIDE
{
callback();
callback(config);
}
TCallback callback; ///< The callback delegate for the timer.
@ -259,18 +301,21 @@ namespace etl
//*******************************************
/// Constructor.
//*******************************************
message_timer_node(etl::imessage_router& router_, TMessage message) ETL_NOEXCEPT
message_timer_node(etl::imessage_router& router_, TMessage message_) ETL_NOEXCEPT
: timer_node()
, router(&router_)
, message(message_)
{
}
//*******************************************
/// Invokes the timer's callback.
//*******************************************
virtual void invoke() ETL_OVERRIDE
virtual void invoke(const etl::timer_config& config) ETL_OVERRIDE
{
callback();
message.set_timer_config(config);
router->receive(message);
}
etl::imessage_router* router; ///< The message router to send the message to.

File diff suppressed because it is too large Load Diff

View File

@ -176,11 +176,11 @@ namespace
timer = active_list.next(timer);
CHECK_EQUAL(active_list.end(), timer);
CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_config().get_delta());
CHECK_EQUAL(timer3.get_config().get_period() - timer2.get_config().get_period(), timer3.get_config().get_delta());
CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_config().get_delta());
CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_config().get_delta());
CHECK_EQUAL(timer5.get_config().get_period() - timer3.get_config().get_period(), timer5.get_config().get_delta());
CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_delta());
CHECK_EQUAL(timer3.get_config().get_period() - timer2.get_config().get_period(), timer3.get_delta());
CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_config().get_period() - timer3.get_config().get_period(), timer5.get_delta());
// Remove timer3 from the list.
active_list.remove(timer3.get_config().get_id());
@ -226,10 +226,10 @@ namespace
CHECK_TRUE(timer5_found);
// Check the deltas are still correct.
CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_config().get_delta());
CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_config().get_delta());
CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_config().get_delta());
CHECK_EQUAL(timer5.get_config().get_period() - timer2.get_config().get_period(), timer5.get_config().get_delta());
CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_delta());
CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_config().get_period() - timer2.get_config().get_period(), timer5.get_delta());
// Remove a timer that is not in the list.
active_list.remove(timer6.get_config().get_id());
@ -270,10 +270,10 @@ namespace
}
// Check the deltas are still correct.
CHECK_EQUAL(timer2.get_config().get_period(), timer2.get_config().get_delta());
CHECK_EQUAL(timer1.get_config().get_period() - timer3.get_config().get_period(), timer1.get_config().get_delta());
CHECK_EQUAL(timer4.get_config().get_period() - timer1.get_config().get_period(), timer4.get_config().get_delta());
CHECK_EQUAL(timer5.get_config().get_period() - timer2.get_config().get_period(), timer5.get_config().get_delta());
CHECK_EQUAL(timer2.get_period(), timer2.get_delta());
CHECK_EQUAL(timer1.get_period() - timer3.get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_period() - timer1.get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_period() - timer2.get_period(), timer5.get_delta());
// Clear the list.
active_list.clear();
@ -283,7 +283,7 @@ namespace
}
//*************************************************************************
TEST(test_timer_triggered_list)
TEST(test_timer_expired_list)
{
constexpr etl::timer_config timer1_config(1, 100, etl::timer::mode::Repeating);
constexpr etl::timer_config timer2_config(2, 65, etl::timer::mode::Repeating);
@ -297,41 +297,41 @@ namespace
etl::timer_node timer4{timer4_config};
etl::timer_node timer5{timer5_config};
etl::private_timer::timer_triggered_list triggered_list;
etl::private_timer::timer_expired_list expired_list;
CHECK_TRUE(triggered_list.empty());
CHECK_TRUE(triggered_list.first() == triggered_list.end());
CHECK_TRUE(expired_list.empty());
CHECK_TRUE(expired_list.first() == expired_list.end());
// Add the timers to the list.
triggered_list.insert(&timer1);
triggered_list.insert(&timer2);
triggered_list.insert(&timer3);
triggered_list.insert(&timer4);
triggered_list.insert(&timer5);
expired_list.insert(&timer1);
expired_list.insert(&timer2);
expired_list.insert(&timer3);
expired_list.insert(&timer4);
expired_list.insert(&timer5);
CHECK_FALSE(triggered_list.empty());
CHECK_TRUE(triggered_list.first() == &timer1);
CHECK_FALSE(expired_list.empty());
CHECK_TRUE(expired_list.first() == &timer1);
// The timers should be in the order they were added.
etl::timer_node* timer = triggered_list.first();
etl::timer_node* timer = expired_list.first();
CHECK_EQUAL(&timer1, timer);
timer = triggered_list.next(timer);
timer = expired_list.next(timer);
CHECK_EQUAL(&timer2, timer);
timer = triggered_list.next(timer);
timer = expired_list.next(timer);
CHECK_EQUAL(&timer3, timer);
timer = triggered_list.next(timer);
timer = expired_list.next(timer);
CHECK_EQUAL(&timer4, timer);
timer = triggered_list.next(timer);
timer = expired_list.next(timer);
CHECK_EQUAL(&timer5, timer);
timer = triggered_list.next(timer);
CHECK_EQUAL(triggered_list.end(), timer);
timer = expired_list.next(timer);
CHECK_EQUAL(expired_list.end(), timer);
// Clear the list.
triggered_list.clear();
expired_list.clear();
CHECK_TRUE(triggered_list.empty());
CHECK_TRUE(triggered_list.first() == triggered_list.end());
CHECK_TRUE(expired_list.empty());
CHECK_TRUE(expired_list.first() == expired_list.end());
}
}
} // namespace

View File

@ -39,27 +39,22 @@ namespace
{
constexpr etl::timer_config config1(1, 100U, etl::timer::mode::Repeating);
CHECK_EQUAL(100U, config1.get_period());
CHECK_EQUAL(100U, config1.get_delta());
CHECK_EQUAL(etl::timer::mode::Repeating, config1.get_mode());
etl::timer_config config2(2, 200U, etl::timer::mode::Single_Shot);
CHECK_EQUAL(200U, config2.get_period());
CHECK_EQUAL(200U, config2.get_delta());
CHECK_EQUAL(etl::timer::mode::Single_Shot, config2.get_mode());
etl::timer_config config3(config2);
CHECK_EQUAL(config2.get_period(), config3.get_period());
CHECK_EQUAL(config2.get_delta(), config3.get_delta());
CHECK_EQUAL(etl::timer::mode::Single_Shot, config2.get_mode());
etl::timer_config config4 = etl::timer_config(4, 500U, etl::timer::mode::Repeating);
CHECK_EQUAL(500U, config4.get_period());
CHECK_EQUAL(500U, config4.get_delta());
CHECK_EQUAL(etl::timer::mode::Repeating, config4.get_mode());
config2.clear();
CHECK_EQUAL(etl::timer::state::Inactive, config2.get_period());
CHECK_EQUAL(etl::timer::state::Inactive, config2.get_delta());
CHECK_EQUAL(etl::timer::mode::Repeating, config2.get_mode());
}
@ -74,11 +69,9 @@ namespace
const etl::timer_config& config1_ref = node1.get_config();
CHECK_EQUAL(config1.get_period(), node1.get_config().get_period());
CHECK_EQUAL(config1.get_delta(), node1.get_config().get_delta());
CHECK_EQUAL(config1.get_mode(), node1.get_config().get_mode());
CHECK_EQUAL(node1.get_config().get_period(), node2.get_config().get_period());
CHECK_EQUAL(node1.get_config().get_delta(), node2.get_config().get_delta());
CHECK_EQUAL(node1.get_config().get_mode(), node2.get_config().get_mode());
}
}

View File

@ -3559,6 +3559,7 @@
<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\intrusive_timer.h" />
<ClInclude Include="..\..\include\etl\private\timer_lists.h" />
<ClInclude Include="..\..\include\etl\timer_node.h" />
<ClInclude Include="..\..\include\etl\invoke.h" />
@ -10403,6 +10404,7 @@
<ClCompile Include="..\test_index_of_type.cpp" />
<ClCompile Include="..\test_inplace_function.cpp" />
<ClCompile Include="..\test_intrusive_links.cpp" />
<ClCompile Include="..\test_intrusive_timer.cpp" />
<ClCompile Include="..\test_invoke.cpp" />
<ClCompile Include="..\test_is_invocable.cpp" />
<ClCompile Include="..\test_macros.cpp" />

View File

@ -1575,6 +1575,9 @@
<ClInclude Include="..\..\include\etl\private\timer_lists.h">
<Filter>ETL\Private</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\intrusive_timer.h">
<Filter>ETL\Frameworks</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\test_string_char.cpp">
@ -3851,6 +3854,9 @@
<ClCompile Include="..\test_timer_lists.cpp">
<Filter>Tests\Callback Timers</Filter>
</ClCompile>
<ClCompile Include="..\test_intrusive_timer.cpp">
<Filter>Tests\Callback Timers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\library.properties">