diff --git a/repos/os/include/os/time_source.h b/repos/os/include/os/time_source.h new file mode 100644 index 000000000..33f05aeb0 --- /dev/null +++ b/repos/os/include/os/time_source.h @@ -0,0 +1,62 @@ +/* + * \brief Interface of a time source that can handle one timeout at a time + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _OS__TIME_SOURCE_H_ +#define _OS__TIME_SOURCE_H_ + +namespace Genode { class Time_source; } + +/** + * Interface of a time source that can handle one timeout at a time + */ +struct Genode::Time_source +{ + /** + * Makes it clear which time unit an interfaces takes + */ + struct Microseconds + { + unsigned long value; + + explicit Microseconds(unsigned long const value) : value(value) { } + }; + + /** + * Interface of a timeout callback + */ + struct Timeout_handler + { + virtual void handle_timeout(Microseconds curr_time) = 0; + }; + + /** + * Return the current time of the source + */ + virtual Microseconds curr_time() const = 0; + + /** + * Return the maximum timeout duration that the source can handle + */ + virtual Microseconds max_timeout() const = 0; + + /** + * Install a timeout, overrides the last timeout if any + * + * \param duration timeout duration + * \param handler timeout callback + */ + virtual void schedule_timeout(Microseconds duration, + Timeout_handler &handler) = 0; +}; + +#endif /* _OS__TIME_SOURCE_H_ */ diff --git a/repos/os/include/os/timeout.h b/repos/os/include/os/timeout.h new file mode 100644 index 000000000..7b1912522 --- /dev/null +++ b/repos/os/include/os/timeout.h @@ -0,0 +1,259 @@ +/* + * \brief Multiplexing one time source amongst different timeouts + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _OS__TIMEOUT_H_ +#define _OS__TIMEOUT_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode { + + class Timeout_scheduler; + class Timeout; + class Alarm_timeout_scheduler; + template class Periodic_timeout; + template class One_shot_timeout; +} + + +/** + * Interface of a time-source multiplexer + */ +struct Genode::Timeout_scheduler +{ + using Microseconds = Time_source::Microseconds; + + /** + * Read out the now time of the scheduler + */ + virtual Microseconds curr_time() const = 0; + + /** + * 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; +}; + + +/** + * Timeout callback that can be used for both one-shot and periodic timeouts + * + * This class should be used only if it is necessary to use one timeout + * callback for both periodic and one-shot timeouts. This is the case, for + * 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 +{ + friend class Alarm_timeout_scheduler; + + public: + + /** + * Interface of a timeout handler + */ + struct Handler + { + using Microseconds = Time_source::Microseconds; + + virtual void handle_timeout(Microseconds curr_time) = 0; + }; + + private: + + using Microseconds = Time_source::Microseconds; + + + struct Alarm : Genode::Alarm + { + Timeout_scheduler &timeout_scheduler; + Handler *handler = nullptr; + bool periodic; + + Alarm(Timeout_scheduler &timeout_scheduler) + : timeout_scheduler(timeout_scheduler) { } + + + /******************* + ** Genode::Alarm ** + *******************/ + + bool on_alarm(unsigned) override; + + } _alarm; + + public: + + Timeout(Timeout_scheduler &timeout_scheduler) + : _alarm(timeout_scheduler) { } + + ~Timeout() { _alarm.timeout_scheduler.discard(*this); } + + void schedule_periodic(Microseconds duration, Handler &handler); + + void schedule_one_shot(Microseconds duration, Handler &handler); +}; + + +/** + * Periodic timeout that is linked to a custom handler, starts when constructed + */ +template +struct Genode::Periodic_timeout : private Noncopyable +{ + public: + + using Microseconds = Timeout_scheduler::Microseconds; + + private: + + typedef void (HANDLER::*Handler_method)(Microseconds); + + Timeout _timeout; + + struct Handler : Timeout::Handler + { + HANDLER &object; + Handler_method const method; + + Handler(HANDLER &object, Handler_method method) + : object(object), method(method) { } + + + /********************** + ** Timeout::Handler ** + **********************/ + + void handle_timeout(Microseconds curr_time) override { + (object.*method)(curr_time); } + + } _handler; + + public: + + Periodic_timeout(Timeout_scheduler &timeout_scheduler, + HANDLER &object, + Handler_method method, + Microseconds duration) + : + _timeout(timeout_scheduler), _handler(object, method) + { + _timeout.schedule_periodic(duration, _handler); + } +}; + + +/** + * One-shot timeout that is linked to a custom handler, started manually + */ +template +class Genode::One_shot_timeout : private Noncopyable +{ + private: + + using Microseconds = Timeout_scheduler::Microseconds; + + typedef void (HANDLER::*Handler_method)(Microseconds); + + Timeout _timeout; + + struct Handler : Timeout::Handler + { + HANDLER &object; + Handler_method const method; + + Handler(HANDLER &object, Handler_method method) + : object(object), method(method) { } + + + /********************** + ** Timeout::Handler ** + **********************/ + + void handle_timeout(Microseconds curr_time) override { + (object.*method)(curr_time); } + + } _handler; + + public: + + One_shot_timeout(Timeout_scheduler &timeout_scheduler, + HANDLER &object, + Handler_method method) + : _timeout(timeout_scheduler), _handler(object, method) { } + + void start(Microseconds duration) { + _timeout.schedule_one_shot(duration, _handler); } +}; + + +/** + * Timeout-scheduler implementation using the Alarm framework + */ +class Genode::Alarm_timeout_scheduler : private Noncopyable, + public Timeout_scheduler, + public Time_source::Timeout_handler +{ + private: + + Time_source &_time_source; + Alarm_scheduler _alarm_scheduler; + + + /********************************** + ** Time_source::Timeout_handler ** + **********************************/ + + void handle_timeout(Microseconds curr_time) override; + + public: + + Alarm_timeout_scheduler(Time_source &time_source); + + + /*********************** + ** Timeout_scheduler ** + ***********************/ + + void schedule_one_shot(Timeout &timeout, Microseconds duration) override; + void schedule_periodic(Timeout &timeout, Microseconds duration) override; + + Microseconds curr_time() const override { + return _time_source.curr_time(); } + + void discard(Timeout &timeout) override { + _alarm_scheduler.discard(&timeout._alarm); } +}; + +#endif /* _OS__TIMEOUT_H_ */ diff --git a/repos/os/include/os/timer.h b/repos/os/include/os/timer.h new file mode 100644 index 000000000..3db120dbc --- /dev/null +++ b/repos/os/include/os/timer.h @@ -0,0 +1,107 @@ +/* + * \brief Multiplexes a timer session amongst different timeouts + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Genode { class Timer; } + + +/** + * Multiplexes a timer session amongst different timeouts + */ +class Genode::Timer : public Timeout_scheduler +{ + private: + + class Time_source : public Genode::Time_source + { + private: + + enum { MIN_TIMEOUT_US = 100000 }; + + using Signal_handler = + Genode::Signal_handler; + + ::Timer::Session &_session; + Signal_handler _signal_handler; + Timeout_handler *_handler = nullptr; + + void _handle_timeout() + { + if (_handler) { + _handler->handle_timeout(curr_time()); } + } + + public: + + Time_source(::Timer::Session &session, Entrypoint &ep) + : + _session(session), + _signal_handler(ep, *this, &Time_source::_handle_timeout) + { + _session.sigh(_signal_handler); + } + + Microseconds curr_time() const { + return Microseconds(1000ULL * _session.elapsed_ms()); } + + void schedule_timeout(Microseconds duration, + Timeout_handler &handler) + { + if (duration.value < MIN_TIMEOUT_US) { + duration.value = MIN_TIMEOUT_US; } + + if (duration.value > max_timeout().value) { + duration.value = max_timeout().value; } + + _handler = &handler; + _session.trigger_once(duration.value); + } + + Microseconds max_timeout() const { + return Microseconds(~0UL); } + + } _time_source; + + Alarm_timeout_scheduler _timeout_scheduler { _time_source }; + + public: + + Timer(::Timer::Session &session, Entrypoint &ep) + : _time_source(session, ep) { } + + + /*********************** + ** Timeout_scheduler ** + ***********************/ + + void schedule_periodic(Timeout &timeout, Microseconds duration) override { + _timeout_scheduler.schedule_periodic(timeout, duration); } + + void schedule_one_shot(Timeout &timeout, Microseconds duration) override { + _timeout_scheduler.schedule_one_shot(timeout, duration); } + + Microseconds curr_time() const override { + return _timeout_scheduler.curr_time(); } + + void discard(Timeout &timeout) override { + _timeout_scheduler.discard(timeout); } +}; + +#endif /* _TIMER_H_ */ diff --git a/repos/os/lib/mk/timeout.mk b/repos/os/lib/mk/timeout.mk new file mode 100644 index 000000000..2c489a767 --- /dev/null +++ b/repos/os/lib/mk/timeout.mk @@ -0,0 +1,5 @@ +SRC_CC += timeout.cc + +LIBS += alarm + +vpath % $(REP_DIR)/src/lib/timeout diff --git a/repos/os/run/timeout.run b/repos/os/run/timeout.run new file mode 100644 index 000000000..15ff831dc --- /dev/null +++ b/repos/os/run/timeout.run @@ -0,0 +1,72 @@ +# +# Build +# + +build "core init drivers/timer test/timeout" + +# +# Boot image +# + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + +} + +build_boot_image "core init timer test-timeout" + +# +# Execution +# + +append qemu_args "-nographic -m 64" + +# +# We check for each timeout that has a distance of at least 200ms to each +# other timeout: +# +# 0 ms +# 0 ms +# 700 ms -> check for 700 ms +# 1000 ms -> check for 1000 ms +# 1400 ms -> check for 700 ms +# 2000 ms +# 2100 ms +# 2800 ms -> check for 700 ms +# 3000 ms -> check for 1000 ms +# 3250 ms -> check for 3250 ms +# 3500 ms -> check for 700 ms +# 4000 ms -> check for 1000 ms +# 4200 ms -> check for 700 ms +# 4900 ms +# 5000 ms +# 5200 ms -> check for 5200 ms +# 5600 ms -> check for 700 ms +# 6000 ms -> check for 1000 ms +# + +run_genode_until ".*700ms timeout.*\n.*1000ms timeout.*\n.*700ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n.*3250ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n.*700ms timeout.*\n.*5200ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n" 20 diff --git a/repos/os/src/lib/timeout/timeout.cc b/repos/os/src/lib/timeout/timeout.cc new file mode 100644 index 000000000..f190aa0f3 --- /dev/null +++ b/repos/os/src/lib/timeout/timeout.cc @@ -0,0 +1,101 @@ +/* + * \brief Multiplexing one time source amongst different timeout subjects + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include + +using namespace Genode; + + +/************* + ** Timeout ** + *************/ + +void Timeout::schedule_periodic(Microseconds duration, Handler &handler) +{ + _alarm.handler = &handler; + _alarm.periodic = true; + _alarm.timeout_scheduler.schedule_periodic(*this, duration); +} + +void Timeout::schedule_one_shot(Microseconds duration, Handler &handler) +{ + _alarm.handler = &handler; + _alarm.periodic = false; + _alarm.timeout_scheduler.schedule_one_shot(*this, duration); +} + + +/******************** + ** Timeout::Alarm ** + ********************/ + +bool Timeout::Alarm::on_alarm(unsigned) +{ + if (handler) { + handler->handle_timeout(timeout_scheduler.curr_time()); } + return periodic; +} + + + +/***************************** + ** Alarm_timeout_scheduler ** + *****************************/ + +void Alarm_timeout_scheduler::handle_timeout(Microseconds curr_time) +{ + _alarm_scheduler.handle(curr_time.value); + + unsigned long sleep_time_us; + Alarm::Time deadline_us; + if (_alarm_scheduler.next_deadline(&deadline_us)) { + sleep_time_us = deadline_us - curr_time.value; + } else { + sleep_time_us = _time_source.max_timeout().value; } + + 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) +: + _time_source(time_source) +{ + time_source.schedule_timeout(Microseconds(0), *this); +} + + +void Alarm_timeout_scheduler::schedule_one_shot(Timeout &timeout, + Microseconds duration) +{ + _alarm_scheduler.schedule_absolute(&timeout._alarm, + _time_source.curr_time().value + + duration.value); + + if (_alarm_scheduler.head_timeout(&timeout._alarm)) { + _time_source.schedule_timeout(Microseconds(0), *this); } +} + + +void Alarm_timeout_scheduler::schedule_periodic(Timeout &timeout, + Microseconds duration) +{ + _alarm_scheduler.handle(_time_source.curr_time().value); + _alarm_scheduler.schedule(&timeout._alarm, duration.value); + if (_alarm_scheduler.head_timeout(&timeout._alarm)) { + _time_source.schedule_timeout(Microseconds(0), *this); } +} diff --git a/repos/os/src/test/timeout/main.cc b/repos/os/src/test/timeout/main.cc new file mode 100644 index 000000000..3ce9270d1 --- /dev/null +++ b/repos/os/src/test/timeout/main.cc @@ -0,0 +1,60 @@ +/* + * \brief Test for timeout library + * \author Martin Stein + * \date 2016-11-24 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + + +using namespace Genode; + + +class Main +{ + private: + + using Microseconds = Genode::Timer::Microseconds; + + void _handle(Microseconds now, Cstring name) { + log(now.value / 1000, " ms: ", name, " timeout triggered"); } + + void _handle_pt1(Microseconds now) { _handle(now, "Periodic 700ms"); } + void _handle_pt2(Microseconds now) { _handle(now, "Periodic 1000ms"); } + void _handle_ot1(Microseconds now) { _handle(now, "One-shot 3250ms"); } + void _handle_ot2(Microseconds now) { _handle(now, "One-shot 5200ms"); } + + Timer::Connection _timer_connection; + Genode::Timer _timer; + Periodic_timeout
_pt1 { _timer, *this, &Main::_handle_pt1, Microseconds(700000) }; + Periodic_timeout
_pt2 { _timer, *this, &Main::_handle_pt2, Microseconds(1000000) }; + One_shot_timeout
_ot1 { _timer, *this, &Main::_handle_ot1 }; + One_shot_timeout
_ot2 { _timer, *this, &Main::_handle_ot2 }; + + public: + + Main(Env &env) : _timer_connection(env), + _timer(_timer_connection, env.ep()) + { + _ot1.start(Microseconds(3250000)); + _ot2.start(Microseconds(5300000)); + } +}; + + +/*************** + ** Component ** + ***************/ + +size_t Component::stack_size() { return 4 * 1024 * sizeof(addr_t); } +void Component::construct(Env &env) { static Main main(env); } diff --git a/repos/os/src/test/timeout/target.mk b/repos/os/src/test/timeout/target.mk new file mode 100644 index 000000000..146f03935 --- /dev/null +++ b/repos/os/src/test/timeout/target.mk @@ -0,0 +1,4 @@ +TARGET = test-timeout +SRC_CC += main.cc +LIBS += base timeout +INC_DIR += $(PRG_DIR) diff --git a/tool/autopilot.list b/tool/autopilot.list index 899a8e238..75decf3b4 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -75,3 +75,4 @@ cpu_sampler_noux usb_hid smartcard new_delete +timeout