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
This commit is contained in:
Martin Stein
2020-09-11 15:04:40 +02:00
committed by Christian Helmuth
parent 9e5d479d03
commit 7feea78991
26 changed files with 682 additions and 825 deletions

View File

@ -36,7 +36,7 @@ Timer::Time_source::Time_source(Env &env)
} }
void Timer::Time_source::schedule_timeout(Microseconds duration, void Timer::Time_source::set_timeout(Microseconds duration,
Timeout_handler &handler) Timeout_handler &handler)
{ {
Kernel::timeout_t duration_us = duration.value; Kernel::timeout_t duration_us = duration.value;

View File

@ -27,6 +27,8 @@ namespace Timer {
using Microseconds = Genode::Microseconds; using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration; using Duration = Genode::Duration;
using Timeout_handler = Genode::Timeout_handler;
class Time_source; class Time_source;
} }
@ -47,7 +49,7 @@ class Timer::Time_source : public Genode::Signalled_time_source
*************************/ *************************/
Duration curr_time() override; 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 { Microseconds max_timeout() const override {
return Microseconds(_max_timeout_us); }; return Microseconds(_max_timeout_us); };
}; };

View File

@ -22,50 +22,47 @@ using namespace Genode;
using namespace Nova; using namespace Nova;
void Timer::Time_source::schedule_timeout(Microseconds duration, void Timer::Time_source::set_timeout(Microseconds duration,
Timeout_handler &handler) Timeout_handler &handler)
{ {
/* set new timeout parameters and wake up the blocking thread */
Threaded_time_source::handler(handler); Threaded_time_source::handler(handler);
_timeout_us = duration.value;
/* check whether to cancel last timeout */ if (_sem) {
if (duration.value == 0 && _sem) { if (Nova::sm_ctrl(_sem, Nova::SEMAPHORE_UP) != Nova::NOVA_OK) {
uint8_t res = Nova::sm_ctrl(_sem, Nova::SEMAPHORE_UP);
if (res != Nova::NOVA_OK)
nova_die(); nova_die();
} }
/* remember timeout to be set during wait_for_timeout call */ }
_timeout_us = duration.value;
} }
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) { if (!_sem) {
/* initialize first time in context of running thread */
auto const &exc_base = Thread::native_thread().exc_pt_sel; auto const &exc_base = Thread::native_thread().exc_pt_sel;
request_signal_sm_cap(exc_base + Nova::PT_SEL_PAGE_FAULT, request_signal_sm_cap(exc_base + Nova::PT_SEL_PAGE_FAULT,
exc_base + Nova::SM_SEL_SIGNAL); exc_base + Nova::SM_SEL_SIGNAL);
_sem = Thread::native_thread().exc_pt_sel + SM_SEL_SIGNAL; _sem = Thread::native_thread().exc_pt_sel + SM_SEL_SIGNAL;
} }
/* calculate absolute timeout */ /* calculate absolute timeout */
Trace::Timestamp now = Trace::timestamp(); unsigned long long const deadline_timestamp {
Trace::Timestamp us_64 = _timeout_us; _timeout_us <= max_timeout().value ?
Trace::timestamp() + _timeout_us * (_tsc_khz / TSC_FACTOR) : 0 };
if (_timeout_us == max_timeout().value) {
/* 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(); }
} else {
/* block until timeout fires or it gets canceled */ /* block until timeout fires or it gets canceled */
unsigned long long tsc_absolute = now + us_64 * (_tsc_khz / TSC_FACTOR); switch (sm_ctrl(_sem, SEMAPHORE_DOWN, deadline_timestamp)) {
uint8_t res = sm_ctrl(_sem, SEMAPHORE_DOWN, tsc_absolute);
if (res != Nova::NOVA_OK && res != Nova::NOVA_TIMEOUT) { case Nova::NOVA_TIMEOUT:
nova_die(); } return IRQ_TRIGGERED;
case Nova::NOVA_OK:
return CANCELLED;
default:
nova_die();
return CANCELLED;
} }
} }

View File

@ -53,7 +53,7 @@ class Timer::Time_source : public Threaded_time_source
* The returned value must never be zero because it is used as * The returned value must never be zero because it is used as
* divisor by '_tsc_to_us'. * 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; return 1000*1000;
} }
@ -77,7 +77,7 @@ class Timer::Time_source : public Threaded_time_source
** Threaded_time_source ** ** Threaded_time_source **
**************************/ **************************/
void _wait_for_irq() override; Result_of_wait_for_irq _wait_for_irq() override;
public: public:
@ -92,13 +92,12 @@ class Timer::Time_source : public Threaded_time_source
** Genode::Time_source ** ** Genode::Time_source **
*************************/ *************************/
void schedule_timeout(Microseconds duration, void set_timeout(Microseconds duration,
Timeout_handler &handler) override; Timeout_handler &handler) override;
Microseconds max_timeout() const override Microseconds max_timeout() const override
{ {
uint64_t const max_us = _tsc_to_us(~(uint64_t)0); return Microseconds(_tsc_to_us(~(uint64_t)0));
return max_us > ~(uint64_t)0 ? Microseconds(~(uint64_t)0) : Microseconds(max_us);
} }
Duration curr_time() override Duration curr_time() override

View File

