Modified locked and deferred_locked timers

This commit is contained in:
John Wellbelove 2026-09-30 10:24:50 +01:00
parent f19c19ac51
commit 8a7647cb3a
4 changed files with 235 additions and 193 deletions

View File

@ -97,7 +97,6 @@ namespace etl
count -= timer.delta;
active_list.remove(timer.id, true);
remove_callback.call_if(timer.id);
if (timer.callback.is_valid())
{
@ -112,9 +111,10 @@ namespace etl
// Reinsert the timer.
timer.delta = timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
}
time_to_next_callback.call_if(time_to_next());
has_active = !active_list.empty();
}

View File

@ -53,7 +53,7 @@ namespace etl
typedef etl::delegate<void(void)> lock_type;
typedef etl::delegate<void(void)> unlock_type;
typedef etl::delegate<void(etl::timer::id::type)> event_callback_type;
typedef etl::delegate<void(uint32_t)> event_callback_type;
//*******************************************
/// Register a timer.
@ -102,7 +102,7 @@ namespace etl
{
lock();
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
time_to_next_callback.call_if(time_to_next());
unlock();
}
@ -180,12 +180,11 @@ namespace etl
if (timer.is_active())
{
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
}
timer.delta = immediate_ ? 0U : timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
time_to_next_callback.call_if(time_to_next());
unlock();
result = true;
@ -215,7 +214,7 @@ namespace etl
{
lock();
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
time_to_next_callback.call_if(time_to_next());
unlock();
}
@ -324,31 +323,17 @@ namespace etl
}
//*******************************************
/// Set a callback when a timer is inserted on list
/// Set a callback when the time to the next timer changes
//*******************************************
void set_insert_callback(event_callback_type insert_)
void set_time_to_next_callback(event_callback_type callback_)
{
insert_callback = insert_;
time_to_next_callback = callback_;
}
//*******************************************
/// Set a callback when a timer is removed from list
//*******************************************
void set_remove_callback(event_callback_type remove_)
void clear_time_to_next_callback()
{
remove_callback = remove_;
}
//*******************************************
void clear_insert_callback()
{
insert_callback.clear();
}
//*******************************************
void clear_remove_callback()
{
remove_callback.clear();
time_to_next_callback.clear();
}
protected:
@ -641,8 +626,7 @@ namespace etl
lock_type lock; ///< The callback that locks.
unlock_type unlock; ///< The callback that unlocks.
event_callback_type insert_callback;
event_callback_type remove_callback;
event_callback_type time_to_next_callback;
public:
@ -714,7 +698,6 @@ namespace etl
count -= timer.delta;
active_list.remove(timer.id, true);
remove_callback.call_if(timer.id);
if (timer.callback.is_valid())
{
@ -726,9 +709,10 @@ namespace etl
// Reinsert the timer.
timer.delta = timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
}
time_to_next_callback.call_if(time_to_next());
has_active = !active_list.empty();
}

View File

