diff --git a/repos/base-hw/src/core/include/kernel/clock.h b/repos/base-hw/src/core/include/kernel/clock.h index cd2bd812e..e4e412376 100644 --- a/repos/base-hw/src/core/include/kernel/clock.h +++ b/repos/base-hw/src/core/include/kernel/clock.h @@ -39,8 +39,9 @@ class Kernel::Timeout : public Genode::List::Element private: bool _listed = false; - time_t _start = 0; - time_t _end = 0; + time_t _start; + time_t _end; + bool _end_period; public: @@ -62,12 +63,11 @@ class Kernel::Clock unsigned const _cpu_id; Timer * const _timer; time_t _time = 0; - Genode::List _timeout_list; + bool _time_period = false; + Genode::List _timeout_list[2]; time_t _last_timeout_duration = 0; - void _insert_timeout(Timeout * const timeout); - - void _remove_timeout(Timeout * const timeout); + bool _time_overflow(time_t const duration) const; public: diff --git a/repos/base-hw/src/core/kernel/clock.cc b/repos/base-hw/src/core/kernel/clock.cc index fd048963f..4678a63ef 100644 --- a/repos/base-hw/src/core/kernel/clock.cc +++ b/repos/base-hw/src/core/kernel/clock.cc @@ -13,31 +13,11 @@ /* Core includes */ #include +#include using namespace Kernel; -void Clock::_insert_timeout(Timeout * const timeout) -{ - /* timeouts may get re-inserted as result of an update */ - if (timeout->_listed) { _timeout_list.remove(timeout); } - timeout->_listed = true; - - /* timeouts are ordered ascending according to their end time */ - Timeout * t1 = 0; - Timeout * t2 = _timeout_list.first(); - for (; t2 && t2->_end < timeout->_end; t1 = t2, t2 = t2->next()) ; - _timeout_list.insert(timeout, t1); -} - - -void Clock::_remove_timeout(Timeout * const timeout) -{ - timeout->_listed = false; - _timeout_list.remove(timeout); -} - - time_t Clock::us_to_tics(time_t const us) const { return _timer->us_to_tics(us); @@ -57,17 +37,51 @@ time_t Clock::timeout_max_us() const } +bool Clock::_time_overflow(time_t const duration) const +{ + return duration > ~(time_t)0 - _time; +} + + void Clock::set_timeout(Timeout * const timeout, time_t const duration) { - timeout->_start = _time; - timeout->_end = _time + duration; - _insert_timeout(timeout); + /* + * Remove timeout if it is already in use. Timeouts may get overridden as + * result of an update. + */ + if (timeout->_listed) { + _timeout_list[timeout->_end_period].remove(timeout); + } else { + timeout->_listed = true; } + + /* set timeout parameters */ + timeout->_start = _time; + timeout->_end = _time + duration; + timeout->_end_period = _time_overflow(duration) ? !_time_period : + _time_period; + + /* + * Insert timeout. Timeouts are ordered ascending according to their end + * time to be able to quickly determine the nearest timeout. + */ + Genode::List & list = _timeout_list[timeout->_end_period]; + Timeout * t1 = 0; + for (Timeout * t2 = list.first(); t2 && t2->_end < timeout->_end; + t1 = t2, t2 = t2->next()) { } + + list.insert(timeout, t1); } void Clock::schedule_timeout() { - Timeout const * const timeout = _timeout_list.first(); + /* get the timeout with the nearest end time */ + Timeout * timeout = _timeout_list[_time_period].first(); + if (!timeout) { + timeout = _timeout_list[!_time_period].first(); + assert(timeout); + } + /* install timeout at timer hardware */ time_t const duration = (time_t)timeout->_end - _time; _last_timeout_duration = duration; _timer->start_one_shot(duration, _cpu_id); @@ -76,20 +90,47 @@ void Clock::schedule_timeout() time_t Clock::update_time() { - /* update time */ + /* determine how much time has passed */ time_t const old_value = _last_timeout_duration; time_t const new_value = _timer->value(_cpu_id); time_t const duration = old_value > new_value ? old_value - new_value : 1; + + /* is this the end of the current period? */ + if (_time_overflow(duration)) { + + /* flush all timeouts of current period */ + Genode::List & list = _timeout_list[_time_period]; + while (true) { + Timeout * const timeout = list.first(); + if (!timeout) { break; } + list.remove(timeout); + timeout->_listed = false; + timeout->timeout_triggered(); + } + /* switch to next period */ + _time_period = !_time_period; + + } + /* update time */ _time += duration; return duration; } + void Clock::process_timeouts() { + /* + * Walk through timeouts until the first whose end time is in the future. + * Consider only the current periods list as all timeouts of the next + * period must be in the future. + */ + Genode::List & list = _timeout_list[_time_period]; while (true) { - Timeout * const timeout = _timeout_list.first(); - if (!timeout || timeout->_end > _time) { break; } - _remove_timeout(timeout); + Timeout * const timeout = list.first(); + if (!timeout) { break; } + if (timeout->_end > _time) { break; } + list.remove(timeout); + timeout->_listed = false; timeout->timeout_triggered(); } }