@ -21,38 +21,41 @@
/* Genode includes */ /* Genode includes */
#include <util/noncopyable.h> #include <util/noncopyable.h>
#include <util/list.h>
#include <base/duration.h> #include <base/duration.h>
#include <base/log.h>
#include <base/mutex.h> #include <base/mutex.h>
#include <util/misc_math.h>
#include <base/blockade.h>
namespace Genode { namespace Genode {
class Time_source; class Time_source;
class Timeout_scheduler;
class Timeout; class Timeout;
class Alarm_timeout_scheduler; class Timeout_handler;
class Timeout_scheduler;
} }
namespace Timer namespace Timer {
{
class Connection; class Connection;
class Root_component; 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 * Interface of a time source that can handle one timeout at a time
*/ */
struct Genode::Time_source : Interface 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 * Return the current time of the source
*/ */
@ -69,62 +72,8 @@ struct Genode::Time_source : Interface
* \param duration timeout duration * \param duration timeout duration
* \param handler timeout callback * \param handler timeout callback
*/ */
virtual void schedule_timeout(Microseconds duration, virtual void set_timeout(Microseconds duration,
Timeout_handler &handler) = 0; 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;
}; };
@ -136,173 +85,116 @@ class Genode::Timeout_scheduler : Interface
* example, in a Timer-session server. If this is not the case, the classes * example, in a Timer-session server. If this is not the case, the classes
* Periodic_timeout and One_shot_timeout are the better choice. * Periodic_timeout and One_shot_timeout are the better choice.
*/ */
class Genode::Timeout : private Noncopyable class Genode::Timeout : private Noncopyable,
public Genode::List<Timeout>::Element
{ {
friend class Alarm_timeout_scheduler; friend class Timeout_scheduler;
public:
/**
* Interface of a timeout handler
*/
struct Handler : Interface
{
virtual void handle_timeout(Duration curr_time) = 0;
};
private: private:
class Alarm Mutex _mutex { };
{ Timeout_scheduler &_scheduler;
friend class Alarm_timeout_scheduler; Microseconds _period { 0 };
Microseconds _deadline { Microseconds { 0 } };
List_element<Timeout> _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; Timeout &operator = (Timeout const &);
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: public:
Timeout_scheduler &timeout_scheduler; Timeout(Timeout_scheduler &scheduler);
Handler *handler = nullptr;
bool periodic = false;
Alarm(Timeout_scheduler &timeout_scheduler) Timeout(Timer::Connection &timer_connection);
: timeout_scheduler(timeout_scheduler) { _alarm_reset(); }
virtual ~Alarm(); ~Timeout();
} _alarm; void schedule_periodic(Microseconds duration,
Timeout_handler &handler);
public: void schedule_one_shot(Microseconds duration,
Timeout_handler &handler);
Timeout(Timeout_scheduler &timeout_scheduler)
: _alarm(timeout_scheduler) { }
~Timeout() { discard(); }
void schedule_periodic(Microseconds duration, Handler &handler);
void schedule_one_shot(Microseconds duration, Handler &handler);
void discard(); 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, class Genode::Timeout_scheduler : private Noncopyable,
public Timeout_scheduler, public Timeout_handler
public Time_source::Timeout_handler
{ {
friend class Timer::Connection; friend class Timer::Connection;
friend class Timer::Root_component; friend class Timer::Root_component;
friend class Timeout::Alarm; friend class Timeout;
private: private:
using Alarm = Timeout::Alarm; static constexpr uint64_t max_sleep_time_us { 60'000'000 };
Time_source &_time_source;
Mutex _mutex { }; Mutex _mutex { };
Alarm *_active_head { nullptr }; Time_source &_time_source;
Alarm *_pending_head { nullptr }; Microseconds const _max_sleep_time { min(_time_source.max_timeout().value, max_sleep_time_us) };
Alarm::Time _now { 0UL }; List<Timeout> _timeouts { };
bool _now_period { false }; Microseconds _current_time { 0 };
Alarm::Raw _min_handle_period { }; 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 _enable();
void _schedule_one_shot_timeout(Timeout &timeout,
Microseconds duration,
Timeout_handler &handler);
/********************************** void _schedule_periodic_timeout(Timeout &timeout,
** Time_source::Timeout_handler ** 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; 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: public:
Alarm_timeout_scheduler(Time_source &time_source, Timeout_scheduler(Time_source &time_source,
Microseconds min_handle_period = Microseconds(1)); Microseconds min_handle_period);
~Alarm_timeout_scheduler(); ~Timeout_scheduler();
Duration curr_time();
/***********************
** Timeout_scheduler **
***********************/
Duration curr_time() override { return _time_source.curr_time(); }
}; };
#endif /* _TIMER__TIMEOUT_H_ */ #endif /* _TIMER__TIMEOUT_H_ */

View File

@ -34,50 +34,42 @@ namespace Timer
* Periodic timeout that is linked to a custom handler, scheduled when constructed * Periodic timeout that is linked to a custom handler, scheduled when constructed
*/ */
template <typename HANDLER> template <typename HANDLER>
struct Timer::Periodic_timeout : private Genode::Noncopyable struct Timer::Periodic_timeout : private Genode::Noncopyable,
private Genode::Timeout_handler
{ {
private: private:
using Duration = Genode::Duration; using Duration = Genode::Duration;
using Timeout = Genode::Timeout; using Timeout = Genode::Timeout;
using Timeout_scheduler = Genode::Timeout_scheduler;
using Microseconds = Genode::Microseconds; using Microseconds = Genode::Microseconds;
typedef void (HANDLER::*Handler_method)(Duration); typedef void (HANDLER::*Handler_method)(Duration);
Timeout _timeout; Timeout _timeout;
HANDLER &_object;
struct Handler : Timeout::Handler Handler_method const _method;
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
/********************** /*********************
** Timeout::Handler ** ** Timeout_handler **
**********************/ *********************/
void handle_timeout(Duration curr_time) override { void handle_timeout(Duration curr_time) override {
(object.*method)(curr_time); } (_object.*_method)(curr_time); }
} _handler;
public: public:
Periodic_timeout(Timeout_scheduler &timeout_scheduler, Periodic_timeout(Connection &timer,
HANDLER &object, HANDLER &object,
Handler_method method, Handler_method method,
Microseconds duration) 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 * One-shot timeout that is linked to a custom handler, scheduled manually
*/ */
template <typename HANDLER> template <typename HANDLER>
class Timer::One_shot_timeout : private Genode::Noncopyable class Timer::One_shot_timeout : private Genode::Noncopyable,
private Genode::Timeout_handler
{ {
private: private:
using Duration = Genode::Duration; using Duration = Genode::Duration;
using Timeout = Genode::Timeout; using Timeout = Genode::Timeout;
using Timeout_scheduler = Genode::Timeout_scheduler;
using Microseconds = Genode::Microseconds; using Microseconds = Genode::Microseconds;
typedef void (HANDLER::*Handler_method)(Duration); typedef void (HANDLER::*Handler_method)(Duration);
Timeout _timeout; Timeout _timeout;
HANDLER &_object;
struct Handler : Timeout::Handler Handler_method const _method;
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
/********************** /*********************
** Timeout::Handler ** ** Timeout_handler **
**********************/ *********************/
void handle_timeout(Duration curr_time) override { void handle_timeout(Duration curr_time) override {
(object.*method)(curr_time); } (_object.*_method)(curr_time); }
} _handler;
public: public:
One_shot_timeout(Timeout_scheduler &timeout_scheduler, One_shot_timeout(Connection &timer,
HANDLER &object, HANDLER &object,
Handler_method method) Handler_method method)
: _timeout(timeout_scheduler), _handler(object, method) { } :
_timeout { timer },
~One_shot_timeout() { _timeout.discard(); } _object { object },
_method { method }
{ }
void schedule(Microseconds duration) { void schedule(Microseconds duration) {
_timeout.schedule_one_shot(duration, _handler); } _timeout.schedule_one_shot(duration, *this); }
void discard() { _timeout.discard(); } void discard() { _timeout.discard(); }
@ -141,19 +127,21 @@ class Timer::One_shot_timeout : private Genode::Noncopyable
*/ */
class Timer::Connection : public Genode::Connection<Session>, class Timer::Connection : public Genode::Connection<Session>,
public Session_client, public Session_client,
private Genode::Time_source, private Genode::Time_source
public Genode::Timeout_scheduler
{ {
friend class Genode::Timeout;
private: private:
using Timeout = Genode::Timeout; using Timeout = Genode::Timeout;
using Timeout_handler = Genode::Time_source::Timeout_handler; using Timeout_handler = Genode::Timeout_handler;
using Timestamp = Genode::Trace::Timestamp; using Timestamp = Genode::Trace::Timestamp;
using Duration = Genode::Duration; using Duration = Genode::Duration;
using Mutex = Genode::Mutex; using Mutex = Genode::Mutex;
using Microseconds = Genode::Microseconds; using Microseconds = Genode::Microseconds;
using Milliseconds = Genode::Milliseconds; using Milliseconds = Genode::Milliseconds;
using Entrypoint = Genode::Entrypoint; using Entrypoint = Genode::Entrypoint;
using Io_signal_handler = Genode::Io_signal_handler<Connection>;
/* /*
* Noncopyable * Noncopyable
@ -163,19 +151,34 @@ class Timer::Connection : public Genode::Connection<Session>,
/* /*
* The mode determines which interface of the timer connection is * The mode determines which interface of the timer connection is
* enabled. Initially, a timer connection is in LEGACY mode. When in * enabled. Initially, a timer connection is in TIMER_SESSION mode.
* MODERN mode, a call to the LEGACY interface causes an exception. * In this mode, the user can operate directly on the connection using
* When in LEGACY mode, a call to the MODERN interface causes a switch * the methods of the timer-session interface. As soon as the
* to the MODERN mode. The LEGACY interface is deprecated. Please * connection is handed over as argument to the constructor of a
* prefer using the MODERN interface. * 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 * These are the characteristics of the two modes:
* MODERN = more precise curr_time, non-blocking and multiplexed *
* handling with Periodic_timeout resp. One_shot_timeout * 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 { }; Mutex _mutex { };
Genode::Signal_receiver _sig_rec { }; Genode::Signal_receiver _sig_rec { };
Genode::Signal_context _default_sigh_ctx { }; Genode::Signal_context _default_sigh_ctx { };
@ -185,17 +188,14 @@ class Timer::Connection : public Genode::Connection<Session>,
Genode::Signal_context_capability _custom_sigh_cap { }; Genode::Signal_context_capability _custom_sigh_cap { };
void _enable_modern_mode();
void _sigh(Signal_context_capability sigh) void _sigh(Signal_context_capability sigh)
{ {
Session_client::sigh(sigh); Session_client::sigh(sigh);
} }
/****************************************************
/************************* ** Members for interaction with Timeout framework **
** Time_source helpers ** ****************************************************/
*************************/
enum { MIN_TIMEOUT_US = 5000 }; enum { MIN_TIMEOUT_US = 5000 };
enum { REAL_TIME_UPDATE_PERIOD_US = 500000 }; enum { REAL_TIME_UPDATE_PERIOD_US = 500000 };
@ -205,17 +205,19 @@ class Timer::Connection : public Genode::Connection<Session>,
enum { NR_OF_INITIAL_CALIBRATIONS = 3 * MAX_INTERPOLATION_QUALITY }; enum { NR_OF_INITIAL_CALIBRATIONS = 3 * MAX_INTERPOLATION_QUALITY };
enum { MIN_FACTOR_LOG2 = 8 }; enum { MIN_FACTOR_LOG2 = 8 };
Genode::Io_signal_handler<Connection> _signal_handler; Io_signal_handler _signal_handler;
Timeout_handler *_handler { nullptr }; Timeout_handler *_handler { nullptr };
Mutex _real_time_mutex { }; Mutex _real_time_mutex { };
uint64_t _us { elapsed_us() }; uint64_t _us { elapsed_us() };
Timestamp _ts { _timestamp() }; Timestamp _ts { _timestamp() };
Duration _real_time { Microseconds(_us) }; Duration _real_time { Microseconds { _us } };
Duration _interpolated_time { _real_time }; Duration _interpolated_time { _real_time };
unsigned _interpolation_quality { 0 }; unsigned _interpolation_quality { 0 };
uint64_t _us_to_ts_factor { 1UL }; uint64_t _us_to_ts_factor { 1 };
unsigned _us_to_ts_factor_shift { 0 }; 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(); Timestamp _timestamp();
@ -237,28 +239,12 @@ class Timer::Connection : public Genode::Connection<Session>,
** Time_source ** ** 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); } 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: public:
struct Cannot_use_both_legacy_and_modern_interface : Genode::Exception { }; struct Method_cannot_be_used_in_timeout_framework_mode : Genode::Exception { };
/** /**
* Constructor * Constructor
@ -287,8 +273,8 @@ class Timer::Connection : public Genode::Connection<Session>,
*/ */
void sigh(Signal_context_capability sigh) override void sigh(Signal_context_capability sigh) override
{ {
if (_mode == MODERN) { if (_mode == TIMEOUT_FRAMEWORK) {
throw Cannot_use_both_legacy_and_modern_interface(); throw Method_cannot_be_used_in_timeout_framework_mode();
} }
_custom_sigh_cap = sigh; _custom_sigh_cap = sigh;
Session_client::sigh(_custom_sigh_cap); Session_client::sigh(_custom_sigh_cap);
@ -302,8 +288,8 @@ class Timer::Connection : public Genode::Connection<Session>,
*/ */
void usleep(uint64_t us) override void usleep(uint64_t us) override
{ {
if (_mode == MODERN) { if (_mode == TIMEOUT_FRAMEWORK) {
throw Cannot_use_both_legacy_and_modern_interface(); throw Method_cannot_be_used_in_timeout_framework_mode();
} }
/* /*
* Omit the interaction with the timer driver for the corner case * Omit the interaction with the timer driver for the corner case
@ -340,16 +326,16 @@ class Timer::Connection : public Genode::Connection<Session>,
*/ */
void msleep(uint64_t ms) override void msleep(uint64_t ms) override
{ {
if (_mode == MODERN) { if (_mode == TIMEOUT_FRAMEWORK) {
throw Cannot_use_both_legacy_and_modern_interface(); throw Method_cannot_be_used_in_timeout_framework_mode();
} }
usleep(1000*ms); usleep(1000*ms);
} }
/*********************************** /*****************
** Timeout_scheduler/Time_source ** ** Time_source **
***********************************/ *****************/
Duration curr_time() override; Duration curr_time() override;
}; };

View File

@ -41,10 +41,7 @@
_Z11genode_exiti T _Z11genode_exiti T
_Z16main_thread_utcbv T _Z16main_thread_utcbv T
_Z22__ldso_raise_exceptionv T _Z22__ldso_raise_exceptionv T
_ZN5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T _ZN5Timer10Connection11set_timeoutEN6Genode12MicrosecondsERNS1_15Timeout_handlerE T
_ZN5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZN5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZN5Timer10Connection8_discardERN6Genode7TimeoutE T
_ZN5Timer10Connection9curr_timeEv T _ZN5Timer10Connection9curr_timeEv T
_ZN5Timer10ConnectionC1ERN6Genode3EnvEPKc T _ZN5Timer10ConnectionC1ERN6Genode3EnvEPKc T
_ZN5Timer10ConnectionC1ERN6Genode3EnvERNS1_10EntrypointEPKc T _ZN5Timer10ConnectionC1ERN6Genode3EnvERNS1_10EntrypointEPKc T
@ -152,6 +149,16 @@ _ZN6Genode17Rm_session_client6createEm T
_ZN6Genode17Rm_session_client7destroyENS_10CapabilityINS_10Region_mapEEE T _ZN6Genode17Rm_session_client7destroyENS_10CapabilityINS_10Region_mapEEE T
_ZN6Genode17Rm_session_clientC1ENS_10CapabilityINS_10Rm_sessionEEE T _ZN6Genode17Rm_session_clientC1ENS_10CapabilityINS_10Rm_sessionEEE T
_ZN6Genode17Rm_session_clientC2ENS_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_client11create_vcpuERNS_9AllocatorERNS_3EnvERNS_15Vm_handler_baseE T
_ZN6Genode17Vm_session_client3runENS_10Vm_session7Vcpu_idE T _ZN6Genode17Vm_session_client3runENS_10Vm_session7Vcpu_idE T
_ZN6Genode17Vm_session_client5pauseENS_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 _ZN6Genode18Signal_transmitterC2ENS_10CapabilityINS_14Signal_contextEEE T
_ZN6Genode18server_socket_pairEv T _ZN6Genode18server_socket_pairEv T
_ZN6Genode20env_session_id_spaceEv 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 _ZN6Genode25env_stack_area_region_mapE B 8
_ZN6Genode28env_stack_area_ram_allocatorE B 8 _ZN6Genode28env_stack_area_ram_allocatorE B 8
_ZN6Genode3Log3logEv T _ZN6Genode3Log3logEv T
@ -304,12 +302,19 @@ _ZN6Genode7Console7vprintfEPKcP13__va_list_tag T
_ZN6Genode7Console7vprintfEPKcPc T _ZN6Genode7Console7vprintfEPKcPc T
_ZN6Genode7Console7vprintfEPKcPv T _ZN6Genode7Console7vprintfEPKcPv T
_ZN6Genode7Console7vprintfEPKcSt9__va_list T _ZN6Genode7Console7vprintfEPKcSt9__va_list T
_ZN6Genode7Timeout17schedule_one_shotENS_12MicrosecondsERNS0_7HandlerE T _ZN6Genode7Timeout14_alarm_discardEv T
_ZN6Genode7Timeout17schedule_periodicENS_12MicrosecondsERNS0_7HandlerE T _ZN6Genode7Timeout17schedule_one_shotENS_12MicrosecondsERNS_15Timeout_handlerE T
_ZN6Genode7Timeout5AlarmD0Ev T _ZN6Genode7Timeout17schedule_periodicENS_12MicrosecondsERNS_15Timeout_handlerE T
_ZN6Genode7Timeout5AlarmD1Ev T _ZN6Genode7Timeout3RawC1Ev T
_ZN6Genode7Timeout5AlarmD2Ev T _ZN6Genode7Timeout3RawC2Ev T
_ZN6Genode7Timeout7discardEv T _ZN6Genode7Timeout7discardEv T
_ZN6Genode7Timeout9scheduledEv T
_ZN6Genode7TimeoutC1ERN5Timer10ConnectionE T
_ZN6Genode7TimeoutC1ERNS_17Timeout_schedulerE T
_ZN6Genode7TimeoutC2ERN5Timer10ConnectionE T
_ZN6Genode7TimeoutC2ERNS_17Timeout_schedulerE T
_ZN6Genode7TimeoutD1Ev T
_ZN6Genode7TimeoutD2Ev T
_ZN6Genode7cap_mapEv T _ZN6Genode7cap_mapEv T
_ZN6Genode8Duration3addENS_12MicrosecondsE T _ZN6Genode8Duration3addENS_12MicrosecondsE T
_ZN6Genode8Duration3addENS_12MillisecondsE T _ZN6Genode8Duration3addENS_12MillisecondsE T
@ -348,8 +353,8 @@ _ZTIN6Genode14Rpc_entrypointE D 56
_ZTIN6Genode14Signal_contextE D 56 _ZTIN6Genode14Signal_contextE D 56
_ZTIN6Genode17Region_map_clientE D 24 _ZTIN6Genode17Region_map_clientE D 24
_ZTIN6Genode17Rm_session_clientE D 24 _ZTIN6Genode17Rm_session_clientE D 24
_ZTIN6Genode17Timeout_schedulerE D 72
_ZTIN6Genode18Allocator_avl_baseE D 24 _ZTIN6Genode18Allocator_avl_baseE D 24
_ZTIN6Genode23Alarm_timeout_schedulerE D 72
_ZTIN6Genode4HeapE D 24 _ZTIN6Genode4HeapE D 24
_ZTIN6Genode4SlabE D 24 _ZTIN6Genode4SlabE D 24
_ZTIN6Genode5Child14Initial_threadE D 24 _ZTIN6Genode5Child14Initial_threadE D 24
@ -357,15 +362,14 @@ _ZTIN6Genode5ChildE D 72
_ZTIN6Genode6OutputE D 24 _ZTIN6Genode6OutputE D 24
_ZTIN6Genode6ThreadE D 16 _ZTIN6Genode6ThreadE D 16
_ZTIN6Genode7ConsoleE D 16 _ZTIN6Genode7ConsoleE D 16
_ZTIN6Genode7Timeout5AlarmE D 16
_ZTSN5Timer10ConnectionE R 21 _ZTSN5Timer10ConnectionE R 21
_ZTSN6Genode11Sliced_heapE R 23 _ZTSN6Genode11Sliced_heapE R 23
_ZTSN6Genode14Rpc_entrypointE R 26 _ZTSN6Genode14Rpc_entrypointE R 26
_ZTSN6Genode14Signal_contextE R 26 _ZTSN6Genode14Signal_contextE R 26
_ZTSN6Genode17Region_map_clientE R 29 _ZTSN6Genode17Region_map_clientE R 29
_ZTSN6Genode17Rm_session_clientE R 29 _ZTSN6Genode17Rm_session_clientE R 29
_ZTSN6Genode17Timeout_schedulerE R 35
_ZTSN6Genode18Allocator_avl_baseE R 30 _ZTSN6Genode18Allocator_avl_baseE R 30
_ZTSN6Genode23Alarm_timeout_schedulerE R 35
_ZTSN6Genode4HeapE R 15 _ZTSN6Genode4HeapE R 15
_ZTSN6Genode4SlabE R 15 _ZTSN6Genode4SlabE R 15
_ZTSN6Genode5Child14Initial_threadE R 32 _ZTSN6Genode5Child14Initial_threadE R 32
@ -373,7 +377,6 @@ _ZTSN6Genode5ChildE R 16
_ZTSN6Genode6OutputE R 17 _ZTSN6Genode6OutputE R 17
_ZTSN6Genode6ThreadE R 17 _ZTSN6Genode6ThreadE R 17
_ZTSN6Genode7ConsoleE R 18 _ZTSN6Genode7ConsoleE R 18
_ZTSN6Genode7Timeout5AlarmE R 24
_ZTVN5Timer10ConnectionE D 320 _ZTVN5Timer10ConnectionE D 320
_ZTVN6Genode10Vm_sessionE D 56 _ZTVN6Genode10Vm_sessionE D 56
_ZTVN6Genode11Sliced_heapE D 72 _ZTVN6Genode11Sliced_heapE D 72
@ -381,8 +384,8 @@ _ZTVN6Genode14Rpc_entrypointE D 80
_ZTVN6Genode14Signal_contextE D 32 _ZTVN6Genode14Signal_contextE D 32
_ZTVN6Genode17Region_map_clientE D 72 _ZTVN6Genode17Region_map_clientE D 72
_ZTVN6Genode17Rm_session_clientE D 48 _ZTVN6Genode17Rm_session_clientE D 48
_ZTVN6Genode17Timeout_schedulerE D 112
_ZTVN6Genode18Allocator_avl_baseE D 128 _ZTVN6Genode18Allocator_avl_baseE D 128
_ZTVN6Genode23Alarm_timeout_schedulerE D 112
_ZTVN6Genode4HeapE D 72 _ZTVN6Genode4HeapE D 72
_ZTVN6Genode4SlabE D 72 _ZTVN6Genode4SlabE D 72
_ZTVN6Genode5Child14Initial_threadE D 48 _ZTVN6Genode5Child14Initial_threadE D 48
@ -390,23 +393,13 @@ _ZTVN6Genode5ChildE D 440
_ZTVN6Genode6OutputE D 48 _ZTVN6Genode6OutputE D 48
_ZTVN6Genode6ThreadE D 48 _ZTVN6Genode6ThreadE D 48
_ZTVN6Genode7ConsoleE D 48 _ZTVN6Genode7ConsoleE D 48
_ZTVN6Genode7Timeout5AlarmE D 32 _ZThn236_N5Timer10Connection11set_timeoutEN6Genode12MicrosecondsERNS1_15Timeout_handlerE T
_ZThn236_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T
_ZThn236_N5Timer10Connection9curr_timeEv T _ZThn236_N5Timer10Connection9curr_timeEv T
_ZThn240_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T _ZThn288_N5Timer10Connection11set_timeoutEN6Genode12MicrosecondsERNS1_15Timeout_handlerE T
_ZThn240_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T
_ZThn240_N5Timer10Connection8_discardERN6Genode7TimeoutE T
_ZThn240_N5Timer10Connection9curr_timeEv T
_ZThn288_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T
_ZThn288_N5Timer10Connection9curr_timeEv T _ZThn288_N5Timer10Connection9curr_timeEv T
_ZThn296_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T _ZThn8_N6Genode17Timeout_scheduler14handle_timeoutENS_8DurationE T
_ZThn296_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T _ZThn8_N6Genode17Timeout_schedulerD0Ev T
_ZThn296_N5Timer10Connection8_discardERN6Genode7TimeoutE T _ZThn8_N6Genode17Timeout_schedulerD1Ev 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
_ZdlPvPN6Genode11DeallocatorE T _ZdlPvPN6Genode11DeallocatorE T
_ZdlPvPN6Genode9AllocatorE W _ZdlPvPN6Genode9AllocatorE W
_ZdlPvRN6Genode11DeallocatorE T _ZdlPvRN6Genode11DeallocatorE T

View File

@ -13,6 +13,7 @@
/* Genode includes */ /* Genode includes */
#include <timer/timeout.h> #include <timer/timeout.h>
#include <timer_session/connection.h>
using namespace Genode; using namespace Genode;
@ -21,419 +22,386 @@ using namespace Genode;
** Timeout ** ** Timeout **
*************/ *************/
void Timeout::schedule_periodic(Microseconds duration, Handler &handler) void Timeout::schedule_periodic(Microseconds duration,
Timeout_handler &handler)
{ {
_alarm.handler = &handler; _scheduler._schedule_periodic_timeout(*this, duration, handler);
_alarm.periodic = true;
_alarm.timeout_scheduler._schedule_periodic(*this, duration);
} }
void Timeout::schedule_one_shot(Microseconds duration, Handler &handler) void Timeout::schedule_one_shot(Microseconds duration,
Timeout_handler &handler)
{ {
_alarm.handler = &handler; _scheduler._schedule_one_shot_timeout(*this, duration, handler);
_alarm.periodic = false;
_alarm.timeout_scheduler._schedule_one_shot(*this, duration);
} }
void Timeout::discard() Timeout::Timeout(Timeout_scheduler &scheduler)
{
_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)
: :
_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; List<List_element<Timeout> > pending_timeouts { };
_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))
{ {
curr = curr->_next; /* acquire scheduler and update stored current time */
} Mutex::Guard const scheduler_guard(_mutex);
if (_destructor_called) {
/* if end of list is reached, append new element */
if (curr->_next == 0) {
curr->_next = alarm;
return; return;
} }
_current_time = curr_time.trunc_to_plain_us();
/* insert element in middle of list */ /* apply rate limit to the handling of timeouts */
alarm->_next = curr->_next; if (_current_time.value < _rate_limit_deadline.value) {
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; return;
} }
_rate_limit_deadline.value = _current_time.value +
_rate_limit_period.value;
/* find predecessor in alarm queue */ /*
Alarm *curr; * Filter out all pending timeouts to a local list first. The
for (curr = _active_head; curr && (curr->_next != alarm); curr = curr->_next); * 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.
*/
while (Timeout *timeout = _timeouts.first()) {
/* alarm is not enqueued */ timeout->_mutex.acquire();
if (!curr) return; if (timeout->_deadline.value > _current_time.value) {
timeout->_mutex.release();
break;
}
_timeouts.remove(timeout);
pending_timeouts.insert(&timeout->_pending_timeouts_le);
}
/*
* Do the framework-internal processing of the pending timeouts and
* then release their mutexes.
*/
for (List_element<Timeout> const *elem { pending_timeouts.first() };
elem != nullptr;
elem = elem->next()) {
/* remove alarm from alarm queue */ Timeout &timeout { *elem->object() };
curr->_next = alarm->_next; if (!timeout._in_discard_blockade) {
alarm->_alarm_reset();
/*
* 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<Timeout> 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();
}
}
} }
Timeout::Alarm *Alarm_timeout_scheduler::_alarm_get_pending_alarm() 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()
{ {
Mutex::Guard mutex_guard(_mutex); /*
* Acquire the scheduler mutex and don't release it at the end of this
do { * function to ease debugging in case that someone accesses a dangling
if (!_active_head || !_active_head->_raw.is_pending_at(_now, _now_period)) { * scheduler pointer.
return nullptr; } */
_mutex.acquire();
/* remove alarm from head of the list */
Alarm *pending_alarm = _active_head;
_active_head = _active_head->_next;
/* /*
* Acquire dispatch mutex to defer destruction until the call of '_on_alarm' * The function 'Timeout_scheduler::_discard_timeout_unsynchronized' may
* is finished * 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.
*/ */
pending_alarm->_dispatch_mutex.acquire(); _destructor_called = true;
/* reset alarm object */ /* discard all scheduled timeouts */
pending_alarm->_next = nullptr; while (Timeout *timeout = _timeouts.first()) {
pending_alarm->_active--; Mutex::Guard const timeout_guard { timeout->_mutex };
_discard_timeout_unsynchronized(*timeout);
if (pending_alarm->_delete) {
pending_alarm->_dispatch_mutex.release();
continue;
} }
return pending_alarm;
} while (true);
} }
void Alarm_timeout_scheduler::_alarm_handle(Alarm::Time curr_time) void Timeout_scheduler::_enable()
{ {
/* Mutex::Guard const scheduler_guard { _mutex };
* Raise the time counter and if it wraps, update also in which if (_destructor_called) {
* period of the time counter we are.
*/
if (_now > curr_time) {
_now_period = !_now_period;
}
_now = curr_time;
if (!_min_handle_period.is_pending_at(_now, _now_period)) {
return; return;
} }
Alarm::Time const deadline = _now + _min_handle_period.period; _set_time_source_timeout();
_min_handle_period.deadline = deadline;
_min_handle_period.deadline_period = _now > deadline ?
!_now_period : _now_period;
/*
* 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;
}
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();
}
} }
void Alarm_timeout_scheduler::_alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration) void Timeout_scheduler::_set_time_source_timeout()
{ {
/* _set_time_source_timeout(
* If the alarm is already present in the queue, re-consider its queue _timeouts.first() ?
* position because its deadline might have changed. I.e., if an alarm is _timeouts.first()->_deadline.value - _current_time.value :
* rescheduled with a new timeout before the original timeout triggered. ~(uint64_t)0);
*/
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);
} }
void Alarm_timeout_scheduler::_alarm_schedule_absolute(Alarm *alarm, Alarm::Time duration) void Timeout_scheduler::_set_time_source_timeout(uint64_t duration_us)
{ {
Mutex::Guard alarm_list_guard(_mutex); if (duration_us < _rate_limit_period.value) {
duration_us = _rate_limit_period.value;
_alarm_setup_alarm(*alarm, 0, duration); }
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_schedule(Alarm *alarm, Alarm::Time period) void Timeout_scheduler::_schedule_one_shot_timeout(Timeout &timeout,
Microseconds duration,
Timeout_handler &handler)
{ {
Mutex::Guard alarm_list_guard(_mutex); _schedule_timeout(timeout, duration, Microseconds { 0 }, handler);
}
/*
* Refuse to schedule a periodic timeout of 0 because it would trigger void Timeout_scheduler::_schedule_periodic_timeout(Timeout &timeout,
* infinitely in the 'handle' function. To account for the case where the Microseconds period,
* alarm object was already scheduled, we make sure to remove it from the Timeout_handler &handler)
* queue. {
*/
if (period == 0) { /* prevent using a period of 0 */
_alarm_unsynchronized_dequeue(alarm); if (period.value == 0) {
period.value = 1;
}
_schedule_timeout(timeout, Microseconds { 0 }, period, handler);
}
void Timeout_scheduler::_schedule_timeout(Timeout &timeout,
Microseconds duration,
Microseconds period,
Timeout_handler &handler)
{
/* acquire scheduler and timeout mutex */
Mutex::Guard const scheduler_guard { _mutex };
if (_destructor_called) {
return; return;
} }
Mutex::Guard const timeout_guard(timeout._mutex);
/* first deadline is overdue */ /* prevent inserting a timeout twice */
_alarm_setup_alarm(*alarm, period, 0); 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 * If the new timeout is the first to trigger, we have to update the
* grabbing the '_dispatch_mutex'. This is important when this function * time-source timeout.
* 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 (alarm) { if (_timeouts.first() == &timeout) {
{ _set_time_source_timeout(deadline_us - curr_time_us);
/* 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;
} }
} }
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 timeout list is empty, insert as first element */
if (_timeouts.first() == nullptr) {
if (!_active_head) return false; _timeouts.insert(&timeout);
return;
if (deadline)
*deadline = _active_head->_raw.deadline;
if (*deadline < _min_handle_period.deadline) {
*deadline = _min_handle_period.deadline;
} }
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();
} }

View File

@ -97,7 +97,7 @@ void Timer::Connection::_handle_timeout()
} }
void Timer::Connection::schedule_timeout(Microseconds duration, void Timer::Connection::set_timeout(Microseconds duration,
Timeout_handler &handler) Timeout_handler &handler)
{ {
if (duration.value < MIN_TIMEOUT_US) if (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, Timer::Connection::Connection(Genode::Env &env, Genode::Entrypoint &ep,
char const *label) char const *label)
: :
@ -145,22 +129,20 @@ Timer::Connection::Connection(Genode::Env &env, char const *label)
: Timer::Connection(env, env.ep(), 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(); if (_mode == TIMEOUT_FRAMEWORK) {
_scheduler._schedule_one_shot(timeout, duration); 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);
}

View File

@ -38,8 +38,8 @@ void Timer::Connection::_update_real_time()
* reached, we take the results that has the lowest latency. * reached, we take the results that has the lowest latency.
*/ */
for (unsigned remote_time_trials = 0; 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 */ /* read out the two time values close in succession */
Timestamp volatile new_ts = _timestamp(); Timestamp volatile new_ts = _timestamp();
uint64_t volatile new_us = elapsed_us(); 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. * raise the shift successively to get as much precision as possible.
*/ */
uint64_t ts_diff_shifted = ts_diff << factor_shift; 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++; factor_shift++;
ts_diff_shifted <<= 1; ts_diff_shifted <<= 1;
old_factor <<= 1; old_factor <<= 1;
@ -143,7 +143,7 @@ void Timer::Connection::_update_real_time()
Duration Timer::Connection::curr_time() Duration Timer::Connection::curr_time()
{ {
_enable_modern_mode(); _switch_to_timeout_framework_mode();
Reconstructible<Mutex::Guard> mutex_guard(_real_time_mutex); Reconstructible<Mutex::Guard> mutex_guard(_real_time_mutex);
Duration interpolated_time(_real_time); 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 * the value would stand still for quite some time because we
* can't let it jump back to a more realistic level. * 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 */ /* buffer interpolation related members and free the mutex */
Timestamp const ts = _ts; Timestamp const ts = _ts;
uint64_t const us_to_ts_factor = _us_to_ts_factor; uint64_t const us_to_ts_factor = _us_to_ts_factor;

View File

@ -20,8 +20,8 @@
using namespace Genode; using namespace Genode;
void Timer::Time_source::schedule_timeout(Genode::Microseconds duration, void Timer::Time_source::set_timeout(Genode::Microseconds duration,
Timeout_handler &handler) Genode::Timeout_handler &handler)
{ {
unsigned long const ticks = (1ULL * duration.value * TICKS_PER_MS) / 1000; unsigned long const ticks = (1ULL * duration.value * TICKS_PER_MS) / 1000;
_handler = &handler; _handler = &handler;

View File

@ -71,7 +71,7 @@ class Timer::Time_source : private Genode::Attached_mmio,
*************************/ *************************/
Genode::Duration curr_time() override; 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; }; Genode::Microseconds max_timeout() const override { return _max_timeout; };
}; };

View File

@ -17,7 +17,7 @@
using namespace Genode; using namespace Genode;
void Timer::Time_source::schedule_timeout(Genode::Microseconds duration, void Timer::Time_source::set_timeout(Genode::Microseconds duration,
Timeout_handler &handler) Timeout_handler &handler)
{ {
_handler = &handler; _handler = &handler;

View File

@ -68,8 +68,8 @@ class Timer::Time_source : private Genode::Attached_mmio,
*************************/ *************************/
Genode::Duration curr_time() override; Genode::Duration curr_time() override;
void schedule_timeout(Genode::Microseconds duration, void set_timeout(Genode::Microseconds duration,
Timeout_handler &handler) override; Genode::Timeout_handler &handler) override;
Genode::Microseconds max_timeout() const override; Genode::Microseconds max_timeout() const override;
}; };

View File

@ -32,7 +32,7 @@ class Timer::Root_component : public Genode::Root_component<Session_component>
enum { MIN_TIMEOUT_US = 1000 }; enum { MIN_TIMEOUT_US = 1000 };
Time_source _time_source; Time_source _time_source;
Genode::Alarm_timeout_scheduler _timeout_scheduler; Genode::Timeout_scheduler _timeout_scheduler;
/******************** /********************

View File

@ -34,7 +34,7 @@ namespace Timer {
class Timer::Session_component : public Genode::Rpc_object<Session>, class Timer::Session_component : public Genode::Rpc_object<Session>,
private Genode::List<Session_component>::Element, private Genode::List<Session_component>::Element,
private Genode::Timeout::Handler private Genode::Timeout_handler
{ {
private: private:
@ -47,6 +47,11 @@ class Timer::Session_component : public Genode::Rpc_object<Session>,
uint64_t const _init_time_us = uint64_t const _init_time_us =
_timeout_scheduler.curr_time().trunc_to_plain_us().value; _timeout_scheduler.curr_time().trunc_to_plain_us().value;
/*********************
** Timeout_handler **
*********************/
void handle_timeout(Duration) override { void handle_timeout(Duration) override {
Genode::Signal_transmitter(_sigh).submit(); } Genode::Signal_transmitter(_sigh).submit(); }

View File

@ -24,6 +24,8 @@ namespace Timer {
using Genode::Microseconds; using Genode::Microseconds;
using Genode::Duration; using Genode::Duration;
using Genode::Timeout_handler;
class Threaded_time_source; class Threaded_time_source;
} }
@ -31,6 +33,10 @@ namespace Timer {
class Timer::Threaded_time_source : public Genode::Time_source, class Timer::Threaded_time_source : public Genode::Time_source,
protected Genode::Thread protected Genode::Thread
{ {
public:
enum Result_of_wait_for_irq { IRQ_TRIGGERED, CANCELLED };
private: private:
struct Irq_dispatcher : Genode::Interface struct Irq_dispatcher : Genode::Interface
@ -76,7 +82,7 @@ class Timer::Threaded_time_source : public Genode::Time_source,
Genode::Capability<Irq_dispatcher> _irq_dispatcher_cap; Genode::Capability<Irq_dispatcher> _irq_dispatcher_cap;
virtual void _wait_for_irq() = 0; virtual Result_of_wait_for_irq _wait_for_irq() = 0;
/*********************** /***********************
** Thread_deprecated ** ** Thread_deprecated **
@ -85,10 +91,11 @@ class Timer::Threaded_time_source : public Genode::Time_source,
void entry() override void entry() override
{ {
while (true) { while (true) {
_wait_for_irq(); if (_wait_for_irq() == IRQ_TRIGGERED) {
_irq_dispatcher_cap.call<Irq_dispatcher::Rpc_do_dispatch>(); _irq_dispatcher_cap.call<Irq_dispatcher::Rpc_do_dispatch>();
} }
} }
}
protected: protected:

View File

@ -18,7 +18,7 @@
using namespace Genode; using namespace Genode;
void Timer::Time_source::schedule_timeout(Microseconds duration, void Timer::Time_source::set_timeout(Microseconds duration,
Timeout_handler &handler) Timeout_handler &handler)
{ {
Mutex::Guard mutex_guard(_mutex); Mutex::Guard mutex_guard(_mutex);
@ -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 }; enum { SLEEP_GRANULARITY_US = 1000 };
uint64_t last_time_us = curr_time().trunc_to_plain_us().value; uint64_t last_time_us = curr_time().trunc_to_plain_us().value;
@ -49,4 +50,5 @@ void Timer::Time_source::_wait_for_irq()
break; break;
} }
_mutex.release(); _mutex.release();
return IRQ_TRIGGERED;
} }

View File

@ -42,7 +42,7 @@ class Timer::Time_source : public Threaded_time_source
** Threaded_time_source ** ** Threaded_time_source **
**************************/ **************************/
void _wait_for_irq() override; Result_of_wait_for_irq _wait_for_irq() override;
public: public:
@ -56,7 +56,7 @@ class Timer::Time_source : public Threaded_time_source
Duration curr_time() override; Duration curr_time() override;
Microseconds max_timeout() const 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_ */ #endif /* _TIME_SOURCE_H_ */

View File

@ -49,7 +49,7 @@ uint16_t Timer::Time_source::_read_counter(bool *wrapped)
} }
void Timer::Time_source::schedule_timeout(Microseconds duration, void Timer::Time_source::set_timeout(Microseconds duration,
Timeout_handler &handler) Timeout_handler &handler)
{ {
_handler = &handler; _handler = &handler;

View File

@ -95,7 +95,7 @@ class Timer::Time_source : public Genode::Signalled_time_source
*************************/ *************************/
Duration curr_time() override; 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 { Microseconds max_timeout() const override {
return Microseconds(PIT_MAX_USEC); } return Microseconds(PIT_MAX_USEC); }
}; };

View File

@ -27,7 +27,7 @@ namespace Lx {
void (*ticker)()); void (*ticker)());
void timer_init(Genode::Entrypoint &ep, void timer_init(Genode::Entrypoint &ep,
Genode::Timeout_scheduler &scheduler, ::Timer::Connection &timer,
Genode::Allocator &alloc, Genode::Allocator &alloc,
void (*ticker)()); void (*ticker)());

View File

@ -84,15 +84,15 @@ class Lx::Timer
private: private:
Genode::Entrypoint &_ep; Genode::Entrypoint &_ep;
Genode::Timeout_scheduler &_scheduler; ::Timer::Connection &_timer;
/* One-shot timeout for timer list */ /* One-shot timeout for timer list */
::Timer::One_shot_timeout<Lx::Timer> _timers_one_shot { ::Timer::One_shot_timeout<Lx::Timer> _timers_one_shot {
_scheduler, *this, &Lx::Timer::_handle_timers }; _timer, *this, &Lx::Timer::_handle_timers };
/* One-shot timeout for 'wait' */ /* One-shot timeout for 'wait' */
::Timer::One_shot_timeout<Lx::Timer> _wait_one_shot { ::Timer::One_shot_timeout<Lx::Timer> _wait_one_shot {
_scheduler, *this, &Lx::Timer::_handle_wait }; _timer, *this, &Lx::Timer::_handle_wait };
Lx_kit::List<Context> _list; Lx_kit::List<Context> _list;
Genode::Tslab<Context, 32 * sizeof(Context)> _timer_alloc; Genode::Tslab<Context, 32 * sizeof(Context)> _timer_alloc;
@ -165,7 +165,7 @@ class Lx::Timer
{ {
auto new_jiffies = usecs_to_jiffies(dur.trunc_to_plain_us().value); auto new_jiffies = usecs_to_jiffies(dur.trunc_to_plain_us().value);
if (new_jiffies < jiffies) 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 else
jiffies = new_jiffies; jiffies = new_jiffies;
} }
@ -200,12 +200,12 @@ class Lx::Timer
* Constructor * Constructor
*/ */
Timer(Genode::Entrypoint &ep, Timer(Genode::Entrypoint &ep,
Genode::Timeout_scheduler &scheduler, ::Timer::Connection &timer,
Genode::Allocator &alloc, Genode::Allocator &alloc,
void (*tick)()) void (*tick)())
: :
_ep(ep), _ep(ep),
_scheduler(scheduler), _timer(timer),
_timer_alloc(&alloc), _timer_alloc(&alloc),
_tick(tick) _tick(tick)
{ {
@ -296,7 +296,7 @@ class Lx::Timer
* Do not use lx_emul usecs_to_jiffies(unsigned int) because * Do not use lx_emul usecs_to_jiffies(unsigned int) because
* of implicit truncation! * 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;
} }
/** /**
@ -327,10 +327,11 @@ static Lx::Timer *_timer;
void Lx::timer_init(Genode::Entrypoint &ep, void Lx::timer_init(Genode::Entrypoint &ep,
Genode::Timeout_scheduler &scheduler, ::Timer::Connection &timer,
Genode::Allocator &alloc, void (*tick)()) Genode::Allocator &alloc,
void (*tick)())
{ {
static Lx::Timer inst(ep, scheduler, alloc, tick); static Lx::Timer inst(ep, timer, alloc, tick);
_timer = &inst; _timer = &inst;
} }

View File

@ -18,7 +18,7 @@
#include <base/allocator.h> #include <base/allocator.h>
namespace Lwip { namespace Lwip {
void genode_init(Genode::Allocator &heap, Genode::Timeout_scheduler &timer); void genode_init(Genode::Allocator &heap, ::Timer::Connection &timer);
Genode::Mutex &mutex(); Genode::Mutex &mutex();
} }

View File

@ -42,18 +42,18 @@ namespace Lwip {
sys_check_timeouts(); sys_check_timeouts();
} }
Genode::Timeout_scheduler &timer; ::Timer::Connection &timer;
Timer::Periodic_timeout<Sys_timer> timeout { Timer::Periodic_timeout<Sys_timer> timeout {
timer, *this, &Sys_timer::check_timeouts, timer, *this, &Sys_timer::check_timeouts,
Genode::Microseconds{250*1000} }; 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; 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", LWIP_ASSERT("LwIP initialized with an allocator that does not track sizes",
!heap.need_size_for_free()); !heap.need_size_for_free());

View File

@ -50,6 +50,17 @@ static bool precise_time(Xml_node config)
return false; return false;
} }
struct A1 {
A1() {
log(__func__,__LINE__);
}
};
struct A2 {
A2() {
log(__func__,__LINE__);
}
};
struct Test struct Test
{ {
@ -58,6 +69,7 @@ struct Test
Signal_transmitter done; Signal_transmitter done;
unsigned id; unsigned id;
Attached_rom_dataspace config { env, "config" }; Attached_rom_dataspace config { env, "config" };
Timer::Connection timer { env }; Timer::Connection timer { env };
Test(Env &env, Test(Env &env,
@ -76,6 +88,7 @@ struct Test
* because Timer::Connection by now must be backwards compatible * because Timer::Connection by now must be backwards compatible
* and therefore starts interpolation only on demand. * and therefore starts interpolation only on demand.
*/ */
timer.curr_time(); timer.curr_time();
log("\nTEST ", id, ": ", brief, "\n"); 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);
}