@ -156,38 +156,29 @@ namespace
callback_type member_callback_inc2 = callback_type::create<Object, object, &Object::callback_inc2>();
callback_type member_callback_inc3 = callback_type::create<Object, object, &Object::callback_inc3>();
class TimerInsertRemoveTest
class TimerTimeToNextTest
{
public:
uint32_t inserted;
uint32_t removed;
TimerInsertRemoveTest()
: inserted(0)
, removed(0)
uint32_t time_to_next;
TimerTimeToNextTest()
: time_to_next(0)
{
}
void insert_handler(etl::timer::id::type id_)
void time_to_next_handler(uint32_t time_to_next_)
{
(void)id_;
inserted++;
}
void remove_handler(etl::timer::id::type id_)
{
(void)id_;
removed++;
time_to_next = time_to_next_;
}
void clear(void)
{
inserted = 0;
removed = 0;
time_to_next = 0;
}
};
TimerInsertRemoveTest timerInsertRemoveTest;
TimerTimeToNextTest timerTimeToNextTest;
//***************************************************************************
// Free function callback via etl::function
@ -1013,9 +1004,49 @@ namespace
}
//*************************************************************************
TEST(callback_timer_is_active)
// Tick ID1 ID2 ID3
// 0 . . .
// 1 . . .
// 2 . . .
// 3 . . .
// 4 . . .
// 5 . . .
// 6 . . .
// 7 . . .
// 8 . . .
// 9 . . O
// 10 . . .
// 11 . . .
// 12 . . .
// 13 . . .
// 14 . . .
// 15 . . .
// 16 . . .
// 17 . . .
// 18 . . O
// 19 . . .
// 20 . . .
// 21 . . .
// 22 . . .
// 23 . O .
// 24 . . .
// 25 . . .
// 26 . . .
// 27 . . O
// 28 . . .
// 29 . . .
// 30 . . .
// 31 . . .
// 32 . . .
// 33 . . .
// 34 . . .
// 35 . . .
// 36 . . O
// 37 O . .
//
TEST(callback_timer_is_active_and_time_to_next)
{
timerInsertRemoveTest.clear();
timerTimeToNextTest.clear();
locks.clear();
try_lock_type try_lock = try_lock_type::create<Locks, locks, &Locks::try_lock>();
lock_type lock = lock_type::create<Locks, locks, &Locks::lock>();
@ -1025,19 +1056,20 @@ namespace
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Single_Shot);
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Single_Shot);
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::Single_Shot);
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 9, etl::timer::mode::Repeating);
timer_controller.set_insert_callback(
event_callback_type::create<TimerInsertRemoveTest, timerInsertRemoveTest, &TimerInsertRemoveTest::insert_handler>());
timer_controller.set_remove_callback(
event_callback_type::create<TimerInsertRemoveTest, timerInsertRemoveTest, &TimerInsertRemoveTest::remove_handler>());
timer_controller.set_time_to_next_callback(
event_callback_type::create<TimerTimeToNextTest, timerTimeToNextTest, &TimerTimeToNextTest::time_to_next_handler>());
timer_controller.start(id1);
timer_controller.start(id3);
timer_controller.start(id2);
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(0, timerInsertRemoveTest.removed);
timer_controller.start(id1);
timer_controller.start(id3);
timer_controller.start(id2);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next); // The time to next should be the interval for id3.
timer_controller.enable(true);
@ -1045,81 +1077,77 @@ namespace
CHECK_TRUE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(0, timerInsertRemoveTest.removed);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next); // The time to next should be the interval for id3.
timer_controller.tick(11);
// Tick the time for id3 to expire.
timer_controller.tick(9);
timer_controller.handle_deferred();
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_TRUE(timer_controller.is_active(id2));
CHECK_FALSE(timer_controller.is_active(id3));
CHECK_TRUE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(1, timerInsertRemoveTest.removed);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next); // The time to next should be the what's left of the interval for id3.
timer_controller.tick(23 - 11);
// Stop the timer and check that the time to next is updated to that of id2.
timer_controller.stop(id3);
CHECK_EQUAL(14, timerTimeToNextTest.time_to_next); // The time to next should be the what's left of the interval for id2.
// Start the timer again and check that the time to next is updated to the interval for id3.
timer_controller.start(id3);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next);
// Tick the time for id3 to expire.
timer_controller.tick(9);
timer_controller.handle_deferred();
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_TRUE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
// The time to next should be the what's left of the interval for id2.
CHECK_EQUAL(5, timerTimeToNextTest.time_to_next);
// Tick the remaining time for id2 to expire.
timer_controller.tick(5);
timer_controller.handle_deferred();
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_FALSE(timer_controller.is_active(id3));
CHECK_TRUE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(2, timerInsertRemoveTest.removed);
// The time to next should be the what's left of the interval for id3.
CHECK_EQUAL(4, timerTimeToNextTest.time_to_next);
timer_controller.tick(37 - 23);
// Tick the remaining time for id3 to expire.
timer_controller.tick(4);
timer_controller.handle_deferred();
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
// The time to next should be the what's left of the interval for id3.
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next);
// Tick the remaining time for id3 to expire.
timer_controller.tick(9);
timer_controller.handle_deferred();
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
// The time to next should be the what's left of the interval for id1.
CHECK_EQUAL(1, timerTimeToNextTest.time_to_next);
// Stop timer id3 so that id1 is the last timeout.
timer_controller.stop(id3);
// Tick the remaining time for id1 to expire.
timer_controller.tick(1);
timer_controller.handle_deferred();
CHECK_FALSE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_FALSE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(3, timerInsertRemoveTest.removed);
}
//*************************************************************************
TEST(message_timer_time_to_next_with_has_active_timer)
{
locks.clear();
try_lock_type try_lock = try_lock_type::create<Locks, locks, &Locks::try_lock>();
lock_type lock = lock_type::create<Locks, locks, &Locks::lock>();
unlock_type unlock = unlock_type::create<Locks, locks, &Locks::unlock>();
etl::callback_timer_deferred_locked<3, 3> timer_controller(try_lock, lock, unlock);
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Single_Shot);
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Single_Shot);
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::Single_Shot);
timer_controller.set_insert_callback(
event_callback_type::create<TimerInsertRemoveTest, timerInsertRemoveTest, &TimerInsertRemoveTest::insert_handler>());
timer_controller.set_remove_callback(
event_callback_type::create<TimerInsertRemoveTest, timerInsertRemoveTest, &TimerInsertRemoveTest::remove_handler>());
timer_controller.start(id1);
timer_controller.start(id3);
timer_controller.start(id2);
timer_controller.enable(true);
timer_controller.tick(11);
timer_controller.handle_deferred();
CHECK_EQUAL(23 - 11, timer_controller.time_to_next());
CHECK_TRUE(timer_controller.has_active_timer());
timer_controller.tick(23);
timer_controller.handle_deferred();
CHECK_EQUAL(3, timer_controller.time_to_next());
CHECK_TRUE(timer_controller.has_active_timer());
timer_controller.tick(2);
timer_controller.handle_deferred();
CHECK_EQUAL(1, timer_controller.time_to_next());
CHECK_TRUE(timer_controller.has_active_timer());
timer_controller.tick(1);
timer_controller.handle_deferred();
CHECK_EQUAL(static_cast<etl::timer::interval::type>(etl::timer::interval::No_Active_Interval), timer_controller.time_to_next());
CHECK_FALSE(timer_controller.has_active_timer());
// There are no active timers so the time to next should be No_Active_Interval.
CHECK_EQUAL(etl::timer::interval::No_Active_Interval, timerTimeToNextTest.time_to_next);
}
//*************************************************************************

