From 7feea78991285c6c3b3c0f83659b80c62be0de77 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Fri, 11 Sep 2020 15:04:40 +0200 Subject: [PATCH] timeout: rework timeout framework * get rid of alarm abstraction * get rid of Timeout::Time type * get rid of pointer arguments * get rid of _discard_timeout indirection * get rid of 65th bit in stored time values * get rid of Timeout_scheduler interface * get rid of uninitialized deadlines * get rid of default arguments * get rid of Timeout::_periodic * get rid of Timeout::Raw * use list abstraction * only one interface for timeout handlers * rework locking scheme to be smp safe * move all method definitions to CC file * name mutexes more accurate * fix when & how to set time-source timeout * fix deadlocks Fixes #3884 --- repos/base-hw/src/timer/hw/time_source.cc | 4 +- repos/base-hw/src/timer/hw/time_source.h | 8 +- repos/base-nova/src/timer/nova/time_source.cc | 49 +- repos/base-nova/src/timer/nova/time_source.h | 11 +- repos/base/include/timer/timeout.h | 280 +++---- repos/base/include/timer_session/connection.h | 214 +++--- repos/base/lib/symbols/ld | 69 +- repos/base/src/lib/timeout/timeout.cc | 682 +++++++++--------- .../base/src/lib/timeout/timer_connection.cc | 52 +- .../src/lib/timeout/timer_connection_time.cc | 14 +- repos/base/src/timer/epit/time_source.cc | 4 +- repos/base/src/timer/epit/time_source.h | 2 +- repos/base/src/timer/gpt/time_source.cc | 4 +- repos/base/src/timer/gpt/time_source.h | 4 +- repos/base/src/timer/include/root_component.h | 4 +- .../src/timer/include/session_component.h | 7 +- .../src/timer/include/threaded_time_source.h | 15 +- repos/base/src/timer/periodic/time_source.cc | 8 +- repos/base/src/timer/periodic/time_source.h | 4 +- repos/base/src/timer/pit/time_source.cc | 4 +- repos/base/src/timer/pit/time_source.h | 2 +- repos/dde_linux/src/lib/lxip/lx.h | 6 +- repos/dde_linux/src/lib/lxip/timer_handler.cc | 27 +- repos/libports/include/lwip/genode_init.h | 2 +- .../src/lib/lwip/platform/sys_arch.cc | 6 +- repos/os/src/test/timeout/main.cc | 25 +- 26 files changed, 682 insertions(+), 825 deletions(-) diff --git a/repos/base-hw/src/timer/hw/time_source.cc b/repos/base-hw/src/timer/hw/time_source.cc index bc03eb4248..bf0f699c71 100644 --- a/repos/base-hw/src/timer/hw/time_source.cc +++ b/repos/base-hw/src/timer/hw/time_source.cc @@ -36,8 +36,8 @@ Timer::Time_source::Time_source(Env &env) } -void Timer::Time_source::schedule_timeout(Microseconds duration, - Timeout_handler &handler) +void Timer::Time_source::set_timeout(Microseconds duration, + Timeout_handler &handler) { Kernel::timeout_t duration_us = duration.value; if (duration_us < MIN_TIMEOUT_US) { diff --git a/repos/base-hw/src/timer/hw/time_source.h b/repos/base-hw/src/timer/hw/time_source.h index 7abcdd6351..514c77e855 100644 --- a/repos/base-hw/src/timer/hw/time_source.h +++ b/repos/base-hw/src/timer/hw/time_source.h @@ -25,8 +25,10 @@ namespace Timer { - using Microseconds = Genode::Microseconds; - using Duration = Genode::Duration; + using Microseconds = Genode::Microseconds; + using Duration = Genode::Duration; + using Timeout_handler = Genode::Timeout_handler; + class Time_source; } @@ -47,7 +49,7 @@ class Timer::Time_source : public Genode::Signalled_time_source *************************/ Duration curr_time() override; - void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; + void set_timeout(Microseconds duration, Timeout_handler &handler) override; Microseconds max_timeout() const override { return Microseconds(_max_timeout_us); }; }; diff --git a/repos/base-nova/src/timer/nova/time_source.cc b/repos/base-nova/src/timer/nova/time_source.cc index d1b9fb697b..726526d81b 100644 --- a/repos/base-nova/src/timer/nova/time_source.cc +++ b/repos/base-nova/src/timer/nova/time_source.cc @@ -22,50 +22,47 @@ using namespace Genode; using namespace Nova; -void Timer::Time_source::schedule_timeout(Microseconds duration, - Timeout_handler &handler) +void Timer::Time_source::set_timeout(Microseconds duration, + Timeout_handler &handler) { + /* set new timeout parameters and wake up the blocking thread */ Threaded_time_source::handler(handler); - - /* check whether to cancel last timeout */ - if (duration.value == 0 && _sem) { - uint8_t res = Nova::sm_ctrl(_sem, Nova::SEMAPHORE_UP); - if (res != Nova::NOVA_OK) - nova_die(); - } - /* remember timeout to be set during wait_for_timeout call */ _timeout_us = duration.value; + if (_sem) { + if (Nova::sm_ctrl(_sem, Nova::SEMAPHORE_UP) != Nova::NOVA_OK) { + nova_die(); + } + } } -void Timer::Time_source::_wait_for_irq() +Timer::Time_source::Result_of_wait_for_irq +Timer::Time_source::_wait_for_irq() { + /* initialize semaphore if not done yet */ if (!_sem) { - /* initialize first time in context of running thread */ auto const &exc_base = Thread::native_thread().exc_pt_sel; request_signal_sm_cap(exc_base + Nova::PT_SEL_PAGE_FAULT, exc_base + Nova::SM_SEL_SIGNAL); _sem = Thread::native_thread().exc_pt_sel + SM_SEL_SIGNAL; } - /* calculate absolute timeout */ - Trace::Timestamp now = Trace::timestamp(); - Trace::Timestamp us_64 = _timeout_us; + unsigned long long const deadline_timestamp { + _timeout_us <= max_timeout().value ? + Trace::timestamp() + _timeout_us * (_tsc_khz / TSC_FACTOR) : 0 }; - if (_timeout_us == max_timeout().value) { + /* block until timeout fires or it gets canceled */ + switch (sm_ctrl(_sem, SEMAPHORE_DOWN, deadline_timestamp)) { - /* tsc_absolute == 0 means blocking without timeout */ - uint8_t res = sm_ctrl(_sem, SEMAPHORE_DOWN, 0); - if (res != Nova::NOVA_OK && res != Nova::NOVA_TIMEOUT) { - nova_die(); } + case Nova::NOVA_TIMEOUT: + return IRQ_TRIGGERED; - } else { + case Nova::NOVA_OK: + return CANCELLED; - /* block until timeout fires or it gets canceled */ - unsigned long long tsc_absolute = now + us_64 * (_tsc_khz / TSC_FACTOR); - uint8_t res = sm_ctrl(_sem, SEMAPHORE_DOWN, tsc_absolute); - if (res != Nova::NOVA_OK && res != Nova::NOVA_TIMEOUT) { - nova_die(); } + default: + nova_die(); + return CANCELLED; } } diff --git a/repos/base-nova/src/timer/nova/time_source.h b/repos/base-nova/src/timer/nova/time_source.h index 7145d57a41..c3b4c5128f 100644 --- a/repos/base-nova/src/timer/nova/time_source.h +++ b/repos/base-nova/src/timer/nova/time_source.h @@ -53,7 +53,7 @@ class Timer::Time_source : public Threaded_time_source * The returned value must never be zero because it is used as * divisor by '_tsc_to_us'. */ - Genode::warning("unable to obtain tsc frequency, asuming 1 GHz"); + Genode::warning("unable to obtain tsc frequency, assuming 1 GHz"); return 1000*1000; } @@ -77,7 +77,7 @@ class Timer::Time_source : public Threaded_time_source ** Threaded_time_source ** **************************/ - void _wait_for_irq() override; + Result_of_wait_for_irq _wait_for_irq() override; public: @@ -92,13 +92,12 @@ class Timer::Time_source : public Threaded_time_source ** Genode::Time_source ** *************************/ - void schedule_timeout(Microseconds duration, - Timeout_handler &handler) override; + void set_timeout(Microseconds duration, + Timeout_handler &handler) override; Microseconds max_timeout() const override { - uint64_t const max_us = _tsc_to_us(~(uint64_t)0); - return max_us > ~(uint64_t)0 ? Microseconds(~(uint64_t)0) : Microseconds(max_us); + return Microseconds(_tsc_to_us(~(uint64_t)0)); } Duration curr_time() override diff --git a/repos/base/include/timer/timeout.h b/repos/base/include/timer/timeout.h index 8d87c91252..c9fab0c45a 100644 --- a/repos/base/include/timer/timeout.h +++ b/repos/base/include/timer/timeout.h @@ -21,38 +21,41 @@ /* Genode includes */ #include +#include #include -#include #include +#include +#include namespace Genode { class Time_source; - class Timeout_scheduler; class Timeout; - class Alarm_timeout_scheduler; + class Timeout_handler; + class Timeout_scheduler; } -namespace Timer -{ +namespace Timer { + class Connection; class Root_component; } +/** + * Interface of a timeout callback + */ +struct Genode::Timeout_handler : Interface +{ + virtual void handle_timeout(Duration curr_time) = 0; +}; + + /** * Interface of a time source that can handle one timeout at a time */ struct Genode::Time_source : Interface { - /** - * Interface of a timeout callback - */ - struct Timeout_handler : Interface - { - virtual void handle_timeout(Duration curr_time) = 0; - }; - /** * Return the current time of the source */ @@ -69,62 +72,8 @@ struct Genode::Time_source : Interface * \param duration timeout duration * \param handler timeout callback */ - virtual void schedule_timeout(Microseconds duration, - Timeout_handler &handler) = 0; - - /** - * Tell the time source which scheduler to use for its own timeouts - * - * This method enables a time source for example to synchronize with an - * accurate but expensive timer only on a periodic basis while using a - * cheaper interpolation in general. - */ - virtual void scheduler(Timeout_scheduler &) { }; -}; - - -/** - * Interface of a time-source multiplexer - * - * Beside 'curr_time()', this abstract interface is used by the Timeout - * implementation only. Users of the timeout framework must schedule and - * discard timeouts via methods of the timeout. - */ -class Genode::Timeout_scheduler : Interface -{ - private: - - friend Timeout; - - /** - * Add a one-shot timeout to the schedule - * - * \param timeout timeout callback object - * \param duration timeout trigger delay - */ - virtual void _schedule_one_shot(Timeout &timeout, Microseconds duration) = 0; - - /** - * Add a periodic timeout to the schedule - * - * \param timeout timeout callback object - * \param duration timeout trigger period - */ - virtual void _schedule_periodic(Timeout &timeout, Microseconds duration) = 0; - - /** - * Remove timeout from the scheduler - * - * \param timeout corresponding timeout callback object - */ - virtual void _discard(Timeout &timeout) = 0; - - public: - - /** - * Read out the now time of the scheduler - */ - virtual Duration curr_time() = 0; + virtual void set_timeout(Microseconds duration, + Timeout_handler &handler) = 0; }; @@ -136,173 +85,116 @@ class Genode::Timeout_scheduler : Interface * example, in a Timer-session server. If this is not the case, the classes * Periodic_timeout and One_shot_timeout are the better choice. */ -class Genode::Timeout : private Noncopyable +class Genode::Timeout : private Noncopyable, + public Genode::List::Element { - friend class Alarm_timeout_scheduler; - - public: - - /** - * Interface of a timeout handler - */ - struct Handler : Interface - { - virtual void handle_timeout(Duration curr_time) = 0; - }; + friend class Timeout_scheduler; private: - class Alarm - { - friend class Alarm_timeout_scheduler; + Mutex _mutex { }; + Timeout_scheduler &_scheduler; + Microseconds _period { 0 }; + Microseconds _deadline { Microseconds { 0 } }; + List_element _pending_timeouts_le { this }; + Timeout_handler *_pending_handler { nullptr }; + Timeout_handler *_handler { nullptr }; + bool _in_discard_blockade { false }; + Blockade _discard_blockade { }; - private: + Timeout(Timeout const &); - typedef uint64_t Time; - - struct Raw - { - Time deadline; - bool deadline_period; - Time period; - - bool is_pending_at(uint64_t time, bool time_period) const; - }; - - Mutex _dispatch_mutex { }; - Raw _raw { }; - short _active { 0 }; - bool _delete { false }; - Alarm *_next { nullptr }; - Alarm_timeout_scheduler *_scheduler { nullptr }; - - void _alarm_assign(Time period, - Time deadline, - bool deadline_period, - Alarm_timeout_scheduler *scheduler) - { - _raw.period = period; - _raw.deadline_period = deadline_period; - _raw.deadline = deadline; - _scheduler = scheduler; - } - - void _alarm_reset() { _alarm_assign(0, 0, false, 0), _active = 0, _next = 0; } - - bool _on_alarm(uint64_t); - - Alarm(Alarm const &); - Alarm &operator = (Alarm const &); - - public: - - Timeout_scheduler &timeout_scheduler; - Handler *handler = nullptr; - bool periodic = false; - - Alarm(Timeout_scheduler &timeout_scheduler) - : timeout_scheduler(timeout_scheduler) { _alarm_reset(); } - - virtual ~Alarm(); - - } _alarm; + Timeout &operator = (Timeout const &); public: - Timeout(Timeout_scheduler &timeout_scheduler) - : _alarm(timeout_scheduler) { } + Timeout(Timeout_scheduler &scheduler); - ~Timeout() { discard(); } + Timeout(Timer::Connection &timer_connection); - void schedule_periodic(Microseconds duration, Handler &handler); + ~Timeout(); - void schedule_one_shot(Microseconds duration, Handler &handler); + void schedule_periodic(Microseconds duration, + Timeout_handler &handler); + + void schedule_one_shot(Microseconds duration, + Timeout_handler &handler); void discard(); - bool scheduled() { return _alarm.handler != nullptr; } + bool scheduled(); }; /** - * Timeout-scheduler implementation using the Alarm framework + * Multiplexes one time source amongst different timeouts */ -class Genode::Alarm_timeout_scheduler : private Noncopyable, - public Timeout_scheduler, - public Time_source::Timeout_handler +class Genode::Timeout_scheduler : private Noncopyable, + public Timeout_handler { friend class Timer::Connection; friend class Timer::Root_component; - friend class Timeout::Alarm; + friend class Timeout; private: - using Alarm = Timeout::Alarm; + static constexpr uint64_t max_sleep_time_us { 60'000'000 }; - Time_source &_time_source; - Mutex _mutex { }; - Alarm *_active_head { nullptr }; - Alarm *_pending_head { nullptr }; - Alarm::Time _now { 0UL }; - bool _now_period { false }; - Alarm::Raw _min_handle_period { }; + Mutex _mutex { }; + Time_source &_time_source; + Microseconds const _max_sleep_time { min(_time_source.max_timeout().value, max_sleep_time_us) }; + List _timeouts { }; + Microseconds _current_time { 0 }; + bool _destructor_called { false }; + Microseconds _rate_limit_period; + Microseconds _rate_limit_deadline; - void _alarm_unsynchronized_enqueue(Alarm *alarm); + void _insert_into_timeouts_list(Timeout &timeout); - void _alarm_unsynchronized_dequeue(Alarm *alarm); + void _set_time_source_timeout(); - Alarm *_alarm_get_pending_alarm(); + void _set_time_source_timeout(uint64_t duration_us); - void _alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration); + void _schedule_timeout(Timeout &timeout, + Microseconds duration, + Microseconds period, + Timeout_handler &handler); + + void _discard_timeout_unsynchronized(Timeout &timeout); void _enable(); + void _schedule_one_shot_timeout(Timeout &timeout, + Microseconds duration, + Timeout_handler &handler); - /********************************** - ** Time_source::Timeout_handler ** - **********************************/ + void _schedule_periodic_timeout(Timeout &timeout, + Microseconds period, + Timeout_handler &handler); + + void _discard_timeout(Timeout &timeout); + + void _destruct_timeout(Timeout &timeout); + + Timeout_scheduler(Timeout_scheduler const &); + + Timeout_scheduler &operator = (Timeout_scheduler const &); + + + /********************* + ** Timeout_handler ** + *********************/ void handle_timeout(Duration curr_time) override; - - /*********************** - ** Timeout_scheduler ** - ***********************/ - - void _schedule_one_shot(Timeout &timeout, Microseconds duration) override; - void _schedule_periodic(Timeout &timeout, Microseconds duration) override; - - void _discard(Timeout &timeout) override { - _alarm_discard(&timeout._alarm); } - - void _alarm_discard(Alarm *alarm); - - void _alarm_schedule_absolute(Alarm *alarm, Alarm::Time duration); - - void _alarm_schedule(Alarm *alarm, Alarm::Time period); - - void _alarm_handle(Alarm::Time now); - - bool _alarm_next_deadline(Alarm::Time *deadline); - - bool _alarm_head_timeout(const Alarm * alarm) { return _active_head == alarm; } - - Alarm_timeout_scheduler(Alarm_timeout_scheduler const &); - Alarm_timeout_scheduler &operator = (Alarm_timeout_scheduler const &); - public: - Alarm_timeout_scheduler(Time_source &time_source, - Microseconds min_handle_period = Microseconds(1)); + Timeout_scheduler(Time_source &time_source, + Microseconds min_handle_period); - ~Alarm_timeout_scheduler(); + ~Timeout_scheduler(); - - /*********************** - ** Timeout_scheduler ** - ***********************/ - - Duration curr_time() override { return _time_source.curr_time(); } + Duration curr_time(); }; #endif /* _TIMER__TIMEOUT_H_ */ diff --git a/repos/base/include/timer_session/connection.h b/repos/base/include/timer_session/connection.h index 39b9586653..cf126ca64b 100644 --- a/repos/base/include/timer_session/connection.h +++ b/repos/base/include/timer_session/connection.h @@ -34,50 +34,42 @@ namespace Timer * Periodic timeout that is linked to a custom handler, scheduled when constructed */ template -struct Timer::Periodic_timeout : private Genode::Noncopyable +struct Timer::Periodic_timeout : private Genode::Noncopyable, + private Genode::Timeout_handler { private: using Duration = Genode::Duration; using Timeout = Genode::Timeout; - using Timeout_scheduler = Genode::Timeout_scheduler; using Microseconds = Genode::Microseconds; typedef void (HANDLER::*Handler_method)(Duration); - Timeout _timeout; - - struct Handler : Timeout::Handler - { - HANDLER &object; - Handler_method const method; - - Handler(HANDLER &object, Handler_method method) - : object(object), method(method) { } + Timeout _timeout; + HANDLER &_object; + Handler_method const _method; - /********************** - ** Timeout::Handler ** - **********************/ + /********************* + ** Timeout_handler ** + *********************/ - void handle_timeout(Duration curr_time) override { - (object.*method)(curr_time); } - - } _handler; + void handle_timeout(Duration curr_time) override { + (_object.*_method)(curr_time); } public: - Periodic_timeout(Timeout_scheduler &timeout_scheduler, - HANDLER &object, - Handler_method method, - Microseconds duration) + Periodic_timeout(Connection &timer, + HANDLER &object, + Handler_method method, + Microseconds duration) : - _timeout(timeout_scheduler), _handler(object, method) + _timeout { timer }, + _object { object }, + _method { method } { - _timeout.schedule_periodic(duration, _handler); + _timeout.schedule_periodic(duration, *this); } - - ~Periodic_timeout() { _timeout.discard(); } }; @@ -85,48 +77,42 @@ struct Timer::Periodic_timeout : private Genode::Noncopyable * One-shot timeout that is linked to a custom handler, scheduled manually */ template -class Timer::One_shot_timeout : private Genode::Noncopyable +class Timer::One_shot_timeout : private Genode::Noncopyable, + private Genode::Timeout_handler { private: using Duration = Genode::Duration; using Timeout = Genode::Timeout; - using Timeout_scheduler = Genode::Timeout_scheduler; using Microseconds = Genode::Microseconds; typedef void (HANDLER::*Handler_method)(Duration); - Timeout _timeout; - - struct Handler : Timeout::Handler - { - HANDLER &object; - Handler_method const method; - - Handler(HANDLER &object, Handler_method method) - : object(object), method(method) { } + Timeout _timeout; + HANDLER &_object; + Handler_method const _method; - /********************** - ** Timeout::Handler ** - **********************/ + /********************* + ** Timeout_handler ** + *********************/ - void handle_timeout(Duration curr_time) override { - (object.*method)(curr_time); } - - } _handler; + void handle_timeout(Duration curr_time) override { + (_object.*_method)(curr_time); } public: - One_shot_timeout(Timeout_scheduler &timeout_scheduler, - HANDLER &object, - Handler_method method) - : _timeout(timeout_scheduler), _handler(object, method) { } - - ~One_shot_timeout() { _timeout.discard(); } + One_shot_timeout(Connection &timer, + HANDLER &object, + Handler_method method) + : + _timeout { timer }, + _object { object }, + _method { method } + { } void schedule(Microseconds duration) { - _timeout.schedule_one_shot(duration, _handler); } + _timeout.schedule_one_shot(duration, *this); } void discard() { _timeout.discard(); } @@ -141,19 +127,21 @@ class Timer::One_shot_timeout : private Genode::Noncopyable */ class Timer::Connection : public Genode::Connection, public Session_client, - private Genode::Time_source, - public Genode::Timeout_scheduler + private Genode::Time_source { + friend class Genode::Timeout; + private: - using Timeout = Genode::Timeout; - using Timeout_handler = Genode::Time_source::Timeout_handler; - using Timestamp = Genode::Trace::Timestamp; - using Duration = Genode::Duration; - using Mutex = Genode::Mutex; - using Microseconds = Genode::Microseconds; - using Milliseconds = Genode::Milliseconds; - using Entrypoint = Genode::Entrypoint; + using Timeout = Genode::Timeout; + using Timeout_handler = Genode::Timeout_handler; + using Timestamp = Genode::Trace::Timestamp; + using Duration = Genode::Duration; + using Mutex = Genode::Mutex; + using Microseconds = Genode::Microseconds; + using Milliseconds = Genode::Milliseconds; + using Entrypoint = Genode::Entrypoint; + using Io_signal_handler = Genode::Io_signal_handler; /* * Noncopyable @@ -163,19 +151,34 @@ class Timer::Connection : public Genode::Connection, /* * The mode determines which interface of the timer connection is - * enabled. Initially, a timer connection is in LEGACY mode. When in - * MODERN mode, a call to the LEGACY interface causes an exception. - * When in LEGACY mode, a call to the MODERN interface causes a switch - * to the MODERN mode. The LEGACY interface is deprecated. Please - * prefer using the MODERN interface. + * enabled. Initially, a timer connection is in TIMER_SESSION mode. + * In this mode, the user can operate directly on the connection using + * the methods of the timer-session interface. As soon as the + * connection is handed over as argument to the constructor of a + * Periodic_timeout or a One_shot_timeout, it switches to + * TIMEOUT_FRAMEWORK mode. From this point on, the only method that + * the user can use directly on the connection is 'curr_time'. For + * the rest of the functionality he rather uses the interfaces of + * timeout objects that reference the connection. * - * LEGACY = Timer Session interface, blocking calls usleep and msleep - * MODERN = more precise curr_time, non-blocking and multiplexed - * handling with Periodic_timeout resp. One_shot_timeout + * These are the characteristics of the two modes: + * + * TIMER_SESSION: + * + * * Allows for both blocking and non-blocking timeout semantics. + * * Missing local interpolation leads to less precise curr_time + * results. + * * Only one timeout at a time per connection. + * + * TIMEOUT FRAMEWORK: + * + * * Supports only non-blocking timeout semantics. + * * More precise curr_time results through local interpolation. + * * Multiplexing of multiple timeouts at the same connection */ - enum Mode { LEGACY, MODERN }; + enum Mode { TIMER_SESSION, TIMEOUT_FRAMEWORK }; - Mode _mode { LEGACY }; + Mode _mode { TIMER_SESSION }; Mutex _mutex { }; Genode::Signal_receiver _sig_rec { }; Genode::Signal_context _default_sigh_ctx { }; @@ -185,17 +188,14 @@ class Timer::Connection : public Genode::Connection, Genode::Signal_context_capability _custom_sigh_cap { }; - void _enable_modern_mode(); - void _sigh(Signal_context_capability sigh) { Session_client::sigh(sigh); } - - /************************* - ** Time_source helpers ** - *************************/ + /**************************************************** + ** Members for interaction with Timeout framework ** + ****************************************************/ enum { MIN_TIMEOUT_US = 5000 }; enum { REAL_TIME_UPDATE_PERIOD_US = 500000 }; @@ -205,17 +205,19 @@ class Timer::Connection : public Genode::Connection, enum { NR_OF_INITIAL_CALIBRATIONS = 3 * MAX_INTERPOLATION_QUALITY }; enum { MIN_FACTOR_LOG2 = 8 }; - Genode::Io_signal_handler _signal_handler; + Io_signal_handler _signal_handler; + Timeout_handler *_handler { nullptr }; + Mutex _real_time_mutex { }; + uint64_t _us { elapsed_us() }; + Timestamp _ts { _timestamp() }; + Duration _real_time { Microseconds { _us } }; + Duration _interpolated_time { _real_time }; + unsigned _interpolation_quality { 0 }; + uint64_t _us_to_ts_factor { 1 }; + unsigned _us_to_ts_factor_shift { 0 }; + Genode::Timeout_scheduler _timeout_scheduler { *this, Microseconds { 1 } }; - Timeout_handler *_handler { nullptr }; - Mutex _real_time_mutex { }; - uint64_t _us { elapsed_us() }; - Timestamp _ts { _timestamp() }; - Duration _real_time { Microseconds(_us) }; - Duration _interpolated_time { _real_time }; - unsigned _interpolation_quality { 0 }; - uint64_t _us_to_ts_factor { 1UL }; - unsigned _us_to_ts_factor_shift { 0 }; + Genode::Timeout_scheduler &_switch_to_timeout_framework_mode(); Timestamp _timestamp(); @@ -237,28 +239,12 @@ class Timer::Connection : public Genode::Connection, ** Time_source ** *****************/ - void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; + void set_timeout(Microseconds duration, Timeout_handler &handler) override; Microseconds max_timeout() const override { return Microseconds(REAL_TIME_UPDATE_PERIOD_US); } - - /******************************* - ** Timeout_scheduler helpers ** - *******************************/ - - Genode::Alarm_timeout_scheduler _scheduler { *this }; - - - /*********************** - ** Timeout_scheduler ** - ***********************/ - - void _schedule_one_shot(Timeout &timeout, Microseconds duration) override; - void _schedule_periodic(Timeout &timeout, Microseconds duration) override; - void _discard(Timeout &timeout) override; - public: - struct Cannot_use_both_legacy_and_modern_interface : Genode::Exception { }; + struct Method_cannot_be_used_in_timeout_framework_mode : Genode::Exception { }; /** * Constructor @@ -287,8 +273,8 @@ class Timer::Connection : public Genode::Connection, */ void sigh(Signal_context_capability sigh) override { - if (_mode == MODERN) { - throw Cannot_use_both_legacy_and_modern_interface(); + if (_mode == TIMEOUT_FRAMEWORK) { + throw Method_cannot_be_used_in_timeout_framework_mode(); } _custom_sigh_cap = sigh; Session_client::sigh(_custom_sigh_cap); @@ -302,8 +288,8 @@ class Timer::Connection : public Genode::Connection, */ void usleep(uint64_t us) override { - if (_mode == MODERN) { - throw Cannot_use_both_legacy_and_modern_interface(); + if (_mode == TIMEOUT_FRAMEWORK) { + throw Method_cannot_be_used_in_timeout_framework_mode(); } /* * Omit the interaction with the timer driver for the corner case @@ -340,16 +326,16 @@ class Timer::Connection : public Genode::Connection, */ void msleep(uint64_t ms) override { - if (_mode == MODERN) { - throw Cannot_use_both_legacy_and_modern_interface(); + if (_mode == TIMEOUT_FRAMEWORK) { + throw Method_cannot_be_used_in_timeout_framework_mode(); } usleep(1000*ms); } - /*********************************** - ** Timeout_scheduler/Time_source ** - ***********************************/ + /***************** + ** Time_source ** + *****************/ Duration curr_time() override; }; diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 459a9a562c..e9e6fca32e 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -41,10 +41,7 @@ _Z11genode_exiti T _Z16main_thread_utcbv T _Z22__ldso_raise_exceptionv T -_ZN5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T -_ZN5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T -_ZN5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T -_ZN5Timer10Connection8_discardERN6Genode7TimeoutE T +_ZN5Timer10Connection11set_timeoutEN6Genode12MicrosecondsERNS1_15Timeout_handlerE T _ZN5Timer10Connection9curr_timeEv T _ZN5Timer10ConnectionC1ERN6Genode3EnvEPKc T _ZN5Timer10ConnectionC1ERN6Genode3EnvERNS1_10EntrypointEPKc T @@ -152,6 +149,16 @@ _ZN6Genode17Rm_session_client6createEm T _ZN6Genode17Rm_session_client7destroyENS_10CapabilityINS_10Region_mapEEE T _ZN6Genode17Rm_session_clientC1ENS_10CapabilityINS_10Rm_sessionEEE T _ZN6Genode17Rm_session_clientC2ENS_10CapabilityINS_10Rm_sessionEEE T +_ZN6Genode17Timeout_scheduler14handle_timeoutENS_8DurationE T +_ZN6Genode17Timeout_scheduler18_schedule_one_shotERNS_7TimeoutENS_12MicrosecondsE T +_ZN6Genode17Timeout_scheduler18_schedule_periodicERNS_7TimeoutENS_12MicrosecondsE T +_ZN6Genode17Timeout_scheduler7_enableEv T +_ZN6Genode17Timeout_scheduler9curr_timeEv T +_ZN6Genode17Timeout_schedulerC1ERNS_11Time_sourceENS_12MicrosecondsE T +_ZN6Genode17Timeout_schedulerC2ERNS_11Time_sourceENS_12MicrosecondsE T +_ZN6Genode17Timeout_schedulerD0Ev T +_ZN6Genode17Timeout_schedulerD1Ev T +_ZN6Genode17Timeout_schedulerD2Ev T _ZN6Genode17Vm_session_client11create_vcpuERNS_9AllocatorERNS_3EnvERNS_15Vm_handler_baseE T _ZN6Genode17Vm_session_client3runENS_10Vm_session7Vcpu_idE T _ZN6Genode17Vm_session_client5pauseENS_10Vm_session7Vcpu_idE T @@ -180,15 +187,6 @@ _ZN6Genode18Signal_transmitterC1ENS_10CapabilityINS_14Signal_contextEEE T _ZN6Genode18Signal_transmitterC2ENS_10CapabilityINS_14Signal_contextEEE T _ZN6Genode18server_socket_pairEv T _ZN6Genode20env_session_id_spaceEv T -_ZN6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T -_ZN6Genode23Alarm_timeout_scheduler18_schedule_one_shotERNS_7TimeoutENS_12MicrosecondsE T -_ZN6Genode23Alarm_timeout_scheduler18_schedule_periodicERNS_7TimeoutENS_12MicrosecondsE T -_ZN6Genode23Alarm_timeout_scheduler7_enableEv T -_ZN6Genode23Alarm_timeout_schedulerC1ERNS_11Time_sourceENS_12MicrosecondsE T -_ZN6Genode23Alarm_timeout_schedulerC2ERNS_11Time_sourceENS_12MicrosecondsE T -_ZN6Genode23Alarm_timeout_schedulerD0Ev T -_ZN6Genode23Alarm_timeout_schedulerD1Ev T -_ZN6Genode23Alarm_timeout_schedulerD2Ev T _ZN6Genode25env_stack_area_region_mapE B 8 _ZN6Genode28env_stack_area_ram_allocatorE B 8 _ZN6Genode3Log3logEv T @@ -304,12 +302,19 @@ _ZN6Genode7Console7vprintfEPKcP13__va_list_tag T _ZN6Genode7Console7vprintfEPKcPc T _ZN6Genode7Console7vprintfEPKcPv T _ZN6Genode7Console7vprintfEPKcSt9__va_list T -_ZN6Genode7Timeout17schedule_one_shotENS_12MicrosecondsERNS0_7HandlerE T -_ZN6Genode7Timeout17schedule_periodicENS_12MicrosecondsERNS0_7HandlerE T -_ZN6Genode7Timeout5AlarmD0Ev T -_ZN6Genode7Timeout5AlarmD1Ev T -_ZN6Genode7Timeout5AlarmD2Ev T +_ZN6Genode7Timeout14_alarm_discardEv T +_ZN6Genode7Timeout17schedule_one_shotENS_12MicrosecondsERNS_15Timeout_handlerE T +_ZN6Genode7Timeout17schedule_periodicENS_12MicrosecondsERNS_15Timeout_handlerE T +_ZN6Genode7Timeout3RawC1Ev T +_ZN6Genode7Timeout3RawC2Ev T _ZN6Genode7Timeout7discardEv T +_ZN6Genode7Timeout9scheduledEv T +_ZN6Genode7TimeoutC1ERN5Timer10ConnectionE T +_ZN6Genode7TimeoutC1ERNS_17Timeout_schedulerE T +_ZN6Genode7TimeoutC2ERN5Timer10ConnectionE T +_ZN6Genode7TimeoutC2ERNS_17Timeout_schedulerE T +_ZN6Genode7TimeoutD1Ev T +_ZN6Genode7TimeoutD2Ev T _ZN6Genode7cap_mapEv T _ZN6Genode8Duration3addENS_12MicrosecondsE T _ZN6Genode8Duration3addENS_12MillisecondsE T @@ -348,8 +353,8 @@ _ZTIN6Genode14Rpc_entrypointE D 56 _ZTIN6Genode14Signal_contextE D 56 _ZTIN6Genode17Region_map_clientE D 24 _ZTIN6Genode17Rm_session_clientE D 24 +_ZTIN6Genode17Timeout_schedulerE D 72 _ZTIN6Genode18Allocator_avl_baseE D 24 -_ZTIN6Genode23Alarm_timeout_schedulerE D 72 _ZTIN6Genode4HeapE D 24 _ZTIN6Genode4SlabE D 24 _ZTIN6Genode5Child14Initial_threadE D 24 @@ -357,15 +362,14 @@ _ZTIN6Genode5ChildE D 72 _ZTIN6Genode6OutputE D 24 _ZTIN6Genode6ThreadE D 16 _ZTIN6Genode7ConsoleE D 16 -_ZTIN6Genode7Timeout5AlarmE D 16 _ZTSN5Timer10ConnectionE R 21 _ZTSN6Genode11Sliced_heapE R 23 _ZTSN6Genode14Rpc_entrypointE R 26 _ZTSN6Genode14Signal_contextE R 26 _ZTSN6Genode17Region_map_clientE R 29 _ZTSN6Genode17Rm_session_clientE R 29 +_ZTSN6Genode17Timeout_schedulerE R 35 _ZTSN6Genode18Allocator_avl_baseE R 30 -_ZTSN6Genode23Alarm_timeout_schedulerE R 35 _ZTSN6Genode4HeapE R 15 _ZTSN6Genode4SlabE R 15 _ZTSN6Genode5Child14Initial_threadE R 32 @@ -373,7 +377,6 @@ _ZTSN6Genode5ChildE R 16 _ZTSN6Genode6OutputE R 17 _ZTSN6Genode6ThreadE R 17 _ZTSN6Genode7ConsoleE R 18 -_ZTSN6Genode7Timeout5AlarmE R 24 _ZTVN5Timer10ConnectionE D 320 _ZTVN6Genode10Vm_sessionE D 56 _ZTVN6Genode11Sliced_heapE D 72 @@ -381,8 +384,8 @@ _ZTVN6Genode14Rpc_entrypointE D 80 _ZTVN6Genode14Signal_contextE D 32 _ZTVN6Genode17Region_map_clientE D 72 _ZTVN6Genode17Rm_session_clientE D 48 +_ZTVN6Genode17Timeout_schedulerE D 112 _ZTVN6Genode18Allocator_avl_baseE D 128 -_ZTVN6Genode23Alarm_timeout_schedulerE D 112 _ZTVN6Genode4HeapE D 72 _ZTVN6Genode4SlabE D 72 _ZTVN6Genode5Child14Initial_threadE D 48 @@ -390,23 +393,13 @@ _ZTVN6Genode5ChildE D 440 _ZTVN6Genode6OutputE D 48 _ZTVN6Genode6ThreadE D 48 _ZTVN6Genode7ConsoleE D 48 -_ZTVN6Genode7Timeout5AlarmE D 32 -_ZThn236_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T +_ZThn236_N5Timer10Connection11set_timeoutEN6Genode12MicrosecondsERNS1_15Timeout_handlerE T _ZThn236_N5Timer10Connection9curr_timeEv T -_ZThn240_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T -_ZThn240_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T -_ZThn240_N5Timer10Connection8_discardERN6Genode7TimeoutE T -_ZThn240_N5Timer10Connection9curr_timeEv T -_ZThn288_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T +_ZThn288_N5Timer10Connection11set_timeoutEN6Genode12MicrosecondsERNS1_15Timeout_handlerE T _ZThn288_N5Timer10Connection9curr_timeEv T -_ZThn296_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T -_ZThn296_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T -_ZThn296_N5Timer10Connection8_discardERN6Genode7TimeoutE T -_ZThn296_N5Timer10Connection9curr_timeEv T -_ZThn4_N6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T -_ZThn8_N6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T -_ZThn8_N6Genode23Alarm_timeout_schedulerD0Ev T -_ZThn8_N6Genode23Alarm_timeout_schedulerD1Ev T +_ZThn8_N6Genode17Timeout_scheduler14handle_timeoutENS_8DurationE T +_ZThn8_N6Genode17Timeout_schedulerD0Ev T +_ZThn8_N6Genode17Timeout_schedulerD1Ev T _ZdlPvPN6Genode11DeallocatorE T _ZdlPvPN6Genode9AllocatorE W _ZdlPvRN6Genode11DeallocatorE T diff --git a/repos/base/src/lib/timeout/timeout.cc b/repos/base/src/lib/timeout/timeout.cc index feea5d6b7e..2edda0cbf5 100644 --- a/repos/base/src/lib/timeout/timeout.cc +++ b/repos/base/src/lib/timeout/timeout.cc @@ -13,6 +13,7 @@ /* Genode includes */ #include +#include using namespace Genode; @@ -21,419 +22,386 @@ using namespace Genode; ** Timeout ** *************/ -void Timeout::schedule_periodic(Microseconds duration, Handler &handler) +void Timeout::schedule_periodic(Microseconds duration, + Timeout_handler &handler) { - _alarm.handler = &handler; - _alarm.periodic = true; - _alarm.timeout_scheduler._schedule_periodic(*this, duration); + _scheduler._schedule_periodic_timeout(*this, duration, handler); } -void Timeout::schedule_one_shot(Microseconds duration, Handler &handler) +void Timeout::schedule_one_shot(Microseconds duration, + Timeout_handler &handler) { - _alarm.handler = &handler; - _alarm.periodic = false; - _alarm.timeout_scheduler._schedule_one_shot(*this, duration); + _scheduler._schedule_one_shot_timeout(*this, duration, handler); } -void Timeout::discard() -{ - _alarm.timeout_scheduler._discard(*this); - _alarm.handler = nullptr; -} - - -/******************** - ** Timeout::Alarm ** - ********************/ - -bool Timeout::Alarm::_on_alarm(uint64_t) -{ - if (handler) { - Handler *current = handler; - if (!periodic) { - handler = nullptr; - } - current->handle_timeout(timeout_scheduler.curr_time()); - } - return periodic; -} - - -Timeout::Alarm::~Alarm() -{ - if (_scheduler) - _scheduler->_alarm_discard(this); -} - - -bool Timeout::Alarm::Raw::is_pending_at(uint64_t time, bool time_period) const -{ - return (time_period == deadline_period && - time >= deadline) || - (time_period != deadline_period && - time < deadline); -} - - -/***************************** - ** Alarm_timeout_scheduler ** - *****************************/ - -void Alarm_timeout_scheduler::handle_timeout(Duration duration) -{ - uint64_t const curr_time_us = duration.trunc_to_plain_us().value; - - _alarm_handle(curr_time_us); - - /* sleep time is either until the next deadline or the maximum timout */ - uint64_t sleep_time_us; - Alarm::Time deadline_us; - if (_alarm_next_deadline(&deadline_us)) { - sleep_time_us = deadline_us - curr_time_us; - } else { - sleep_time_us = _time_source.max_timeout().value; } - - /* limit max timeout to a more reasonable value, e.g. 60s */ - if (sleep_time_us > 60000000) { - sleep_time_us = 60000000; - } else if (sleep_time_us == 0) { - sleep_time_us = 1; } - - _time_source.schedule_timeout(Microseconds(sleep_time_us), *this); -} - - -Alarm_timeout_scheduler::Alarm_timeout_scheduler(Time_source &time_source, - Microseconds min_handle_period) +Timeout::Timeout(Timeout_scheduler &scheduler) : - _time_source(time_source) + _scheduler(scheduler) +{ } + + +Timeout::Timeout(Timer::Connection &timer_connection) +: + _scheduler(timer_connection._switch_to_timeout_framework_mode()) +{ } + + +Timeout::~Timeout() { _scheduler._destruct_timeout(*this); } + +void Timeout::discard() { _scheduler._discard_timeout(*this); } + +bool Timeout::scheduled() { return _handler != nullptr; } + + +/*********************** + ** Timeout_scheduler ** + ***********************/ + +void Timeout_scheduler::handle_timeout(Duration curr_time) { - Alarm::Time const deadline = _now + min_handle_period.value; - _min_handle_period.period = min_handle_period.value; - _min_handle_period.deadline = deadline; - _min_handle_period.deadline_period = _now > deadline ? - !_now_period : _now_period; -} - - -Alarm_timeout_scheduler::~Alarm_timeout_scheduler() -{ - Mutex::Guard mutex_guard(_mutex); - while (_active_head) { - Alarm *next = _active_head->_next; - _active_head->_alarm_reset(); - _active_head = next; - } -} - - -void Alarm_timeout_scheduler::_enable() -{ - _time_source.schedule_timeout(Microseconds(0), *this); -} - - -void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout, - Microseconds duration) -{ - /* raise timeout duration by the age of the local time value */ - uint64_t us = _time_source.curr_time().trunc_to_plain_us().value; - if (us >= _now) { - us = duration.value + (us - _now); } - else { - us = duration.value + (~0UL - _now) + us; } - if (us >= duration.value) { - duration.value = us; } - - /* insert timeout into scheduling queue */ - _alarm_schedule_absolute(&timeout._alarm, duration.value); - - /* if new timeout is the closest to now, update the time-source timeout */ - if (_alarm_head_timeout(&timeout._alarm)) { - _time_source.schedule_timeout(Microseconds(0), *this); } -} - - -void Alarm_timeout_scheduler::_schedule_periodic(Timeout &timeout, - Microseconds duration) -{ - _alarm_schedule(&timeout._alarm, duration.value); - - if (_alarm_head_timeout(&timeout._alarm)) { - _time_source.schedule_timeout(Microseconds(0), *this); } -} - - -void Alarm_timeout_scheduler::_alarm_unsynchronized_enqueue(Alarm *alarm) -{ - if (alarm->_active) { - error("trying to insert the same alarm twice!"); - return; - } - - alarm->_active++; - - /* if active alarm list is empty add first element */ - if (!_active_head) { - alarm->_next = 0; - _active_head = alarm; - return; - } - - /* if deadline is smaller than any other deadline, put it on the head */ - if (alarm->_raw.is_pending_at(_active_head->_raw.deadline, _active_head->_raw.deadline_period)) { - alarm->_next = _active_head; - _active_head = alarm; - return; - } - - /* find list element with a higher deadline */ - Alarm *curr = _active_head; - while (curr->_next && - curr->_next->_raw.is_pending_at(alarm->_raw.deadline, alarm->_raw.deadline_period)) + List > pending_timeouts { }; { - curr = curr->_next; - } + /* acquire scheduler and update stored current time */ + Mutex::Guard const scheduler_guard(_mutex); + if (_destructor_called) { + return; + } + _current_time = curr_time.trunc_to_plain_us(); - /* if end of list is reached, append new element */ - if (curr->_next == 0) { - curr->_next = alarm; - return; - } + /* apply rate limit to the handling of timeouts */ + if (_current_time.value < _rate_limit_deadline.value) { - /* insert element in middle of list */ - alarm->_next = curr->_next; - curr->_next = alarm; -} + _time_source.set_timeout( + Microseconds { _rate_limit_deadline.value - + _current_time.value }, + *this); - -void Alarm_timeout_scheduler::_alarm_unsynchronized_dequeue(Alarm *alarm) -{ - if (!_active_head) return; - - if (_active_head == alarm) { - _active_head = alarm->_next; - alarm->_alarm_reset(); - return; - } - - /* find predecessor in alarm queue */ - Alarm *curr; - for (curr = _active_head; curr && (curr->_next != alarm); curr = curr->_next); - - /* alarm is not enqueued */ - if (!curr) return; - - /* remove alarm from alarm queue */ - curr->_next = alarm->_next; - alarm->_alarm_reset(); -} - - -Timeout::Alarm *Alarm_timeout_scheduler::_alarm_get_pending_alarm() -{ - Mutex::Guard mutex_guard(_mutex); - - do { - if (!_active_head || !_active_head->_raw.is_pending_at(_now, _now_period)) { - return nullptr; } - - /* remove alarm from head of the list */ - Alarm *pending_alarm = _active_head; - _active_head = _active_head->_next; + return; + } + _rate_limit_deadline.value = _current_time.value + + _rate_limit_period.value; /* - * Acquire dispatch mutex to defer destruction until the call of '_on_alarm' - * is finished + * Filter out all pending timeouts to a local list first. The + * processing of pending timeouts can have effects on the '_timeouts' + * list and these would interfere with the filtering if we would do + * it all in the same loop. */ - pending_alarm->_dispatch_mutex.acquire(); + while (Timeout *timeout = _timeouts.first()) { - /* reset alarm object */ - pending_alarm->_next = nullptr; - pending_alarm->_active--; - - if (pending_alarm->_delete) { - pending_alarm->_dispatch_mutex.release(); - continue; + timeout->_mutex.acquire(); + if (timeout->_deadline.value > _current_time.value) { + timeout->_mutex.release(); + break; + } + _timeouts.remove(timeout); + pending_timeouts.insert(&timeout->_pending_timeouts_le); } - return pending_alarm; - } while (true); + /* + * Do the framework-internal processing of the pending timeouts and + * then release their mutexes. + */ + for (List_element const *elem { pending_timeouts.first() }; + elem != nullptr; + elem = elem->next()) { + + Timeout &timeout { *elem->object() }; + if (!timeout._in_discard_blockade) { + + /* + * Remember the handler in an extra member that is altered + * only by this code path. This enables us to release the + * mutexes of all pending timeouts before starting to call + * the timeout handlers. This is necessary to prevent + * deadlocks in a situation where multiple timeouts become + * pending at once, the handler of the first pending + * timeout is about to re-schedule his timeout, and then + * a second thread calls 'discard' on another pending + * timeout just before that handlers call to 'schedule'. + */ + timeout._pending_handler = timeout._handler; + + } else { + + /* + * Another thread, that wants to discard the timeout, has been + * waiting for a prior call to the timeout handler to finish. + * It has already been unblocked again but couldn't continue + * discarding the timeout yet. Therefore, we refrain from + * calling the timeout handler again until the other thread + * could complete its task. + */ + pending_timeouts.remove(elem); + } + if (timeout._period.value == 0) { + + /* discard one-shot timeouts */ + timeout._handler = nullptr; + + } else { + + /* determine new timeout deadline */ + uint64_t const nr_of_periods { + ((_current_time.value - timeout._deadline.value) / + timeout._period.value) + 1 }; + + uint64_t deadline_us { timeout._deadline.value + + nr_of_periods * timeout._period.value }; + + if (deadline_us < _current_time.value) { + deadline_us = ~(uint64_t)0; + } + /* re-insert timeout into timeouts list */ + timeout._deadline = Microseconds { deadline_us }; + _insert_into_timeouts_list(timeout); + } + timeout._mutex.release(); + } + _set_time_source_timeout(); + } + /* call the handler of each pending timeout */ + while (List_element const *elem = pending_timeouts.first()) { + + Timeout &timeout { *elem->object() }; + pending_timeouts.remove(elem); + + /* + * Timeout handlers are called without holding any timeout mutex or + * the scheduler mutex. This ensures that the handler can, + * for instance, re-schedule the timeout without running into a + * deadlock. The only thing we synchronize is discarding the + * timeout. As long as the timeout's '_pending_handler' is set, + * a thread that wants to discard the timeout will block at the + * timeout's '_discard_blockade'. + */ + timeout._pending_handler->handle_timeout(curr_time); + + /* + * Unset the timeout's '_pending_handler' again. While the timeout + * handler was running, another thread might have tried to discard + * the timeout and got blocked at the timeout's '_discard_blockade'. + * If this is the case, we have to unblock the other thread. + */ + Mutex::Guard timeout_guard(timeout._mutex); + timeout._pending_handler = nullptr; + if (timeout._in_discard_blockade) { + timeout._discard_blockade.wakeup(); + } + } } -void Alarm_timeout_scheduler::_alarm_handle(Alarm::Time curr_time) +Timeout_scheduler::Timeout_scheduler(Time_source &time_source, + Microseconds rate_limit_period) +: + _time_source { time_source }, + _rate_limit_period { rate_limit_period }, + _rate_limit_deadline { Microseconds { _current_time.value + + rate_limit_period.value } } +{ } + + +Timeout_scheduler::~Timeout_scheduler() { /* - * Raise the time counter and if it wraps, update also in which - * period of the time counter we are. + * Acquire the scheduler mutex and don't release it at the end of this + * function to ease debugging in case that someone accesses a dangling + * scheduler pointer. */ - if (_now > curr_time) { - _now_period = !_now_period; - } - _now = curr_time; + _mutex.acquire(); - if (!_min_handle_period.is_pending_at(_now, _now_period)) { + /* + * The function 'Timeout_scheduler::_discard_timeout_unsynchronized' may + * have to release and re-acquire the scheduler mutex due to pending + * timeout handlers. But, nonetheless, we don't want others to schedule + * or discard timeouts while we are emptying the timeout list. Setting + * the flag '_destructor_called' causes such attempts to finish without + * effect. + */ + _destructor_called = true; + + /* discard all scheduled timeouts */ + while (Timeout *timeout = _timeouts.first()) { + Mutex::Guard const timeout_guard { timeout->_mutex }; + _discard_timeout_unsynchronized(*timeout); + } +} + + +void Timeout_scheduler::_enable() +{ + Mutex::Guard const scheduler_guard { _mutex }; + if (_destructor_called) { return; } - Alarm::Time const deadline = _now + _min_handle_period.period; - _min_handle_period.deadline = deadline; - _min_handle_period.deadline_period = _now > deadline ? - !_now_period : _now_period; + _set_time_source_timeout(); +} - /* - * Dequeue all pending alarms before starting to re-schedule. Otherwise, - * a long-lasting alarm that has a deadline in the next now_period might - * get scheduled as head of this now_period falsely because the code - * thinks that it belongs to the last now_period. - */ - while (Alarm *curr = _alarm_get_pending_alarm()) { - /* enqueue alarm into list of pending alarms */ - curr->_next = _pending_head; - _pending_head = curr; +void Timeout_scheduler::_set_time_source_timeout() +{ + _set_time_source_timeout( + _timeouts.first() ? + _timeouts.first()->_deadline.value - _current_time.value : + ~(uint64_t)0); +} + + +void Timeout_scheduler::_set_time_source_timeout(uint64_t duration_us) +{ + if (duration_us < _rate_limit_period.value) { + duration_us = _rate_limit_period.value; } - while (Alarm *curr = _pending_head) { - - /* dequeue alarm from list of pending alarms */ - _pending_head = _pending_head->_next; - curr->_next = nullptr; - - uint64_t triggered = 1; - - if (curr->_raw.period) { - Alarm::Time deadline = curr->_raw.deadline; - - /* schedule next event */ - if (deadline == 0) - deadline = curr_time; - - triggered += (curr_time - deadline) / curr->_raw.period; - } - - /* do not reschedule if alarm function returns 0 */ - bool reschedule = curr->_on_alarm(triggered); - - if (reschedule) { - - /* - * At this point, the alarm deadline normally is somewhere near - * the current time but If the alarm had no deadline by now, - * initialize it with the current time. - */ - if (curr->_raw.deadline == 0) { - curr->_raw.deadline = _now; - curr->_raw.deadline_period = _now_period; - } - /* - * Raise the deadline value by one period of the alarm and - * if the deadline value wraps thereby, update also in which - * period it is located. - */ - Alarm::Time const deadline = curr->_raw.deadline + - triggered * curr->_raw.period; - if (curr->_raw.deadline > deadline) { - curr->_raw.deadline_period = !curr->_raw.deadline_period; - } - curr->_raw.deadline = deadline; - - /* synchronize enqueue operation */ - Mutex::Guard mutex_guard(_mutex); - _alarm_unsynchronized_enqueue(curr); - } - - /* release alarm, resume concurrent destructor operation */ - curr->_dispatch_mutex.release(); + if (duration_us > _max_sleep_time.value) { + duration_us = _max_sleep_time.value; } + _time_source.set_timeout(Microseconds(duration_us), *this); } -void Alarm_timeout_scheduler::_alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration) +void Timeout_scheduler::_schedule_one_shot_timeout(Timeout &timeout, + Microseconds duration, + Timeout_handler &handler) { - /* - * If the alarm is already present in the queue, re-consider its queue - * position because its deadline might have changed. I.e., if an alarm is - * rescheduled with a new timeout before the original timeout triggered. - */ - if (alarm._active) - _alarm_unsynchronized_dequeue(&alarm); - - Alarm::Time deadline = _now + first_duration; - alarm._alarm_assign(period, deadline, _now > deadline ? !_now_period : _now_period, this); - - _alarm_unsynchronized_enqueue(&alarm); + _schedule_timeout(timeout, duration, Microseconds { 0 }, handler); } -void Alarm_timeout_scheduler::_alarm_schedule_absolute(Alarm *alarm, Alarm::Time duration) +void Timeout_scheduler::_schedule_periodic_timeout(Timeout &timeout, + Microseconds period, + Timeout_handler &handler) { - Mutex::Guard alarm_list_guard(_mutex); - _alarm_setup_alarm(*alarm, 0, duration); + /* prevent using a period of 0 */ + if (period.value == 0) { + period.value = 1; + } + _schedule_timeout(timeout, Microseconds { 0 }, period, handler); } -void Alarm_timeout_scheduler::_alarm_schedule(Alarm *alarm, Alarm::Time period) +void Timeout_scheduler::_schedule_timeout(Timeout &timeout, + Microseconds duration, + Microseconds period, + Timeout_handler &handler) { - Mutex::Guard alarm_list_guard(_mutex); - - /* - * Refuse to schedule a periodic timeout of 0 because it would trigger - * infinitely in the 'handle' function. To account for the case where the - * alarm object was already scheduled, we make sure to remove it from the - * queue. - */ - if (period == 0) { - _alarm_unsynchronized_dequeue(alarm); + /* acquire scheduler and timeout mutex */ + Mutex::Guard const scheduler_guard { _mutex }; + if (_destructor_called) { return; } + Mutex::Guard const timeout_guard(timeout._mutex); - /* first deadline is overdue */ - _alarm_setup_alarm(*alarm, period, 0); -} + /* prevent inserting a timeout twice */ + if (timeout._handler != nullptr) { + _timeouts.remove(&timeout); + } + /* determine timeout deadline */ + uint64_t const curr_time_us { + _time_source.curr_time().trunc_to_plain_us().value }; + uint64_t const deadline_us { + duration.value <= ~(uint64_t)0 - curr_time_us ? + curr_time_us + duration.value : ~(uint64_t)0 }; + + /* set up timeout object and insert into timeouts list */ + timeout._handler = &handler; + timeout._deadline = Microseconds { deadline_us }; + timeout._period = period; + _insert_into_timeouts_list(timeout); -void Alarm_timeout_scheduler::_alarm_discard(Alarm *alarm) -{ /* - * Make sure that nobody is inside the '_alarm_get_pending_alarm' when - * grabbing the '_dispatch_mutex'. This is important when this function - * is called from the 'Alarm' destructor. Without the '_dispatch_mutex', - * we could take the mutex and proceed with destruction just before - * '_alarm_get_pending_alarm' tries to grab the mutex. When the destructor - * is finished, '_alarm_get_pending_alarm' would proceed with operating on - * a dangling pointer. + * If the new timeout is the first to trigger, we have to update the + * time-source timeout. */ - if (alarm) { - { - /* inform that this object is going to be deleted */ - Mutex::Guard alarm_guard(alarm->_dispatch_mutex); - alarm->_delete = true; - } - { - Mutex::Guard alarm_list_guard(_mutex); - _alarm_unsynchronized_dequeue(alarm); - } - - /* get anyone using this out of '_alarm_get_pending_alarm'() finally */ - Mutex::Guard alarm_guard(alarm->_dispatch_mutex); - alarm->_delete = false; + if (_timeouts.first() == &timeout) { + _set_time_source_timeout(deadline_us - curr_time_us); } } -bool Alarm_timeout_scheduler::_alarm_next_deadline(Alarm::Time *deadline) +void Timeout_scheduler::_insert_into_timeouts_list(Timeout &timeout) { - Mutex::Guard alarm_list_guard(_mutex); - - if (!_active_head) return false; - - if (deadline) - *deadline = _active_head->_raw.deadline; - - if (*deadline < _min_handle_period.deadline) { - *deadline = _min_handle_period.deadline; + /* if timeout list is empty, insert as first element */ + if (_timeouts.first() == nullptr) { + _timeouts.insert(&timeout); + return; } - return true; + /* if timeout has the shortest deadline, insert as first element */ + if (_timeouts.first()->_deadline.value >= timeout._deadline.value) { + _timeouts.insert(&timeout); + return; + } + /* find list element with next shorter deadline and insert behind it */ + Timeout *curr_timeout { _timeouts.first() }; + for (; + curr_timeout->next() != nullptr && + curr_timeout->next()->_deadline.value < timeout._deadline.value; + curr_timeout = curr_timeout->_next); + + _timeouts.insert(&timeout, curr_timeout); +} + + +void Timeout_scheduler::_discard_timeout(Timeout &timeout) +{ + Mutex::Guard const scheduler_mutex { _mutex }; + Mutex::Guard const timeout_mutex { timeout._mutex }; + _discard_timeout_unsynchronized(timeout); +} + + +void Timeout_scheduler::_destruct_timeout(Timeout &timeout) +{ + Mutex::Guard const scheduler_mutex { _mutex }; + + /* + * Acquire the timeout mutex and don't release it at the end of this + * function to ease debugging in case that someone accesses a dangling + * timeout pointer. + */ + timeout._mutex.acquire(); + _discard_timeout_unsynchronized(timeout); +} + + +void Timeout_scheduler::_discard_timeout_unsynchronized(Timeout &timeout) +{ + if (timeout._pending_handler != nullptr) { + + if (timeout._in_discard_blockade) { + error("timeout is getting discarded by multiple threads"); + } + + /* + * We cannot discard a timeout whose handler is currently executed. We + * rather set its flag '_in_discard_blockade' (this ensures that the + * timeout handler is not getting called again) and then wait for the + * current handler call to finish. 'Timeout_scheduler::handle_timeout' + * will wake us up as soon as the handler returned. + */ + timeout._in_discard_blockade = true; + timeout._mutex.release(); + _mutex.release(); + + timeout._discard_blockade.block(); + + _mutex.acquire(); + timeout._mutex.acquire(); + timeout._in_discard_blockade = false; + } + _timeouts.remove(&timeout); + timeout._handler = nullptr; +} + + +Duration Timeout_scheduler::curr_time() +{ + Mutex::Guard const scheduler_guard { _mutex }; + if (_destructor_called) { + return Duration { Microseconds { 0 } }; + } + return _time_source.curr_time(); } diff --git a/repos/base/src/lib/timeout/timer_connection.cc b/repos/base/src/lib/timeout/timer_connection.cc index 6fb791653b..46e3082cd6 100644 --- a/repos/base/src/lib/timeout/timer_connection.cc +++ b/repos/base/src/lib/timeout/timer_connection.cc @@ -97,8 +97,8 @@ void Timer::Connection::_handle_timeout() } -void Timer::Connection::schedule_timeout(Microseconds duration, - Timeout_handler &handler) +void Timer::Connection::set_timeout(Microseconds duration, + Timeout_handler &handler) { if (duration.value < MIN_TIMEOUT_US) duration.value = MIN_TIMEOUT_US; @@ -111,22 +111,6 @@ void Timer::Connection::schedule_timeout(Microseconds duration, } -void Timer::Connection::_enable_modern_mode() -{ - if (_mode == MODERN) { - return; - } - _mode = MODERN; - _sigh(_signal_handler); - _scheduler._enable(); - - /* do initial calibration burst to make interpolation available earlier */ - for (unsigned i = 0; i < NR_OF_INITIAL_CALIBRATIONS; i++) { - _update_real_time(); - } -} - - Timer::Connection::Connection(Genode::Env &env, Genode::Entrypoint &ep, char const *label) : @@ -145,22 +129,20 @@ Timer::Connection::Connection(Genode::Env &env, char const *label) : Timer::Connection(env, env.ep(), label) {} -void Timer::Connection::_schedule_one_shot(Timeout &timeout, Microseconds duration) +Timeout_scheduler &Timer::Connection::_switch_to_timeout_framework_mode() { - _enable_modern_mode(); - _scheduler._schedule_one_shot(timeout, duration); + if (_mode == TIMEOUT_FRAMEWORK) { + return _timeout_scheduler; + } + _mode = TIMEOUT_FRAMEWORK; + _sigh(_signal_handler); + + _timeout_scheduler._enable(); + + + /* do initial calibration burst to make interpolation available earlier */ + for (unsigned i = 0; i < NR_OF_INITIAL_CALIBRATIONS; i++) { + _update_real_time(); + } + return _timeout_scheduler; }; - - -void Timer::Connection::_schedule_periodic(Timeout &timeout, Microseconds duration) -{ - _enable_modern_mode(); - _scheduler._schedule_periodic(timeout, duration); -}; - - -void Timer::Connection::_discard(Timeout &timeout) -{ - _enable_modern_mode(); - _scheduler._discard(timeout); -} diff --git a/repos/base/src/lib/timeout/timer_connection_time.cc b/repos/base/src/lib/timeout/timer_connection_time.cc index 83caf09e55..36de5f9440 100644 --- a/repos/base/src/lib/timeout/timer_connection_time.cc +++ b/repos/base/src/lib/timeout/timer_connection_time.cc @@ -38,8 +38,8 @@ void Timer::Connection::_update_real_time() * reached, we take the results that has the lowest latency. */ for (unsigned remote_time_trials = 0; - remote_time_trials < MAX_REMOTE_TIME_TRIALS; ) - { + remote_time_trials < MAX_REMOTE_TIME_TRIALS; ) { + /* read out the two time values close in succession */ Timestamp volatile new_ts = _timestamp(); uint64_t volatile new_us = elapsed_us(); @@ -111,8 +111,8 @@ void Timer::Connection::_update_real_time() * raise the shift successively to get as much precision as possible. */ uint64_t ts_diff_shifted = ts_diff << factor_shift; - while (ts_diff_shifted < us_diff << MIN_FACTOR_LOG2) - { + while (ts_diff_shifted < us_diff << MIN_FACTOR_LOG2) { + factor_shift++; ts_diff_shifted <<= 1; old_factor <<= 1; @@ -143,7 +143,7 @@ void Timer::Connection::_update_real_time() Duration Timer::Connection::curr_time() { - _enable_modern_mode(); + _switch_to_timeout_framework_mode(); Reconstructible mutex_guard(_real_time_mutex); Duration interpolated_time(_real_time); @@ -156,8 +156,8 @@ Duration Timer::Connection::curr_time() * the value would stand still for quite some time because we * can't let it jump back to a more realistic level. */ - if (_interpolation_quality == MAX_INTERPOLATION_QUALITY) - { + if (_interpolation_quality == MAX_INTERPOLATION_QUALITY) { + /* buffer interpolation related members and free the mutex */ Timestamp const ts = _ts; uint64_t const us_to_ts_factor = _us_to_ts_factor; diff --git a/repos/base/src/timer/epit/time_source.cc b/repos/base/src/timer/epit/time_source.cc index 1137efa595..38ece8408a 100644 --- a/repos/base/src/timer/epit/time_source.cc +++ b/repos/base/src/timer/epit/time_source.cc @@ -20,8 +20,8 @@ using namespace Genode; -void Timer::Time_source::schedule_timeout(Genode::Microseconds duration, - Timeout_handler &handler) +void Timer::Time_source::set_timeout(Genode::Microseconds duration, + Genode::Timeout_handler &handler) { unsigned long const ticks = (1ULL * duration.value * TICKS_PER_MS) / 1000; _handler = &handler; diff --git a/repos/base/src/timer/epit/time_source.h b/repos/base/src/timer/epit/time_source.h index d0ee90d6e2..f4fd4a54ea 100644 --- a/repos/base/src/timer/epit/time_source.h +++ b/repos/base/src/timer/epit/time_source.h @@ -71,7 +71,7 @@ class Timer::Time_source : private Genode::Attached_mmio, *************************/ Genode::Duration curr_time() override; - void schedule_timeout(Genode::Microseconds duration, Timeout_handler &handler) override; + void set_timeout(Genode::Microseconds, Genode::Timeout_handler &) override; Genode::Microseconds max_timeout() const override { return _max_timeout; }; }; diff --git a/repos/base/src/timer/gpt/time_source.cc b/repos/base/src/timer/gpt/time_source.cc index f8ad6e0f79..6ce5814ebb 100644 --- a/repos/base/src/timer/gpt/time_source.cc +++ b/repos/base/src/timer/gpt/time_source.cc @@ -17,8 +17,8 @@ using namespace Genode; -void Timer::Time_source::schedule_timeout(Genode::Microseconds duration, - Timeout_handler &handler) +void Timer::Time_source::set_timeout(Genode::Microseconds duration, + Timeout_handler &handler) { _handler = &handler; diff --git a/repos/base/src/timer/gpt/time_source.h b/repos/base/src/timer/gpt/time_source.h index f57197f6b1..4a5a3d1e50 100644 --- a/repos/base/src/timer/gpt/time_source.h +++ b/repos/base/src/timer/gpt/time_source.h @@ -68,8 +68,8 @@ class Timer::Time_source : private Genode::Attached_mmio, *************************/ Genode::Duration curr_time() override; - void schedule_timeout(Genode::Microseconds duration, - Timeout_handler &handler) override; + void set_timeout(Genode::Microseconds duration, + Genode::Timeout_handler &handler) override; Genode::Microseconds max_timeout() const override; }; diff --git a/repos/base/src/timer/include/root_component.h b/repos/base/src/timer/include/root_component.h index d2f89c0333..498dc13156 100644 --- a/repos/base/src/timer/include/root_component.h +++ b/repos/base/src/timer/include/root_component.h @@ -31,8 +31,8 @@ class Timer::Root_component : public Genode::Root_component enum { MIN_TIMEOUT_US = 1000 }; - Time_source _time_source; - Genode::Alarm_timeout_scheduler _timeout_scheduler; + Time_source _time_source; + Genode::Timeout_scheduler _timeout_scheduler; /******************** diff --git a/repos/base/src/timer/include/session_component.h b/repos/base/src/timer/include/session_component.h index 4ace282bf5..d9f3058287 100644 --- a/repos/base/src/timer/include/session_component.h +++ b/repos/base/src/timer/include/session_component.h @@ -34,7 +34,7 @@ namespace Timer { class Timer::Session_component : public Genode::Rpc_object, private Genode::List::Element, - private Genode::Timeout::Handler + private Genode::Timeout_handler { private: @@ -47,6 +47,11 @@ class Timer::Session_component : public Genode::Rpc_object, uint64_t const _init_time_us = _timeout_scheduler.curr_time().trunc_to_plain_us().value; + + /********************* + ** Timeout_handler ** + *********************/ + void handle_timeout(Duration) override { Genode::Signal_transmitter(_sigh).submit(); } diff --git a/repos/base/src/timer/include/threaded_time_source.h b/repos/base/src/timer/include/threaded_time_source.h index 1314fb3ead..bf74b8cfba 100644 --- a/repos/base/src/timer/include/threaded_time_source.h +++ b/repos/base/src/timer/include/threaded_time_source.h @@ -24,6 +24,8 @@ namespace Timer { using Genode::Microseconds; using Genode::Duration; + using Genode::Timeout_handler; + class Threaded_time_source; } @@ -31,6 +33,10 @@ namespace Timer { class Timer::Threaded_time_source : public Genode::Time_source, protected Genode::Thread { + public: + + enum Result_of_wait_for_irq { IRQ_TRIGGERED, CANCELLED }; + private: struct Irq_dispatcher : Genode::Interface @@ -52,7 +58,7 @@ class Timer::Threaded_time_source : public Genode::Time_source, public: - Timeout_handler *handler = nullptr; + Timeout_handler *handler = nullptr; Threaded_time_source &ts; Irq_dispatcher_component(Threaded_time_source &ts) : ts(ts) { } @@ -76,7 +82,7 @@ class Timer::Threaded_time_source : public Genode::Time_source, Genode::Capability _irq_dispatcher_cap; - virtual void _wait_for_irq() = 0; + virtual Result_of_wait_for_irq _wait_for_irq() = 0; /*********************** ** Thread_deprecated ** @@ -85,8 +91,9 @@ class Timer::Threaded_time_source : public Genode::Time_source, void entry() override { while (true) { - _wait_for_irq(); - _irq_dispatcher_cap.call(); + if (_wait_for_irq() == IRQ_TRIGGERED) { + _irq_dispatcher_cap.call(); + } } } diff --git a/repos/base/src/timer/periodic/time_source.cc b/repos/base/src/timer/periodic/time_source.cc index c9948ed5b3..46deb0ed45 100644 --- a/repos/base/src/timer/periodic/time_source.cc +++ b/repos/base/src/timer/periodic/time_source.cc @@ -18,8 +18,8 @@ using namespace Genode; -void Timer::Time_source::schedule_timeout(Microseconds duration, - Timeout_handler &handler) +void Timer::Time_source::set_timeout(Microseconds duration, + Timeout_handler &handler) { Mutex::Guard mutex_guard(_mutex); Threaded_time_source::handler(handler); @@ -27,7 +27,8 @@ void Timer::Time_source::schedule_timeout(Microseconds duration, } -void Timer::Time_source::_wait_for_irq() +Timer::Time_source::Result_of_wait_for_irq +Timer::Time_source::_wait_for_irq() { enum { SLEEP_GRANULARITY_US = 1000 }; uint64_t last_time_us = curr_time().trunc_to_plain_us().value; @@ -49,4 +50,5 @@ void Timer::Time_source::_wait_for_irq() break; } _mutex.release(); + return IRQ_TRIGGERED; } diff --git a/repos/base/src/timer/periodic/time_source.h b/repos/base/src/timer/periodic/time_source.h index 0aead928f0..eb69db96fd 100644 --- a/repos/base/src/timer/periodic/time_source.h +++ b/repos/base/src/timer/periodic/time_source.h @@ -42,7 +42,7 @@ class Timer::Time_source : public Threaded_time_source ** Threaded_time_source ** **************************/ - void _wait_for_irq() override; + Result_of_wait_for_irq _wait_for_irq() override; public: @@ -56,7 +56,7 @@ class Timer::Time_source : public Threaded_time_source Duration curr_time() override; Microseconds max_timeout() const override; - void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; + void set_timeout(Microseconds duration, Genode::Timeout_handler &handler) override; }; #endif /* _TIME_SOURCE_H_ */ diff --git a/repos/base/src/timer/pit/time_source.cc b/repos/base/src/timer/pit/time_source.cc index be9302aa1a..4db3d25fce 100644 --- a/repos/base/src/timer/pit/time_source.cc +++ b/repos/base/src/timer/pit/time_source.cc @@ -49,8 +49,8 @@ uint16_t Timer::Time_source::_read_counter(bool *wrapped) } -void Timer::Time_source::schedule_timeout(Microseconds duration, - Timeout_handler &handler) +void Timer::Time_source::set_timeout(Microseconds duration, + Timeout_handler &handler) { _handler = &handler; uint64_t duration_us = duration.value; diff --git a/repos/base/src/timer/pit/time_source.h b/repos/base/src/timer/pit/time_source.h index 74d9e07e3c..2e0646168a 100644 --- a/repos/base/src/timer/pit/time_source.h +++ b/repos/base/src/timer/pit/time_source.h @@ -95,7 +95,7 @@ class Timer::Time_source : public Genode::Signalled_time_source *************************/ Duration curr_time() override; - void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; + void set_timeout(Microseconds duration, Genode::Timeout_handler &handler) override; Microseconds max_timeout() const override { return Microseconds(PIT_MAX_USEC); } }; diff --git a/repos/dde_linux/src/lib/lxip/lx.h b/repos/dde_linux/src/lib/lxip/lx.h index 45010b3b84..bc5f12c53e 100644 --- a/repos/dde_linux/src/lib/lxip/lx.h +++ b/repos/dde_linux/src/lib/lxip/lx.h @@ -26,9 +26,9 @@ namespace Lx { Genode::Allocator &alloc, void (*ticker)()); - void timer_init(Genode::Entrypoint &ep, - Genode::Timeout_scheduler &scheduler, - Genode::Allocator &alloc, + void timer_init(Genode::Entrypoint &ep, + ::Timer::Connection &timer, + Genode::Allocator &alloc, void (*ticker)()); void timer_update_jiffies(); diff --git a/repos/dde_linux/src/lib/lxip/timer_handler.cc b/repos/dde_linux/src/lib/lxip/timer_handler.cc index 06b8f71611..b95dc737eb 100644 --- a/repos/dde_linux/src/lib/lxip/timer_handler.cc +++ b/repos/dde_linux/src/lib/lxip/timer_handler.cc @@ -84,15 +84,15 @@ class Lx::Timer private: Genode::Entrypoint &_ep; - Genode::Timeout_scheduler &_scheduler; + ::Timer::Connection &_timer; /* One-shot timeout for timer list */ ::Timer::One_shot_timeout _timers_one_shot { - _scheduler, *this, &Lx::Timer::_handle_timers }; + _timer, *this, &Lx::Timer::_handle_timers }; /* One-shot timeout for 'wait' */ ::Timer::One_shot_timeout _wait_one_shot { - _scheduler, *this, &Lx::Timer::_handle_wait }; + _timer, *this, &Lx::Timer::_handle_wait }; Lx_kit::List _list; Genode::Tslab _timer_alloc; @@ -165,7 +165,7 @@ class Lx::Timer { auto new_jiffies = usecs_to_jiffies(dur.trunc_to_plain_us().value); if (new_jiffies < jiffies) - jiffies = usecs_to_jiffies(_scheduler.curr_time().trunc_to_plain_us().value); + jiffies = usecs_to_jiffies(_timer.curr_time().trunc_to_plain_us().value); else jiffies = new_jiffies; } @@ -199,13 +199,13 @@ class Lx::Timer /** * Constructor */ - Timer(Genode::Entrypoint &ep, - Genode::Timeout_scheduler &scheduler, - Genode::Allocator &alloc, + Timer(Genode::Entrypoint &ep, + ::Timer::Connection &timer, + Genode::Allocator &alloc, void (*tick)()) : _ep(ep), - _scheduler(scheduler), + _timer(timer), _timer_alloc(&alloc), _tick(tick) { @@ -296,7 +296,7 @@ class Lx::Timer * Do not use lx_emul usecs_to_jiffies(unsigned int) because * of implicit truncation! */ - jiffies = _scheduler.curr_time().trunc_to_plain_ms().value / JIFFIES_TICK_MS; + jiffies = _timer.curr_time().trunc_to_plain_ms().value / JIFFIES_TICK_MS; } /** @@ -326,11 +326,12 @@ class Lx::Timer static Lx::Timer *_timer; -void Lx::timer_init(Genode::Entrypoint &ep, - Genode::Timeout_scheduler &scheduler, - Genode::Allocator &alloc, void (*tick)()) +void Lx::timer_init(Genode::Entrypoint &ep, + ::Timer::Connection &timer, + Genode::Allocator &alloc, + void (*tick)()) { - static Lx::Timer inst(ep, scheduler, alloc, tick); + static Lx::Timer inst(ep, timer, alloc, tick); _timer = &inst; } diff --git a/repos/libports/include/lwip/genode_init.h b/repos/libports/include/lwip/genode_init.h index 2b20729b00..e89a26099d 100644 --- a/repos/libports/include/lwip/genode_init.h +++ b/repos/libports/include/lwip/genode_init.h @@ -18,7 +18,7 @@ #include namespace Lwip { - void genode_init(Genode::Allocator &heap, Genode::Timeout_scheduler &timer); + void genode_init(Genode::Allocator &heap, ::Timer::Connection &timer); Genode::Mutex &mutex(); } diff --git a/repos/libports/src/lib/lwip/platform/sys_arch.cc b/repos/libports/src/lib/lwip/platform/sys_arch.cc index 03e26d3206..1f3b0143dd 100644 --- a/repos/libports/src/lib/lwip/platform/sys_arch.cc +++ b/repos/libports/src/lib/lwip/platform/sys_arch.cc @@ -42,18 +42,18 @@ namespace Lwip { sys_check_timeouts(); } - Genode::Timeout_scheduler &timer; + ::Timer::Connection &timer; Timer::Periodic_timeout timeout { timer, *this, &Sys_timer::check_timeouts, Genode::Microseconds{250*1000} }; - Sys_timer(Genode::Timeout_scheduler &timer) : timer(timer) { } + Sys_timer(::Timer::Connection &timer) : timer(timer) { } }; static Sys_timer *sys_timer_ptr; - void genode_init(Genode::Allocator &heap, Genode::Timeout_scheduler &timer) + void genode_init(Genode::Allocator &heap, ::Timer::Connection &timer) { LWIP_ASSERT("LwIP initialized with an allocator that does not track sizes", !heap.need_size_for_free()); diff --git a/repos/os/src/test/timeout/main.cc b/repos/os/src/test/timeout/main.cc index 2185b2b6e4..5505979350 100644 --- a/repos/os/src/test/timeout/main.cc +++ b/repos/os/src/test/timeout/main.cc @@ -50,6 +50,17 @@ static bool precise_time(Xml_node config) return false; } +struct A1 { +A1() { +log(__func__,__LINE__); +} +}; + +struct A2 { +A2() { +log(__func__,__LINE__); +} +}; struct Test { @@ -58,6 +69,7 @@ struct Test Signal_transmitter done; unsigned id; Attached_rom_dataspace config { env, "config" }; + Timer::Connection timer { env }; Test(Env &env, @@ -76,6 +88,7 @@ struct Test * because Timer::Connection by now must be backwards compatible * and therefore starts interpolation only on demand. */ + timer.curr_time(); log("\nTEST ", id, ": ", brief, "\n"); @@ -819,4 +832,14 @@ struct Main }; -void Component::construct(Env &env) { static Main main(env); } +void Component::construct(Env &env) { +/* +Timer::Connection timer { env}; +while (1) { +log(__func__,__LINE__); +timer.msleep(1000); +log(__func__,__LINE__); +} +*/ +static Main main(env); +}