mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Added next timer notifications
This commit is contained in:
parent
78bdd671fa
commit
28ac16f035
@ -58,7 +58,11 @@ namespace etl
|
||||
{
|
||||
public:
|
||||
|
||||
typedef etl::delegate<void(etl::timer_node&, uint32_t delta)> timer_update_callback_type;
|
||||
//*******************************************
|
||||
/// The callback type for notifying the user of the next timer that is due to expire.
|
||||
/// Either returns a pointer to the next timer that is due to expire or etl::timer_node::end() if there is no next timer.
|
||||
//*******************************************
|
||||
typedef etl::delegate<void(const etl::timer_node*)> timer_notification_callback_type;
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
@ -167,6 +171,8 @@ namespace etl
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
bool notify = false;
|
||||
|
||||
if (!lock_policy.is_locked())
|
||||
{
|
||||
lock_policy.lock();
|
||||
@ -181,8 +187,12 @@ namespace etl
|
||||
|
||||
while (has_active && (count >= timer->delta))
|
||||
{
|
||||
// Calculate if we have any time left for the next timer.
|
||||
count -= timer->delta;
|
||||
|
||||
// The timer has expired, so reset the delta to 0.
|
||||
timer->delta = 0;
|
||||
|
||||
active_list.remove(timer);
|
||||
|
||||
// If the timer is repeating then add it back to the active list.
|
||||
@ -198,6 +208,8 @@ namespace etl
|
||||
timer = active_list.first();
|
||||
|
||||
has_active = !active_list.empty();
|
||||
|
||||
notify = true;
|
||||
}
|
||||
|
||||
if (has_active)
|
||||
@ -208,10 +220,19 @@ namespace etl
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next timer that is due to expire.
|
||||
etl::timer_node* next_timer = active_list.first();
|
||||
|
||||
lock_policy.unlock();
|
||||
|
||||
was_processed = true;
|
||||
|
||||
if (notify)
|
||||
{
|
||||
// The next timer has changed, so notify the user.
|
||||
next_timer_notification.call_if(next_timer);
|
||||
}
|
||||
|
||||
// Invoke all of the expired timers.
|
||||
invoke_expired_timers();
|
||||
}
|
||||
@ -222,7 +243,7 @@ namespace etl
|
||||
//*******************************************
|
||||
/// Starts a timer.
|
||||
//*******************************************
|
||||
bool start(etl::timer_node& timer_, bool immediate_ = false) ETL_NOEXCEPT
|
||||
bool start(etl::timer_node& timer_, bool immediate_ = false)
|
||||
{
|
||||
bool is_started = false;
|
||||
|
||||
@ -237,6 +258,8 @@ namespace etl
|
||||
}
|
||||
}
|
||||
|
||||
bool notify = (active_list.first() == &timer_);
|
||||
|
||||
lock_policy.unlock();
|
||||
|
||||
if (is_started && immediate_)
|
||||
@ -245,83 +268,81 @@ namespace etl
|
||||
timer_.invoke(timer_.get_config());
|
||||
}
|
||||
|
||||
if (notify)
|
||||
{
|
||||
// The next timer has changed, so notify the user.
|
||||
next_timer_notification.call_if(active_list.first());
|
||||
}
|
||||
|
||||
return is_started;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Stops a timer.
|
||||
//*******************************************
|
||||
bool stop(etl::timer_node* timer_) ETL_NOEXCEPT
|
||||
bool stop(etl::timer_node& timer_) ETL_NOEXCEPT
|
||||
{
|
||||
bool is_stopped = false;
|
||||
|
||||
lock_policy.lock();
|
||||
|
||||
if (timer_->is_active())
|
||||
bool notify = false;
|
||||
|
||||
if (timer_.is_active())
|
||||
{
|
||||
notify = (active_list.first() == &timer_);
|
||||
stop_impl(timer_);
|
||||
is_stopped = true;
|
||||
}
|
||||
|
||||
// Get the next timer that is due to expire.
|
||||
etl::timer_node* next_timer = active_list.first();
|
||||
|
||||
lock_policy.unlock();
|
||||
|
||||
if (notify)
|
||||
{
|
||||
// The next timer has changed, so notify the user.
|
||||
next_timer_notification.call_if(next_timer);
|
||||
}
|
||||
|
||||
return is_stopped;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Sets a timer's period.
|
||||
//*******************************************
|
||||
bool set_period(etl::timer_node* timer_, uint32_t period_) ETL_NOEXCEPT
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
lock_policy.lock();
|
||||
|
||||
if (timer_->is_registered(this))
|
||||
{
|
||||
if (timer_->is_active())
|
||||
{
|
||||
stop_impl(timer_);
|
||||
}
|
||||
|
||||
timer_->set_period(period_);
|
||||
result = true;
|
||||
}
|
||||
|
||||
lock_policy.unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Sets a timer's period.
|
||||
//*******************************************
|
||||
bool set_period(etl::timer_node& timer_, uint32_t period_) ETL_NOEXCEPT
|
||||
{
|
||||
return set_period(&timer_, period_);
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Sets a timer's mode.
|
||||
//*******************************************
|
||||
bool set_mode(etl::timer_node* timer_, etl::timer::mode::type mode_) ETL_NOEXCEPT
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
lock_policy.lock();
|
||||
|
||||
if (timer_->is_registered(this))
|
||||
bool notify = false;
|
||||
|
||||
if (timer_.is_registered(this))
|
||||
{
|
||||
if (timer_->is_active())
|
||||
if (timer_.is_active())
|
||||
{
|
||||
notify = (active_list.first() == &timer_);
|
||||
stop_impl(timer_);
|
||||
}
|
||||
timer_->set_mode(mode_);
|
||||
|
||||
timer_.set_period(period_);
|
||||
result = true;
|
||||
}
|
||||
|
||||
// Get the next timer that is due to expire.
|
||||
etl::timer_node* next_timer = active_list.first();
|
||||
|
||||
lock_policy.unlock();
|
||||
|
||||
if (notify)
|
||||
{
|
||||
// The next timer has changed, so notify the user.
|
||||
next_timer_notification.call_if(next_timer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -330,7 +351,35 @@ namespace etl
|
||||
//*******************************************
|
||||
bool set_mode(etl::timer_node& timer_, etl::timer::mode::type mode_) ETL_NOEXCEPT
|
||||
{
|
||||
return set_mode(&timer_, mode_);
|
||||
bool result = false;
|
||||
|
||||
lock_policy.lock();
|
||||
|
||||
bool notify = false;
|
||||
|
||||
if (timer_.is_registered(this))
|
||||
{
|
||||
if (timer_.is_active())
|
||||
{
|
||||
notify = (active_list.first() == &timer_);
|
||||
stop_impl(timer_);
|
||||
}
|
||||
timer_.set_mode(mode_);
|
||||
result = true;
|
||||
}
|
||||
|
||||
// Get the next timer that is due to expire.
|
||||
etl::timer_node* next_timer = active_list.first();
|
||||
|
||||
lock_policy.unlock();
|
||||
|
||||
if (notify)
|
||||
{
|
||||
// The next timer has changed, so notify the user.
|
||||
next_timer_notification.call_if(next_timer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
@ -356,7 +405,7 @@ namespace etl
|
||||
|
||||
lock_policy.lock();
|
||||
|
||||
if (!active_list.empty())
|
||||
if (has_active_timer())
|
||||
{
|
||||
delta = active_list.first()->get_delta();
|
||||
}
|
||||
@ -374,6 +423,22 @@ namespace etl
|
||||
return timer_count;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Sets the update notification.
|
||||
//*******************************************
|
||||
void set_update_notification(timer_notification_callback_type callback) ETL_NOEXCEPT
|
||||
{
|
||||
next_timer_notification = callback;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Clears the update notification.
|
||||
//*******************************************
|
||||
void clear_time_next_notification() ETL_NOEXCEPT
|
||||
{
|
||||
next_timer_notification.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//*******************************************
|
||||
@ -381,7 +446,7 @@ namespace etl
|
||||
//*******************************************
|
||||
etl::timer::id::type get_next_id() ETL_NOEXCEPT
|
||||
{
|
||||
etl::timer::id::type next_id = 0; // IDs start at 0
|
||||
etl::timer::id::type next_id = 0; // IDs start at 0.
|
||||
|
||||
etl::timer_node* timer = register_list.first();
|
||||
|
||||
@ -389,33 +454,32 @@ namespace etl
|
||||
{
|
||||
if (timer->get_id() == next_id)
|
||||
{
|
||||
// Already in use, so increment next_id and continue checking
|
||||
// Already in use, so increment next_id and continue checking.
|
||||
++next_id;
|
||||
}
|
||||
else if (timer->get_id() > next_id)
|
||||
{
|
||||
// Found a gap in the IDs, so return the next_id
|
||||
// Found a gap in the IDs, so return the next_id.
|
||||
return next_id;
|
||||
}
|
||||
|
||||
timer = register_list.next(timer);
|
||||
}
|
||||
|
||||
// We never found a gap, so return the next available ID
|
||||
// We never found a gap, so return the next available ID.
|
||||
return next_id;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Stops a timer without locking.
|
||||
/// Returns the timer node if it was found, otherwise returns end().
|
||||
//*******************************************
|
||||
void stop_impl(etl::timer_node* timer_) ETL_NOEXCEPT
|
||||
void stop_impl(etl::timer_node& timer_) ETL_NOEXCEPT
|
||||
{
|
||||
active_list.remove(timer_);
|
||||
active_list.remove(&timer_);
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Invoke for all expired timers.
|
||||
/// Invoke all of the expired timers.
|
||||
/// This is called after the lock has been released to avoid deadlocks.
|
||||
//*******************************************
|
||||
void invoke_expired_timers()
|
||||
@ -442,6 +506,8 @@ namespace etl
|
||||
etl::private_timer::timer_active_list active_list;
|
||||
etl::private_timer::timer_expired_list expired_list;
|
||||
|
||||
timer_notification_callback_type next_timer_notification;
|
||||
|
||||
bool enabled;
|
||||
size_t timer_count;
|
||||
};
|
||||
|
||||
@ -224,6 +224,21 @@ namespace etl
|
||||
return config.get_id();
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// 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* end() ETL_NOEXCEPT
|
||||
{
|
||||
static etl::timer_node sentinel_node(etl::timer_config(0, 0));
|
||||
|
||||
return &sentinel_node;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//*******************************************
|
||||
@ -266,21 +281,6 @@ namespace etl
|
||||
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* end() ETL_NOEXCEPT
|
||||
{
|
||||
static etl::timer_node sentinel_node(etl::timer_config(0, 0));
|
||||
|
||||
return &sentinel_node;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Clear the next pointers.
|
||||
//*******************************************
|
||||
|
||||
@ -142,11 +142,13 @@ namespace
|
||||
//*************************************************************************
|
||||
bool global_callback_called = false;
|
||||
const etl::timer_config* global_config;
|
||||
uint64_t global_tick_count = 0;
|
||||
|
||||
static void global_callback(const etl::timer_config& config)
|
||||
{
|
||||
global_callback_called = true;
|
||||
global_config = &config;
|
||||
global_tick_count = ticks;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
@ -157,6 +159,7 @@ namespace
|
||||
Object()
|
||||
: member_callback_called(false)
|
||||
, member_config(ETL_NULLPTR)
|
||||
, member_tick_count(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -164,10 +167,12 @@ namespace
|
||||
{
|
||||
member_callback_called = true;
|
||||
member_config = &config;
|
||||
member_tick_count = ticks;
|
||||
}
|
||||
|
||||
bool member_callback_called = false;
|
||||
const etl::timer_config* member_config;
|
||||
uint64_t member_tick_count;
|
||||
};
|
||||
|
||||
static Object object;
|
||||
@ -198,7 +203,7 @@ namespace
|
||||
: etl::message_router<MessageProcessor, CallbackMessage>()
|
||||
, message_callback_called(false)
|
||||
, message_config(ETL_NULLPTR)
|
||||
, value(0)
|
||||
, message_value(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -207,7 +212,8 @@ namespace
|
||||
{
|
||||
message_callback_called = true;
|
||||
message_config = msg.config;
|
||||
value = msg.value;
|
||||
message_value = msg.value;
|
||||
message_tick_count = ticks;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
@ -215,7 +221,8 @@ namespace
|
||||
|
||||
bool message_callback_called;
|
||||
const etl::timer_config* message_config;
|
||||
int value;
|
||||
int message_value;
|
||||
uint64_t message_tick_count;
|
||||
};
|
||||
|
||||
using MessageTimer = etl::timer_message_node<CallbackMessage>;
|
||||
@ -223,6 +230,33 @@ namespace
|
||||
MessageProcessor message_processor;
|
||||
CallbackMessage callback_message(42);
|
||||
|
||||
//*************************************************************************
|
||||
// Notification of the next timer that is due to expire.
|
||||
//*************************************************************************
|
||||
struct notification_result_type
|
||||
{
|
||||
void clear()
|
||||
{
|
||||
timer = etl::timer_node::end();
|
||||
notified = false;
|
||||
}
|
||||
|
||||
const etl::timer_node* timer;
|
||||
bool notified;
|
||||
};
|
||||
|
||||
notification_result_type notification_result;
|
||||
|
||||
void next_timer_notification(const etl::timer_node* timer_)
|
||||
{
|
||||
notification_result.notified = true;
|
||||
notification_result.timer = timer_;
|
||||
}
|
||||
|
||||
using notification_type = etl::intrusive_timer::timer_notification_callback_type;
|
||||
|
||||
notification_type notification_delegate = notification_type::create(next_timer_notification);
|
||||
|
||||
SUITE(test_intrusive_timer)
|
||||
{
|
||||
//*************************************************************************
|
||||
@ -327,7 +361,7 @@ namespace
|
||||
|
||||
etl::intrusive_timer timer_controller(lock_policy);
|
||||
|
||||
etl::timer_config config1(100, etl::timer::mode::Single_Shot);
|
||||
etl::timer_config config1(37, etl::timer::mode::Single_Shot);
|
||||
CallbackDelegate callback_delegate1 = CallbackDelegate::create(global_callback);
|
||||
CallbackTimer callback_timer1(callback_delegate1, config1);
|
||||
|
||||
@ -357,7 +391,7 @@ namespace
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
while (ticks <= 110U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
@ -367,29 +401,20 @@ namespace
|
||||
CHECK_EQUAL((int)config1_id, (int)global_config->get_id());
|
||||
CHECK_EQUAL(config1.get_mode(), global_config->get_mode());
|
||||
CHECK_EQUAL(config1.get_period(), global_config->get_period());
|
||||
CHECK_EQUAL(config1.get_period(), global_tick_count);
|
||||
|
||||
CHECK_TRUE(object.member_callback_called);
|
||||
CHECK_EQUAL((int)config2_id, (int)object.member_config->get_id());
|
||||
CHECK_EQUAL(config2.get_mode(), object.member_config->get_mode());
|
||||
CHECK_EQUAL(config2.get_period(), object.member_config->get_period());
|
||||
CHECK_EQUAL(config2.get_period(), object.member_tick_count);
|
||||
|
||||
CHECK_TRUE(message_processor.message_callback_called);
|
||||
CHECK_EQUAL((int)config3_id, (int)message_processor.message_config->get_id());
|
||||
CHECK_EQUAL(config3.get_mode(), message_processor.message_config->get_mode());
|
||||
CHECK_EQUAL(config3.get_period(), message_processor.message_config->get_period());
|
||||
CHECK_EQUAL(callback_message.value, message_processor.value);
|
||||
|
||||
// std::vector<uint64_t> compare1 = {37};
|
||||
// std::vector<uint64_t> compare2 = {23};
|
||||
// std::vector<uint64_t> compare3 = {11};
|
||||
|
||||
// CHECK(object.tick_list.size() != 0);
|
||||
// CHECK(free_tick_list1.size() != 0);
|
||||
// CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
// CHECK_ARRAY_EQUAL(compare1.data(), object.tick_list.data(), compare1.size());
|
||||
// CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
// CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
CHECK_EQUAL(callback_message.value, message_processor.message_value);
|
||||
CHECK_EQUAL(config3.get_period(), message_processor.message_tick_count);
|
||||
}
|
||||
|
||||
// //*************************************************************************
|
||||
|
||||
@ -194,7 +194,7 @@ namespace
|
||||
|
||||
etl::timer_node* timer = active_list.first();
|
||||
|
||||
// The timers should be in the order of their deltas.
|
||||
// The timers should be in the order of their periods.
|
||||
CHECK_EQUAL(&timer2, timer);
|
||||
timer = active_list.next(timer);
|
||||
CHECK_EQUAL(&timer3, timer);
|
||||
@ -207,14 +207,25 @@ namespace
|
||||
timer = active_list.next(timer);
|
||||
CHECK_EQUAL(active_list.end(), timer);
|
||||
|
||||
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());
|
||||
// Check the deltas are correct.
|
||||
// The period order of the timers in the list is timer2, timer3, timer5, timer1, timer4.
|
||||
// timer 5 has the same period as timer 3, so it should be after timer 3 in the list.
|
||||
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());
|
||||
|
||||
// Remove timer3 from the list.
|
||||
active_list.remove(&timer3);
|
||||
|
||||
// Check the deltas are still correct.
|
||||
// The period order of the timers in the list is now timer2, timer5, timer1, timer4.
|
||||
CHECK_EQUAL(timer2.get_period(), timer2.get_delta());
|
||||
CHECK_EQUAL(timer5.get_period() - timer2.get_period(), timer5.get_delta());
|
||||
CHECK_EQUAL(timer1.get_period() - timer5.get_period(), timer1.get_delta());
|
||||
CHECK_EQUAL(timer4.get_period() - timer1.get_period(), timer4.get_delta());
|
||||
|
||||
// Search the list to ensure the other timers are still present.
|
||||
bool timer1_found = false;
|
||||
bool timer2_found = false;
|
||||
@ -256,12 +267,6 @@ namespace
|
||||
CHECK_TRUE(timer4_found);
|
||||
CHECK_TRUE(timer5_found);
|
||||
|
||||
// Check the deltas are still correct.
|
||||
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());
|
||||
|
||||
// Clear the list.
|
||||
active_list.clear();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user