View File

@ -127,38 +127,29 @@ namespace
callback_type member_callback = callback_type::create<Object, object, &Object::callback>();
callback_type member_callback2 = callback_type::create<Object, object, &Object::callback2>();
class TimerInsertRemoveTest
class TimerTimeToNextTest
{
public:
uint32_t inserted;
uint32_t removed;
TimerInsertRemoveTest()
: inserted(0)
, removed(0)
uint32_t time_to_next;
TimerTimeToNextTest()
: time_to_next(0)
{
}
void insert_handler(etl::timer::id::type id_)
void time_to_next_handler(uint32_t time_to_next_)
{
(void)id_;
inserted++;
}
void remove_handler(etl::timer::id::type id_)
{
(void)id_;
removed++;
time_to_next = time_to_next_;
}
void clear(void)
{
inserted = 0;
removed = 0;
time_to_next = 0;
}
};
TimerInsertRemoveTest timerInsertRemoveTest;
TimerTimeToNextTest timerTimeToNextTest;
//***************************************************************************
// Free function callback via etl::function
@ -907,9 +898,49 @@ namespace
}
//*************************************************************************
TEST(callback_timer_is_active)
// Tick ID1 ID2 ID3
// 0 . . .
// 1 . . .
// 2 . . .
// 3 . . .
// 4 . . .
// 5 . . .
// 6 . . .
// 7 . . .
// 8 . . .
// 9 . . O
// 10 . . .
// 11 . . .
// 12 . . .
// 13 . . .
// 14 . . .
// 15 . . .
// 16 . . .
// 17 . . .
// 18 . . O
// 19 . . .
// 20 . . .
// 21 . . .
// 22 . . .
// 23 . O .
// 24 . . .
// 25 . . .
// 26 . . .
// 27 . . O
// 28 . . .
// 29 . . .
// 30 . . .
// 31 . . .
// 32 . . .
// 33 . . .
// 34 . . .
// 35 . . .
// 36 . . O
// 37 O . .
//
TEST(callback_timer_is_active_and_time_to_next)
{
timerInsertRemoveTest.clear();
timerTimeToNextTest.clear();
locks.clear();
try_lock_type try_lock = try_lock_type::create<Locks, locks, &Locks::try_lock>();
lock_type lock = lock_type::create<Locks, locks, &Locks::lock>();
@ -919,19 +950,16 @@ namespace
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Single_Shot);
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Single_Shot);
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::Single_Shot);
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 9, etl::timer::mode::Repeating);
timer_controller.set_insert_callback(
event_callback_type::create<TimerInsertRemoveTest, timerInsertRemoveTest, &TimerInsertRemoveTest::insert_handler>());
timer_controller.set_remove_callback(
event_callback_type::create<TimerInsertRemoveTest, timerInsertRemoveTest, &TimerInsertRemoveTest::remove_handler>());
timer_controller.set_time_to_next_callback(
event_callback_type::create<TimerTimeToNextTest, timerTimeToNextTest, &TimerTimeToNextTest::time_to_next_handler>());
timer_controller.start(id1);
timer_controller.start(id3);
timer_controller.start(id2);
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(0, timerInsertRemoveTest.removed);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next); // The time to next should be the interval for id3.
timer_controller.enable(true);
@ -939,69 +967,71 @@ namespace
CHECK_TRUE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(0, timerInsertRemoveTest.removed);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next); // The time to next should be the interval for id3.
timer_controller.tick(11);
// Tick the time for id3 to expire.
timer_controller.tick(9);
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_TRUE(timer_controller.is_active(id2));
CHECK_FALSE(timer_controller.is_active(id3));
CHECK_TRUE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(1, timerInsertRemoveTest.removed);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next); // The time to next should be the what's left of the interval for id3.
timer_controller.tick(23 - 11);
// Stop the timer and check that the time to next is updated to that of id2.
timer_controller.stop(id3);
CHECK_EQUAL(14, timerTimeToNextTest.time_to_next); // The time to next should be the what's left of the interval for id2.
// Start the timer again and check that the time to next is updated to the interval for id3.
timer_controller.start(id3);
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next);
// Tick the time for id3 to expire.
timer_controller.tick(9);
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_TRUE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
// The time to next should be the what's left of the interval for id2.
CHECK_EQUAL(5, timerTimeToNextTest.time_to_next);
// Tick the remaining time for id2 to expire.
timer_controller.tick(5);
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_FALSE(timer_controller.is_active(id3));
CHECK_TRUE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(2, timerInsertRemoveTest.removed);
// The time to next should be the what's left of the interval for id3.
CHECK_EQUAL(4, timerTimeToNextTest.time_to_next);
timer_controller.tick(37 - 23);
// Tick the remaining time for id3 to expire.
timer_controller.tick(4);
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
// The time to next should be the what's left of the interval for id3.
CHECK_EQUAL(9, timerTimeToNextTest.time_to_next);
// Tick the remaining time for id3 to expire.
timer_controller.tick(9);
CHECK_TRUE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_TRUE(timer_controller.is_active(id3));
// The time to next should be the what's left of the interval for id1.
CHECK_EQUAL(1, timerTimeToNextTest.time_to_next);
// Stop timer id3 so that id1 is the last timeout.
timer_controller.stop(id3);
// Tick the remaining time for id1 to expire.
timer_controller.tick(1);
CHECK_FALSE(timer_controller.is_active(id1));
CHECK_FALSE(timer_controller.is_active(id2));
CHECK_FALSE(timer_controller.is_active(id3));
CHECK_EQUAL(3, timerInsertRemoveTest.inserted);
CHECK_EQUAL(3, timerInsertRemoveTest.removed);
}
//*************************************************************************
TEST(message_timer_time_to_next_with_has_active_timer)
{
locks.clear();
try_lock_type try_lock = try_lock_type::create<Locks, locks, &Locks::try_lock>();
lock_type lock = lock_type::create<Locks, locks, &Locks::lock>();
unlock_type unlock = unlock_type::create<Locks, locks, &Locks::unlock>();
etl::callback_timer_locked<3> timer_controller(try_lock, lock, unlock);
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::Single_Shot);
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::Single_Shot);
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::Single_Shot);
timer_controller.start(id1);
timer_controller.start(id3);
timer_controller.start(id2);
timer_controller.enable(true);
timer_controller.tick(11);
CHECK_EQUAL(12, timer_controller.time_to_next());
CHECK_TRUE(timer_controller.has_active_timer());
timer_controller.tick(23);
CHECK_EQUAL(3, timer_controller.time_to_next());
CHECK_TRUE(timer_controller.has_active_timer());
timer_controller.tick(2);
CHECK_EQUAL(1, timer_controller.time_to_next());
CHECK_TRUE(timer_controller.has_active_timer());
timer_controller.tick(1);
CHECK_EQUAL(static_cast<etl::timer::interval::type>(etl::timer::interval::No_Active_Interval), timer_controller.time_to_next());
CHECK_FALSE(timer_controller.has_active_timer());
// There are no active timers so the time to next should be No_Active_Interval.
CHECK_EQUAL(etl::timer::interval::No_Active_Interval, timerTimeToNextTest.time_to_next);
}
//*************************************************************************