/* * \brief Connection to timer service and timeout scheduler * \author Norman Feske * \date 2008-08-22 */ /* * Copyright (C) 2008-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _INCLUDE__TIMER_SESSION__CONNECTION_H_ #define _INCLUDE__TIMER_SESSION__CONNECTION_H_ /* Genode includes */ #include #include #include #include #include #include namespace Timer { class Connection; template class Periodic_timeout; template class One_shot_timeout; } /** * Periodic timeout that is linked to a custom handler, scheduled when constructed */ template struct Timer::Periodic_timeout : private Genode::Noncopyable, private Genode::Timeout_handler { private: using Duration = Genode::Duration; using Timeout = Genode::Timeout; using Microseconds = Genode::Microseconds; typedef void (HANDLER::*Handler_method)(Duration); Timeout _timeout; HANDLER &_object; Handler_method const _method; /********************* ** Timeout_handler ** *********************/ void handle_timeout(Duration curr_time) override { (_object.*_method)(curr_time); } public: Periodic_timeout(Connection &timer, HANDLER &object, Handler_method method, Microseconds duration) : _timeout { timer }, _object { object }, _method { method } { _timeout.schedule_periodic(duration, *this); } }; /** * One-shot timeout that is linked to a custom handler, scheduled manually */ template class Timer::One_shot_timeout : private Genode::Noncopyable, private Genode::Timeout_handler { private: using Duration = Genode::Duration; using Timeout = Genode::Timeout; using Microseconds = Genode::Microseconds; typedef void (HANDLER::*Handler_method)(Duration); Timeout _timeout; HANDLER &_object; Handler_method const _method; /********************* ** Timeout_handler ** *********************/ void handle_timeout(Duration curr_time) override { (_object.*_method)(curr_time); } public: 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, *this); } void discard() { _timeout.discard(); } bool scheduled() { return _timeout.scheduled(); } }; /** * Connection to timer service and timeout scheduler * * Multiplexes a timer session amongst different timeouts. */ class Timer::Connection : public Genode::Connection, public Session_client, private Genode::Time_source { friend class Genode::Timeout; private: 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 */ Connection(Connection const &); Connection &operator = (Connection const &); /* * The mode determines which interface of the timer connection is * 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. * * 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 { TIMER_SESSION, TIMEOUT_FRAMEWORK }; Mode _mode { TIMER_SESSION }; Mutex _mutex { }; Genode::Signal_receiver _sig_rec { }; Genode::Signal_context _default_sigh_ctx { }; Genode::Signal_context_capability _default_sigh_cap = _sig_rec.manage(&_default_sigh_ctx); Genode::Signal_context_capability _custom_sigh_cap { }; void _sigh(Signal_context_capability sigh) { Session_client::sigh(sigh); } /**************************************************** ** Members for interaction with Timeout framework ** ****************************************************/ enum { MIN_TIMEOUT_US = 5000 }; enum { REAL_TIME_UPDATE_PERIOD_US = 500000 }; enum { MAX_INTERPOLATION_QUALITY = 3 }; enum { MAX_REMOTE_TIME_LATENCY_US = 500 }; enum { MAX_REMOTE_TIME_TRIALS = 5 }; enum { NR_OF_INITIAL_CALIBRATIONS = 3 * MAX_INTERPOLATION_QUALITY }; enum { MIN_FACTOR_LOG2 = 8 }; 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 } }; Genode::Timeout_scheduler &_switch_to_timeout_framework_mode(); Timestamp _timestamp(); void _update_interpolation_quality(uint64_t min_factor, uint64_t max_factor); uint64_t _ts_to_us_ratio(Timestamp ts, uint64_t us, unsigned shift); void _update_real_time(); Duration _update_interpolated_time(Duration &interpolated_time); void _handle_timeout(); /***************** ** Time_source ** *****************/ void set_timeout(Microseconds duration, Timeout_handler &handler) override; Microseconds max_timeout() const override { return Microseconds(REAL_TIME_UPDATE_PERIOD_US); } public: struct Method_cannot_be_used_in_timeout_framework_mode : Genode::Exception { }; /** * Constructor * * \param env environment used for construction (e.g. quota trading) * \param ep entrypoint used as timeout handler execution context * \param label optional label used in session routing */ Connection(Genode::Env &env, Genode::Entrypoint & ep, char const *label = ""); /** * Convenience constructor wrapper using the environment's entrypoint as * timeout handler execution context */ Connection(Genode::Env &env, char const *label = ""); ~Connection() { _sig_rec.dissolve(&_default_sigh_ctx); } /* * Intercept 'sigh' to keep track of customized signal handlers * * \noapi * \deprecated Use One_shot_timeout (or Periodic_timeout) instead */ void sigh(Signal_context_capability sigh) override { if (_mode == TIMEOUT_FRAMEWORK) { throw Method_cannot_be_used_in_timeout_framework_mode(); } _custom_sigh_cap = sigh; Session_client::sigh(_custom_sigh_cap); } /* * Block for a time span of 'us' microseconds * * \noapi * \deprecated Use One_shot_timeout (or Periodic_timeout) instead */ void usleep(uint64_t us) override { if (_mode == TIMEOUT_FRAMEWORK) { throw Method_cannot_be_used_in_timeout_framework_mode(); } /* * Omit the interaction with the timer driver for the corner case * of not sleeping at all. This corner case may be triggered when * polling is combined with sleeping (as some device drivers do). * If we passed the sleep operation to the timer driver, the * timer would apply its policy about a minimum sleep time to * the sleep operation, which is not desired when polling. */ if (us == 0) return; /* serialize sleep calls issued by different threads */ Mutex::Guard guard(_mutex); /* temporarily install to the default signal handler */ if (_custom_sigh_cap.valid()) Session_client::sigh(_default_sigh_cap); /* trigger timeout at default signal handler */ trigger_once(us); _sig_rec.wait_for_signal(); /* revert custom signal handler if registered */ if (_custom_sigh_cap.valid()) Session_client::sigh(_custom_sigh_cap); } /* * Block for a time span of 'ms' milliseconds * * \noapi * \deprecated Use One_shot_timeout (or Periodic_timeout) instead */ void msleep(uint64_t ms) override { if (_mode == TIMEOUT_FRAMEWORK) { throw Method_cannot_be_used_in_timeout_framework_mode(); } usleep(1000*ms); } /***************** ** Time_source ** *****************/ Duration curr_time() override; }; #endif /* _INCLUDE__TIMER_SESSION__CONNECTION_H_ */