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,8 +36,8 @@ Timer::Time_source::Time_source(Env &env)
}
void Timer::Time_source::schedule_timeout(Microseconds duration,
Timeout_handler &handler)
void Timer::Time_source::set_timeout(Microseconds duration,
Timeout_handler &handler)
{
Kernel::timeout_t duration_us = duration.value;
if (duration_us < MIN_TIMEOUT_US) {

View File

@ -25,8 +25,10 @@
namespace Timer {
using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration;
using Microseconds = Genode::Microseconds;
using Duration = Genode::Duration;
using Timeout_handler = Genode::Timeout_handler;
class Time_source;
}
@ -47,7 +49,7 @@ class Timer::Time_source : public Genode::Signalled_time_source
*************************/
Duration curr_time() override;
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
void set_timeout(Microseconds duration, Timeout_handler &handler) override;
Microseconds max_timeout() const override {
return Microseconds(_max_timeout_us); };
};

View File

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

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
* divisor by '_tsc_to_us'.
*/
Genode::warning("unable to obtain tsc frequency, asuming 1 GHz");
Genode::warning("unable to obtain tsc frequency, assuming 1 GHz");
return 1000*1000;
}
@ -77,7 +77,7 @@ class Timer::Time_source : public Threaded_time_source
** Threaded_time_source **
**************************/
void _wait_for_irq() override;
Result_of_wait_for_irq _wait_for_irq() override;
public:
@ -92,13 +92,12 @@ class Timer::Time_source : public Threaded_time_source
** Genode::Time_source **
*************************/
void schedule_timeout(Microseconds duration,
Timeout_handler &handler) override;
void set_timeout(Microseconds duration,
Timeout_handler &handler) override;
Microseconds max_timeout() const override
{
uint64_t const max_us = _tsc_to_us(~(uint64_t)0);
return max_us > ~(uint64_t)0 ? Microseconds(~(uint64_t)0) : Microseconds(max_us);
return Microseconds(_tsc_to_us(~(uint64_t)0));
}
Duration curr_time() override

View File

@ -21,38 +21,41 @@
/* Genode includes */
#include <util/noncopyable.h>
#include <util/list.h>
#include <base/duration.h>
#include <base/log.h>
#include <base/mutex.h>
#include <util/misc_math.h>
#include <base/blockade.h>
namespace Genode {
class Time_source;
class Timeout_scheduler;
class Timeout;
class Alarm_timeout_scheduler;
class Timeout_handler;
class Timeout_scheduler;
}
namespace Timer
{
namespace Timer {
class Connection;
class Root_component;
}
/**
* Interface of a timeout callback
*/
struct Genode::Timeout_handler : Interface
{
virtual void handle_timeout(Duration curr_time) = 0;
};
/**
* Interface of a time source that can handle one timeout at a time
*/
struct Genode::Time_source : Interface
{
/**
* Interface of a timeout callback
*/
struct Timeout_handler : Interface
{
virtual void handle_timeout(Duration curr_time) = 0;
};
/**
* Return the current time of the source
*/
@ -69,62 +72,8 @@ struct Genode::Time_source : Interface
* \param duration timeout duration
* \param handler timeout callback
*/
virtual void schedule_timeout(Microseconds duration,
Timeout_handler &handler) = 0;
/**
* Tell the time source which scheduler to use for its own timeouts
*
* This method enables a time source for example to synchronize with an
* accurate but expensive timer only on a periodic basis while using a
* cheaper interpolation in general.
*/
virtual void scheduler(Timeout_scheduler &) { };
};
/**
* Interface of a time-source multiplexer
*
* Beside 'curr_time()', this abstract interface is used by the Timeout
* implementation only. Users of the timeout framework must schedule and
* discard timeouts via methods of the timeout.
*/
class Genode::Timeout_scheduler : Interface
{
private:
friend Timeout;
/**
* Add a one-shot timeout to the schedule
*
* \param timeout timeout callback object
* \param duration timeout trigger delay
*/
virtual void _schedule_one_shot(Timeout &timeout, Microseconds duration) = 0;
/**
* Add a periodic timeout to the schedule
*
* \param timeout timeout callback object
* \param duration timeout trigger period
*/
virtual void _schedule_periodic(Timeout &timeout, Microseconds duration) = 0;
/**
* Remove timeout from the scheduler
*
* \param timeout corresponding timeout callback object
*/
virtual void _discard(Timeout &timeout) = 0;
public:
/**
* Read out the now time of the scheduler
*/
virtual Duration curr_time() = 0;
virtual void set_timeout(Microseconds duration,
Timeout_handler &handler) = 0;
};
@ -136,173 +85,116 @@ class Genode::Timeout_scheduler : Interface
* example, in a Timer-session server. If this is not the case, the classes
* Periodic_timeout and One_shot_timeout are the better choice.
*/
class Genode::Timeout : private Noncopyable
class Genode::Timeout : private Noncopyable,
public Genode::List<Timeout>::Element
{
friend class Alarm_timeout_scheduler;
public:
/**
* Interface of a timeout handler
*/
struct Handler : Interface
{
virtual void handle_timeout(Duration curr_time) = 0;
};
friend class Timeout_scheduler;
private:
class Alarm
{
friend class Alarm_timeout_scheduler;
Mutex _mutex { };
Timeout_scheduler &_scheduler;
Microseconds _period { 0 };
Microseconds _deadline { Microseconds { 0 } };
List_element<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;
struct Raw
{
Time deadline;
bool deadline_period;
Time period;
bool is_pending_at(uint64_t time, bool time_period) const;
};
Mutex _dispatch_mutex { };
Raw _raw { };
short _active { 0 };
bool _delete { false };
Alarm *_next { nullptr };
Alarm_timeout_scheduler *_scheduler { nullptr };
void _alarm_assign(Time period,
Time deadline,
bool deadline_period,
Alarm_timeout_scheduler *scheduler)
{
_raw.period = period;
_raw.deadline_period = deadline_period;
_raw.deadline = deadline;
_scheduler = scheduler;
}
void _alarm_reset() { _alarm_assign(0, 0, false, 0), _active = 0, _next = 0; }
bool _on_alarm(uint64_t);
Alarm(Alarm const &);
Alarm &operator = (Alarm const &);
public:
Timeout_scheduler &timeout_scheduler;
Handler *handler = nullptr;
bool periodic = false;
Alarm(Timeout_scheduler &timeout_scheduler)
: timeout_scheduler(timeout_scheduler) { _alarm_reset(); }
virtual ~Alarm();
} _alarm;
Timeout &operator = (Timeout const &);
public:
Timeout(Timeout_scheduler &timeout_scheduler)
: _alarm(timeout_scheduler) { }
Timeout(Timeout_scheduler &scheduler);
~Timeout() { discard(); }
Timeout(Timer::Connection &timer_connection);
void schedule_periodic(Microseconds duration, Handler &handler);
~Timeout();
void schedule_one_shot(Microseconds duration, Handler &handler);
void schedule_periodic(Microseconds duration,
Timeout_handler &handler);
void schedule_one_shot(Microseconds duration,
Timeout_handler &handler);
void discard();
bool scheduled() { return _alarm.handler != nullptr; }
bool scheduled();
};
/**
* Timeout-scheduler implementation using the Alarm framework
* Multiplexes one time source amongst different timeouts
*/
class Genode::Alarm_timeout_scheduler : private Noncopyable,
public Timeout_scheduler,
public Time_source::Timeout_handler
class Genode::Timeout_scheduler : private Noncopyable,
public Timeout_handler
{
friend class Timer::Connection;
friend class Timer::Root_component;
friend class Timeout::Alarm;
friend class Timeout;
private:
using Alarm = Timeout::Alarm;
static constexpr uint64_t max_sleep_time_us { 60'000'000 };
Time_source &_time_source;
Mutex _mutex { };
Alarm *_active_head { nullptr };
Alarm *_pending_head { nullptr };
Alarm::Time _now { 0UL };
bool _now_period { false };
Alarm::Raw _min_handle_period { };
Mutex _mutex { };
Time_source &_time_source;
Microseconds const _max_sleep_time { min(_time_source.max_timeout().value, max_sleep_time_us) };
List<Timeout> _timeouts { };
Microseconds _current_time { 0 };
bool _destructor_called { false };
Microseconds _rate_limit_period;
Microseconds _rate_limit_deadline;
void _alarm_unsynchronized_enqueue(Alarm *alarm);
void _insert_into_timeouts_list(Timeout &timeout);
void _alarm_unsynchronized_dequeue(Alarm *alarm);
void _set_time_source_timeout();
Alarm *_alarm_get_pending_alarm();
void _set_time_source_timeout(uint64_t duration_us);
void _alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration);
void _schedule_timeout(Timeout &timeout,
Microseconds duration,
Microseconds period,
Timeout_handler &handler);
void _discard_timeout_unsynchronized(Timeout &timeout);
void _enable();
void _schedule_one_shot_timeout(Timeout &timeout,
Microseconds duration,
Timeout_handler &handler);
/**********************************
** Time_source::Timeout_handler **
**********************************/
void _schedule_periodic_timeout(Timeout &timeout,
Microseconds period,
Timeout_handler &handler);
void _discard_timeout(Timeout &timeout);
void _destruct_timeout(Timeout &timeout);
Timeout_scheduler(Timeout_scheduler const &);
Timeout_scheduler &operator = (Timeout_scheduler const &);
/*********************
** Timeout_handler **
*********************/
void handle_timeout(Duration curr_time) override;
/***********************
** Timeout_scheduler **
***********************/
void _schedule_one_shot(Timeout &timeout, Microseconds duration) override;
void _schedule_periodic(Timeout &timeout, Microseconds duration) override;
void _discard(Timeout &timeout) override {
_alarm_discard(&timeout._alarm); }
void _alarm_discard(Alarm *alarm);
void _alarm_schedule_absolute(Alarm *alarm, Alarm::Time duration);
void _alarm_schedule(Alarm *alarm, Alarm::Time period);
void _alarm_handle(Alarm::Time now);
bool _alarm_next_deadline(Alarm::Time *deadline);
bool _alarm_head_timeout(const Alarm * alarm) { return _active_head == alarm; }
Alarm_timeout_scheduler(Alarm_timeout_scheduler const &);
Alarm_timeout_scheduler &operator = (Alarm_timeout_scheduler const &);
public:
Alarm_timeout_scheduler(Time_source &time_source,
Microseconds min_handle_period = Microseconds(1));
Timeout_scheduler(Time_source &time_source,
Microseconds min_handle_period);
~Alarm_timeout_scheduler();
~Timeout_scheduler();
/***********************
** Timeout_scheduler **
***********************/
Duration curr_time() override { return _time_source.curr_time(); }
Duration curr_time();
};
#endif /* _TIMER__TIMEOUT_H_ */

View File

@ -34,50 +34,42 @@ namespace Timer
* Periodic timeout that is linked to a custom handler, scheduled when constructed
*/
template <typename HANDLER>
struct Timer::Periodic_timeout : private Genode::Noncopyable
struct Timer::Periodic_timeout : private Genode::Noncopyable,
private Genode::Timeout_handler
{
private:
using Duration = Genode::Duration;
using Timeout = Genode::Timeout;
using Timeout_scheduler = Genode::Timeout_scheduler;
using Microseconds = Genode::Microseconds;
typedef void (HANDLER::*Handler_method)(Duration);
Timeout _timeout;
struct Handler : Timeout::Handler
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
Timeout _timeout;
HANDLER &_object;
Handler_method const _method;
/**********************
** Timeout::Handler **
**********************/
/*********************
** Timeout_handler **
*********************/
void handle_timeout(Duration curr_time) override {
(object.*method)(curr_time); }
} _handler;
void handle_timeout(Duration curr_time) override {
(_object.*_method)(curr_time); }
public:
Periodic_timeout(Timeout_scheduler &timeout_scheduler,
HANDLER &object,
Handler_method method,
Microseconds duration)
Periodic_timeout(Connection &timer,
HANDLER &object,
Handler_method method,
Microseconds duration)
:
_timeout(timeout_scheduler), _handler(object, method)
_timeout { timer },
_object { object },
_method { method }
{
_timeout.schedule_periodic(duration, _handler);
_timeout.schedule_periodic(duration, *this);
}
~Periodic_timeout() { _timeout.discard(); }
};
@ -85,48 +77,42 @@ struct Timer::Periodic_timeout : private Genode::Noncopyable
* One-shot timeout that is linked to a custom handler, scheduled manually
*/
template <typename HANDLER>
class Timer::One_shot_timeout : private Genode::Noncopyable
class Timer::One_shot_timeout : private Genode::Noncopyable,
private Genode::Timeout_handler
{
private:
using Duration = Genode::Duration;
using Timeout = Genode::Timeout;
using Timeout_scheduler = Genode::Timeout_scheduler;
using Microseconds = Genode::Microseconds;
typedef void (HANDLER::*Handler_method)(Duration);
Timeout _timeout;
struct Handler : Timeout::Handler
{
HANDLER &object;
Handler_method const method;
Handler(HANDLER &object, Handler_method method)
: object(object), method(method) { }
Timeout _timeout;
HANDLER &_object;
Handler_method const _method;
/**********************
** Timeout::Handler **
**********************/
/*********************
** Timeout_handler **
*********************/
void handle_timeout(Duration curr_time) override {
(object.*method)(curr_time); }
} _handler;
void handle_timeout(Duration curr_time) override {
(_object.*_method)(curr_time); }
public:
One_shot_timeout(Timeout_scheduler &timeout_scheduler,
HANDLER &object,
Handler_method method)
: _timeout(timeout_scheduler), _handler(object, method) { }
~One_shot_timeout() { _timeout.discard(); }
One_shot_timeout(Connection &timer,
HANDLER &object,
Handler_method method)
:
_timeout { timer },
_object { object },
_method { method }
{ }
void schedule(Microseconds duration) {
_timeout.schedule_one_shot(duration, _handler); }
_timeout.schedule_one_shot(duration, *this); }
void discard() { _timeout.discard(); }
@ -141,19 +127,21 @@ class Timer::One_shot_timeout : private Genode::Noncopyable
*/
class Timer::Connection : public Genode::Connection<Session>,
public Session_client,
private Genode::Time_source,
public Genode::Timeout_scheduler
private Genode::Time_source
{
friend class Genode::Timeout;
private:
using Timeout = Genode::Timeout;
using Timeout_handler = Genode::Time_source::Timeout_handler;
using Timestamp = Genode::Trace::Timestamp;
using Duration = Genode::Duration;
using Mutex = Genode::Mutex;
using Microseconds = Genode::Microseconds;
using Milliseconds = Genode::Milliseconds;
using Entrypoint = Genode::Entrypoint;
using Timeout = Genode::Timeout;
using Timeout_handler = Genode::Timeout_handler;
using Timestamp = Genode::Trace::Timestamp;
using Duration = Genode::Duration;
using Mutex = Genode::Mutex;
using Microseconds = Genode::Microseconds;
using Milliseconds = Genode::Milliseconds;
using Entrypoint = Genode::Entrypoint;
using Io_signal_handler = Genode::Io_signal_handler<Connection>;
/*
* Noncopyable
@ -163,19 +151,34 @@ class Timer::Connection : public Genode::Connection<Session>,
/*
* The mode determines which interface of the timer connection is
* enabled. Initially, a timer connection is in LEGACY mode. When in
* MODERN mode, a call to the LEGACY interface causes an exception.
* When in LEGACY mode, a call to the MODERN interface causes a switch
* to the MODERN mode. The LEGACY interface is deprecated. Please
* prefer using the MODERN interface.
* enabled. Initially, a timer connection is in TIMER_SESSION mode.
* In this mode, the user can operate directly on the connection using
* the methods of the timer-session interface. As soon as the
* connection is handed over as argument to the constructor of a
* Periodic_timeout or a One_shot_timeout, it switches to
* TIMEOUT_FRAMEWORK mode. From this point on, the only method that
* the user can use directly on the connection is 'curr_time'. For
* the rest of the functionality he rather uses the interfaces of
* timeout objects that reference the connection.
*
* LEGACY = Timer Session interface, blocking calls usleep and msleep
* MODERN = more precise curr_time, non-blocking and multiplexed
* handling with Periodic_timeout resp. One_shot_timeout
* These are the characteristics of the two modes:
*
* TIMER_SESSION:
*
* * Allows for both blocking and non-blocking timeout semantics.
* * Missing local interpolation leads to less precise curr_time
* results.
* * Only one timeout at a time per connection.
*
* TIMEOUT FRAMEWORK:
*
* * Supports only non-blocking timeout semantics.
* * More precise curr_time results through local interpolation.
* * Multiplexing of multiple timeouts at the same connection
*/
enum Mode { LEGACY, MODERN };
enum Mode { TIMER_SESSION, TIMEOUT_FRAMEWORK };
Mode _mode { LEGACY };
Mode _mode { TIMER_SESSION };
Mutex _mutex { };
Genode::Signal_receiver _sig_rec { };
Genode::Signal_context _default_sigh_ctx { };
@ -185,17 +188,14 @@ class Timer::Connection : public Genode::Connection<Session>,
Genode::Signal_context_capability _custom_sigh_cap { };
void _enable_modern_mode();
void _sigh(Signal_context_capability sigh)
{
Session_client::sigh(sigh);
}
/*************************
** Time_source helpers **
*************************/
/****************************************************
** Members for interaction with Timeout framework **
****************************************************/
enum { MIN_TIMEOUT_US = 5000 };
enum { REAL_TIME_UPDATE_PERIOD_US = 500000 };
@ -205,17 +205,19 @@ class Timer::Connection : public Genode::Connection<Session>,
enum { NR_OF_INITIAL_CALIBRATIONS = 3 * MAX_INTERPOLATION_QUALITY };
enum { MIN_FACTOR_LOG2 = 8 };
Genode::Io_signal_handler<Connection> _signal_handler;
Io_signal_handler _signal_handler;
Timeout_handler *_handler { nullptr };
Mutex _real_time_mutex { };
uint64_t _us { elapsed_us() };
Timestamp _ts { _timestamp() };
Duration _real_time { Microseconds { _us } };
Duration _interpolated_time { _real_time };
unsigned _interpolation_quality { 0 };
uint64_t _us_to_ts_factor { 1 };
unsigned _us_to_ts_factor_shift { 0 };
Genode::Timeout_scheduler _timeout_scheduler { *this, Microseconds { 1 } };
Timeout_handler *_handler { nullptr };
Mutex _real_time_mutex { };
uint64_t _us { elapsed_us() };
Timestamp _ts { _timestamp() };
Duration _real_time { Microseconds(_us) };
Duration _interpolated_time { _real_time };
unsigned _interpolation_quality { 0 };
uint64_t _us_to_ts_factor { 1UL };
unsigned _us_to_ts_factor_shift { 0 };
Genode::Timeout_scheduler &_switch_to_timeout_framework_mode();
Timestamp _timestamp();
@ -237,28 +239,12 @@ class Timer::Connection : public Genode::Connection<Session>,
** Time_source **
*****************/
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
void set_timeout(Microseconds duration, Timeout_handler &handler) override;
Microseconds max_timeout() const override { return Microseconds(REAL_TIME_UPDATE_PERIOD_US); }
/*******************************
** Timeout_scheduler helpers **
*******************************/
Genode::Alarm_timeout_scheduler _scheduler { *this };
/***********************
** Timeout_scheduler **
***********************/
void _schedule_one_shot(Timeout &timeout, Microseconds duration) override;
void _schedule_periodic(Timeout &timeout, Microseconds duration) override;
void _discard(Timeout &timeout) override;
public:
struct Cannot_use_both_legacy_and_modern_interface : Genode::Exception { };
struct Method_cannot_be_used_in_timeout_framework_mode : Genode::Exception { };
/**
* Constructor
@ -287,8 +273,8 @@ class Timer::Connection : public Genode::Connection<Session>,
*/
void sigh(Signal_context_capability sigh) override
{
if (_mode == MODERN) {
throw Cannot_use_both_legacy_and_modern_interface();
if (_mode == TIMEOUT_FRAMEWORK) {
throw Method_cannot_be_used_in_timeout_framework_mode();
}
_custom_sigh_cap = sigh;
Session_client::sigh(_custom_sigh_cap);
@ -302,8 +288,8 @@ class Timer::Connection : public Genode::Connection<Session>,
*/
void usleep(uint64_t us) override
{
if (_mode == MODERN) {
throw Cannot_use_both_legacy_and_modern_interface();
if (_mode == TIMEOUT_FRAMEWORK) {
throw Method_cannot_be_used_in_timeout_framework_mode();
}
/*
* Omit the interaction with the timer driver for the corner case
@ -340,16 +326,16 @@ class Timer::Connection : public Genode::Connection<Session>,
*/
void msleep(uint64_t ms) override
{
if (_mode == MODERN) {
throw Cannot_use_both_legacy_and_modern_interface();
if (_mode == TIMEOUT_FRAMEWORK) {
throw Method_cannot_be_used_in_timeout_framework_mode();
}
usleep(1000*ms);
}
/***********************************
** Timeout_scheduler/Time_source **
***********************************/
/*****************
** Time_source **
*****************/
Duration curr_time() override;
};

View File

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

View File

@ -13,6 +13,7 @@
/* Genode includes */
#include <timer/timeout.h>
#include <timer_session/connection.h>
using namespace Genode;
@ -21,419 +22,386 @@ using namespace Genode;
** Timeout **
*************/
void Timeout::schedule_periodic(Microseconds duration, Handler &handler)
void Timeout::schedule_periodic(Microseconds duration,
Timeout_handler &handler)
{
_alarm.handler = &handler;
_alarm.periodic = true;
_alarm.timeout_scheduler._schedule_periodic(*this, duration);
_scheduler._schedule_periodic_timeout(*this, duration, handler);
}
void Timeout::schedule_one_shot(Microseconds duration, Handler &handler)
void Timeout::schedule_one_shot(Microseconds duration,
Timeout_handler &handler)
{
_alarm.handler = &handler;
_alarm.periodic = false;
_alarm.timeout_scheduler._schedule_one_shot(*this, duration);
_scheduler._schedule_one_shot_timeout(*this, duration, handler);
}
void Timeout::discard()
{
_alarm.timeout_scheduler._discard(*this);
_alarm.handler = nullptr;
}
/********************
** Timeout::Alarm **
********************/
bool Timeout::Alarm::_on_alarm(uint64_t)
{
if (handler) {
Handler *current = handler;
if (!periodic) {
handler = nullptr;
}
current->handle_timeout(timeout_scheduler.curr_time());
}
return periodic;
}
Timeout::Alarm::~Alarm()
{
if (_scheduler)
_scheduler->_alarm_discard(this);
}
bool Timeout::Alarm::Raw::is_pending_at(uint64_t time, bool time_period) const
{
return (time_period == deadline_period &&
time >= deadline) ||
(time_period != deadline_period &&
time < deadline);
}
/*****************************
** Alarm_timeout_scheduler **
*****************************/
void Alarm_timeout_scheduler::handle_timeout(Duration duration)
{
uint64_t const curr_time_us = duration.trunc_to_plain_us().value;
_alarm_handle(curr_time_us);
/* sleep time is either until the next deadline or the maximum timout */
uint64_t sleep_time_us;
Alarm::Time deadline_us;
if (_alarm_next_deadline(&deadline_us)) {
sleep_time_us = deadline_us - curr_time_us;
} else {
sleep_time_us = _time_source.max_timeout().value; }
/* limit max timeout to a more reasonable value, e.g. 60s */
if (sleep_time_us > 60000000) {
sleep_time_us = 60000000;
} else if (sleep_time_us == 0) {
sleep_time_us = 1; }
_time_source.schedule_timeout(Microseconds(sleep_time_us), *this);
}
Alarm_timeout_scheduler::Alarm_timeout_scheduler(Time_source &time_source,
Microseconds min_handle_period)
Timeout::Timeout(Timeout_scheduler &scheduler)
:
_time_source(time_source)
_scheduler(scheduler)
{ }
Timeout::Timeout(Timer::Connection &timer_connection)
:
_scheduler(timer_connection._switch_to_timeout_framework_mode())
{ }
Timeout::~Timeout() { _scheduler._destruct_timeout(*this); }
void Timeout::discard() { _scheduler._discard_timeout(*this); }
bool Timeout::scheduled() { return _handler != nullptr; }
/***********************
** Timeout_scheduler **
***********************/
void Timeout_scheduler::handle_timeout(Duration curr_time)
{
Alarm::Time const deadline = _now + min_handle_period.value;
_min_handle_period.period = min_handle_period.value;
_min_handle_period.deadline = deadline;
_min_handle_period.deadline_period = _now > deadline ?
!_now_period : _now_period;
}
Alarm_timeout_scheduler::~Alarm_timeout_scheduler()
{
Mutex::Guard mutex_guard(_mutex);
while (_active_head) {
Alarm *next = _active_head->_next;
_active_head->_alarm_reset();
_active_head = next;
}
}
void Alarm_timeout_scheduler::_enable()
{
_time_source.schedule_timeout(Microseconds(0), *this);
}
void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout,
Microseconds duration)
{
/* raise timeout duration by the age of the local time value */
uint64_t us = _time_source.curr_time().trunc_to_plain_us().value;
if (us >= _now) {
us = duration.value + (us - _now); }
else {
us = duration.value + (~0UL - _now) + us; }
if (us >= duration.value) {
duration.value = us; }
/* insert timeout into scheduling queue */
_alarm_schedule_absolute(&timeout._alarm, duration.value);
/* if new timeout is the closest to now, update the time-source timeout */
if (_alarm_head_timeout(&timeout._alarm)) {
_time_source.schedule_timeout(Microseconds(0), *this); }
}
void Alarm_timeout_scheduler::_schedule_periodic(Timeout &timeout,
Microseconds duration)
{
_alarm_schedule(&timeout._alarm, duration.value);
if (_alarm_head_timeout(&timeout._alarm)) {
_time_source.schedule_timeout(Microseconds(0), *this); }
}
void Alarm_timeout_scheduler::_alarm_unsynchronized_enqueue(Alarm *alarm)
{
if (alarm->_active) {
error("trying to insert the same alarm twice!");
return;
}
alarm->_active++;
/* if active alarm list is empty add first element */
if (!_active_head) {
alarm->_next = 0;
_active_head = alarm;
return;
}
/* if deadline is smaller than any other deadline, put it on the head */
if (alarm->_raw.is_pending_at(_active_head->_raw.deadline, _active_head->_raw.deadline_period)) {
alarm->_next = _active_head;
_active_head = alarm;
return;
}
/* find list element with a higher deadline */
Alarm *curr = _active_head;
while (curr->_next &&
curr->_next->_raw.is_pending_at(alarm->_raw.deadline, alarm->_raw.deadline_period))
List<List_element<Timeout> > pending_timeouts { };
{
curr = curr->_next;
}
/* acquire scheduler and update stored current time */
Mutex::Guard const scheduler_guard(_mutex);
if (_destructor_called) {
return;
}
_current_time = curr_time.trunc_to_plain_us();
/* if end of list is reached, append new element */
if (curr->_next == 0) {
curr->_next = alarm;
return;
}
/* apply rate limit to the handling of timeouts */
if (_current_time.value < _rate_limit_deadline.value) {
/* insert element in middle of list */
alarm->_next = curr->_next;
curr->_next = alarm;
}
_time_source.set_timeout(
Microseconds { _rate_limit_deadline.value -
_current_time.value },
*this);
void Alarm_timeout_scheduler::_alarm_unsynchronized_dequeue(Alarm *alarm)
{
if (!_active_head) return;
if (_active_head == alarm) {
_active_head = alarm->_next;
alarm->_alarm_reset();
return;
}
/* find predecessor in alarm queue */
Alarm *curr;
for (curr = _active_head; curr && (curr->_next != alarm); curr = curr->_next);
/* alarm is not enqueued */
if (!curr) return;
/* remove alarm from alarm queue */
curr->_next = alarm->_next;
alarm->_alarm_reset();
}
Timeout::Alarm *Alarm_timeout_scheduler::_alarm_get_pending_alarm()
{
Mutex::Guard mutex_guard(_mutex);
do {
if (!_active_head || !_active_head->_raw.is_pending_at(_now, _now_period)) {
return nullptr; }
/* remove alarm from head of the list */
Alarm *pending_alarm = _active_head;
_active_head = _active_head->_next;
return;
}
_rate_limit_deadline.value = _current_time.value +
_rate_limit_period.value;
/*
* Acquire dispatch mutex to defer destruction until the call of '_on_alarm'
* is finished
* Filter out all pending timeouts to a local list first. The
* processing of pending timeouts can have effects on the '_timeouts'
* list and these would interfere with the filtering if we would do
* it all in the same loop.
*/
pending_alarm->_dispatch_mutex.acquire();
while (Timeout *timeout = _timeouts.first()) {
/* reset alarm object */
pending_alarm->_next = nullptr;
pending_alarm->_active--;
if (pending_alarm->_delete) {
pending_alarm->_dispatch_mutex.release();
continue;
timeout->_mutex.acquire();
if (timeout->_deadline.value > _current_time.value) {
timeout->_mutex.release();
break;
}
_timeouts.remove(timeout);
pending_timeouts.insert(&timeout->_pending_timeouts_le);
}
return pending_alarm;
} while (true);
/*
* Do the framework-internal processing of the pending timeouts and
* then release their mutexes.
*/
for (List_element<Timeout> const *elem { pending_timeouts.first() };
elem != nullptr;
elem = elem->next()) {
Timeout &timeout { *elem->object() };
if (!timeout._in_discard_blockade) {
/*
* Remember the handler in an extra member that is altered
* only by this code path. This enables us to release the
* mutexes of all pending timeouts before starting to call
* the timeout handlers. This is necessary to prevent
* deadlocks in a situation where multiple timeouts become
* pending at once, the handler of the first pending
* timeout is about to re-schedule his timeout, and then
* a second thread calls 'discard' on another pending
* timeout just before that handlers call to 'schedule'.
*/
timeout._pending_handler = timeout._handler;
} else {
/*
* Another thread, that wants to discard the timeout, has been
* waiting for a prior call to the timeout handler to finish.
* It has already been unblocked again but couldn't continue
* discarding the timeout yet. Therefore, we refrain from
* calling the timeout handler again until the other thread
* could complete its task.
*/
pending_timeouts.remove(elem);
}
if (timeout._period.value == 0) {
/* discard one-shot timeouts */
timeout._handler = nullptr;
} else {
/* determine new timeout deadline */
uint64_t const nr_of_periods {
((_current_time.value - timeout._deadline.value) /
timeout._period.value) + 1 };
uint64_t deadline_us { timeout._deadline.value +
nr_of_periods * timeout._period.value };
if (deadline_us < _current_time.value) {
deadline_us = ~(uint64_t)0;
}
/* re-insert timeout into timeouts list */
timeout._deadline = Microseconds { deadline_us };
_insert_into_timeouts_list(timeout);
}
timeout._mutex.release();
}
_set_time_source_timeout();
}
/* call the handler of each pending timeout */
while (List_element<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();
}
}
}
void Alarm_timeout_scheduler::_alarm_handle(Alarm::Time curr_time)
Timeout_scheduler::Timeout_scheduler(Time_source &time_source,
Microseconds rate_limit_period)
:
_time_source { time_source },
_rate_limit_period { rate_limit_period },
_rate_limit_deadline { Microseconds { _current_time.value +
rate_limit_period.value } }
{ }
Timeout_scheduler::~Timeout_scheduler()
{
/*
* Raise the time counter and if it wraps, update also in which
* period of the time counter we are.
* Acquire the scheduler mutex and don't release it at the end of this
* function to ease debugging in case that someone accesses a dangling
* scheduler pointer.
*/
if (_now > curr_time) {
_now_period = !_now_period;
}
_now = curr_time;
_mutex.acquire();
if (!_min_handle_period.is_pending_at(_now, _now_period)) {
/*
* The function 'Timeout_scheduler::_discard_timeout_unsynchronized' may
* have to release and re-acquire the scheduler mutex due to pending
* timeout handlers. But, nonetheless, we don't want others to schedule
* or discard timeouts while we are emptying the timeout list. Setting
* the flag '_destructor_called' causes such attempts to finish without
* effect.
*/
_destructor_called = true;
/* discard all scheduled timeouts */
while (Timeout *timeout = _timeouts.first()) {
Mutex::Guard const timeout_guard { timeout->_mutex };
_discard_timeout_unsynchronized(*timeout);
}
}
void Timeout_scheduler::_enable()
{
Mutex::Guard const scheduler_guard { _mutex };
if (_destructor_called) {
return;
}
Alarm::Time const deadline = _now + _min_handle_period.period;
_min_handle_period.deadline = deadline;
_min_handle_period.deadline_period = _now > deadline ?
!_now_period : _now_period;
_set_time_source_timeout();
}
/*
* Dequeue all pending alarms before starting to re-schedule. Otherwise,
* a long-lasting alarm that has a deadline in the next now_period might
* get scheduled as head of this now_period falsely because the code
* thinks that it belongs to the last now_period.
*/
while (Alarm *curr = _alarm_get_pending_alarm()) {
/* enqueue alarm into list of pending alarms */
curr->_next = _pending_head;
_pending_head = curr;
void Timeout_scheduler::_set_time_source_timeout()
{
_set_time_source_timeout(
_timeouts.first() ?
_timeouts.first()->_deadline.value - _current_time.value :
~(uint64_t)0);
}
void Timeout_scheduler::_set_time_source_timeout(uint64_t duration_us)
{
if (duration_us < _rate_limit_period.value) {
duration_us = _rate_limit_period.value;
}
while (Alarm *curr = _pending_head) {
/* dequeue alarm from list of pending alarms */
_pending_head = _pending_head->_next;
curr->_next = nullptr;
uint64_t triggered = 1;
if (curr->_raw.period) {
Alarm::Time deadline = curr->_raw.deadline;
/* schedule next event */
if (deadline == 0)
deadline = curr_time;
triggered += (curr_time - deadline) / curr->_raw.period;
}
/* do not reschedule if alarm function returns 0 */
bool reschedule = curr->_on_alarm(triggered);
if (reschedule) {
/*
* At this point, the alarm deadline normally is somewhere near
* the current time but If the alarm had no deadline by now,
* initialize it with the current time.
*/
if (curr->_raw.deadline == 0) {
curr->_raw.deadline = _now;
curr->_raw.deadline_period = _now_period;
}
/*
* Raise the deadline value by one period of the alarm and
* if the deadline value wraps thereby, update also in which
* period it is located.
*/
Alarm::Time const deadline = curr->_raw.deadline +
triggered * curr->_raw.period;
if (curr->_raw.deadline > deadline) {
curr->_raw.deadline_period = !curr->_raw.deadline_period;
}
curr->_raw.deadline = deadline;
/* synchronize enqueue operation */
Mutex::Guard mutex_guard(_mutex);
_alarm_unsynchronized_enqueue(curr);
}
/* release alarm, resume concurrent destructor operation */
curr->_dispatch_mutex.release();
if (duration_us > _max_sleep_time.value) {
duration_us = _max_sleep_time.value;
}
_time_source.set_timeout(Microseconds(duration_us), *this);
}
void Alarm_timeout_scheduler::_alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration)
void Timeout_scheduler::_schedule_one_shot_timeout(Timeout &timeout,
Microseconds duration,
Timeout_handler &handler)
{
/*
* If the alarm is already present in the queue, re-consider its queue
* position because its deadline might have changed. I.e., if an alarm is
* rescheduled with a new timeout before the original timeout triggered.
*/
if (alarm._active)
_alarm_unsynchronized_dequeue(&alarm);
Alarm::Time deadline = _now + first_duration;
alarm._alarm_assign(period, deadline, _now > deadline ? !_now_period : _now_period, this);
_alarm_unsynchronized_enqueue(&alarm);
_schedule_timeout(timeout, duration, Microseconds { 0 }, handler);
}
void Alarm_timeout_scheduler::_alarm_schedule_absolute(Alarm *alarm, Alarm::Time duration)
void Timeout_scheduler::_schedule_periodic_timeout(Timeout &timeout,
Microseconds period,
Timeout_handler &handler)
{
Mutex::Guard alarm_list_guard(_mutex);
_alarm_setup_alarm(*alarm, 0, duration);
/* prevent using a period of 0 */
if (period.value == 0) {
period.value = 1;
}
_schedule_timeout(timeout, Microseconds { 0 }, period, handler);
}
void Alarm_timeout_scheduler::_alarm_schedule(Alarm *alarm, Alarm::Time period)
void Timeout_scheduler::_schedule_timeout(Timeout &timeout,
Microseconds duration,
Microseconds period,
Timeout_handler &handler)
{
Mutex::Guard alarm_list_guard(_mutex);
/*
* Refuse to schedule a periodic timeout of 0 because it would trigger
* infinitely in the 'handle' function. To account for the case where the
* alarm object was already scheduled, we make sure to remove it from the
* queue.
*/
if (period == 0) {
_alarm_unsynchronized_dequeue(alarm);
/* acquire scheduler and timeout mutex */
Mutex::Guard const scheduler_guard { _mutex };
if (_destructor_called) {
return;
}
Mutex::Guard const timeout_guard(timeout._mutex);
/* first deadline is overdue */
_alarm_setup_alarm(*alarm, period, 0);
}
/* prevent inserting a timeout twice */
if (timeout._handler != nullptr) {
_timeouts.remove(&timeout);
}
/* determine timeout deadline */
uint64_t const curr_time_us {
_time_source.curr_time().trunc_to_plain_us().value };
uint64_t const deadline_us {
duration.value <= ~(uint64_t)0 - curr_time_us ?
curr_time_us + duration.value : ~(uint64_t)0 };
/* set up timeout object and insert into timeouts list */
timeout._handler = &handler;
timeout._deadline = Microseconds { deadline_us };
timeout._period = period;
_insert_into_timeouts_list(timeout);
void Alarm_timeout_scheduler::_alarm_discard(Alarm *alarm)
{
/*
* Make sure that nobody is inside the '_alarm_get_pending_alarm' when
* grabbing the '_dispatch_mutex'. This is important when this function
* is called from the 'Alarm' destructor. Without the '_dispatch_mutex',
* we could take the mutex and proceed with destruction just before
* '_alarm_get_pending_alarm' tries to grab the mutex. When the destructor
* is finished, '_alarm_get_pending_alarm' would proceed with operating on
* a dangling pointer.
* If the new timeout is the first to trigger, we have to update the
* time-source timeout.
*/
if (alarm) {
{
/* inform that this object is going to be deleted */
Mutex::Guard alarm_guard(alarm->_dispatch_mutex);
alarm->_delete = true;
}
{
Mutex::Guard alarm_list_guard(_mutex);
_alarm_unsynchronized_dequeue(alarm);
}
/* get anyone using this out of '_alarm_get_pending_alarm'() finally */
Mutex::Guard alarm_guard(alarm->_dispatch_mutex);
alarm->_delete = false;
if (_timeouts.first() == &timeout) {
_set_time_source_timeout(deadline_us - curr_time_us);
}
}
bool Alarm_timeout_scheduler::_alarm_next_deadline(Alarm::Time *deadline)
void Timeout_scheduler::_insert_into_timeouts_list(Timeout &timeout)
{
Mutex::Guard alarm_list_guard(_mutex);
if (!_active_head) return false;
if (deadline)
*deadline = _active_head->_raw.deadline;
if (*deadline < _min_handle_period.deadline) {
*deadline = _min_handle_period.deadline;
/* if timeout list is empty, insert as first element */
if (_timeouts.first() == nullptr) {
_timeouts.insert(&timeout);
return;
}
return true;
/* if timeout has the shortest deadline, insert as first element */
if (_timeouts.first()->_deadline.value >= timeout._deadline.value) {
_timeouts.insert(&timeout);
return;
}
/* find list element with next shorter deadline and insert behind it */
Timeout *curr_timeout { _timeouts.first() };
for (;
curr_timeout->next() != nullptr &&
curr_timeout->next()->_deadline.value < timeout._deadline.value;
curr_timeout = curr_timeout->_next);
_timeouts.insert(&timeout, curr_timeout);
}
void Timeout_scheduler::_discard_timeout(Timeout &timeout)
{
Mutex::Guard const scheduler_mutex { _mutex };
Mutex::Guard const timeout_mutex { timeout._mutex };
_discard_timeout_unsynchronized(timeout);
}
void Timeout_scheduler::_destruct_timeout(Timeout &timeout)
{
Mutex::Guard const scheduler_mutex { _mutex };
/*
* Acquire the timeout mutex and don't release it at the end of this
* function to ease debugging in case that someone accesses a dangling
* timeout pointer.
*/
timeout._mutex.acquire();
_discard_timeout_unsynchronized(timeout);
}
void Timeout_scheduler::_discard_timeout_unsynchronized(Timeout &timeout)
{
if (timeout._pending_handler != nullptr) {
if (timeout._in_discard_blockade) {
error("timeout is getting discarded by multiple threads");
}
/*
* We cannot discard a timeout whose handler is currently executed. We
* rather set its flag '_in_discard_blockade' (this ensures that the
* timeout handler is not getting called again) and then wait for the
* current handler call to finish. 'Timeout_scheduler::handle_timeout'
* will wake us up as soon as the handler returned.
*/
timeout._in_discard_blockade = true;
timeout._mutex.release();
_mutex.release();
timeout._discard_blockade.block();
_mutex.acquire();
timeout._mutex.acquire();
timeout._in_discard_blockade = false;
}
_timeouts.remove(&timeout);
timeout._handler = nullptr;
}
Duration Timeout_scheduler::curr_time()
{
Mutex::Guard const scheduler_guard { _mutex };
if (_destructor_called) {
return Duration { Microseconds { 0 } };
}
return _time_source.curr_time();
}

View File

@ -97,8 +97,8 @@ void Timer::Connection::_handle_timeout()
}
void Timer::Connection::schedule_timeout(Microseconds duration,
Timeout_handler &handler)
void Timer::Connection::set_timeout(Microseconds duration,
Timeout_handler &handler)
{
if (duration.value < MIN_TIMEOUT_US)
duration.value = MIN_TIMEOUT_US;
@ -111,22 +111,6 @@ void Timer::Connection::schedule_timeout(Microseconds duration,
}
void Timer::Connection::_enable_modern_mode()
{
if (_mode == MODERN) {
return;
}
_mode = MODERN;
_sigh(_signal_handler);
_scheduler._enable();
/* do initial calibration burst to make interpolation available earlier */
for (unsigned i = 0; i < NR_OF_INITIAL_CALIBRATIONS; i++) {
_update_real_time();
}
}
Timer::Connection::Connection(Genode::Env &env, Genode::Entrypoint &ep,
char const *label)
:
@ -145,22 +129,20 @@ Timer::Connection::Connection(Genode::Env &env, char const *label)
: Timer::Connection(env, env.ep(), label) {}
void Timer::Connection::_schedule_one_shot(Timeout &timeout, Microseconds duration)
Timeout_scheduler &Timer::Connection::_switch_to_timeout_framework_mode()
{
_enable_modern_mode();
_scheduler._schedule_one_shot(timeout, duration);
if (_mode == TIMEOUT_FRAMEWORK) {
return _timeout_scheduler;
}
_mode = TIMEOUT_FRAMEWORK;
_sigh(_signal_handler);
_timeout_scheduler._enable();
/* do initial calibration burst to make interpolation available earlier */
for (unsigned i = 0; i < NR_OF_INITIAL_CALIBRATIONS; i++) {
_update_real_time();
}
return _timeout_scheduler;
};
void Timer::Connection::_schedule_periodic(Timeout &timeout, Microseconds duration)
{
_enable_modern_mode();
_scheduler._schedule_periodic(timeout, duration);
};
void Timer::Connection::_discard(Timeout &timeout)
{
_enable_modern_mode();
_scheduler._discard(timeout);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ class Timer::Time_source : public Threaded_time_source
** Threaded_time_source **
**************************/
void _wait_for_irq() override;
Result_of_wait_for_irq _wait_for_irq() override;
public:
@ -56,7 +56,7 @@ class Timer::Time_source : public Threaded_time_source
Duration curr_time() override;
Microseconds max_timeout() const override;
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
void set_timeout(Microseconds duration, Genode::Timeout_handler &handler) override;
};
#endif /* _TIME_SOURCE_H_ */

View File

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

View File

@ -95,7 +95,7 @@ class Timer::Time_source : public Genode::Signalled_time_source
*************************/
Duration curr_time() override;
void schedule_timeout(Microseconds duration, Timeout_handler &handler) override;
void set_timeout(Microseconds duration, Genode::Timeout_handler &handler) override;
Microseconds max_timeout() const override {
return Microseconds(PIT_MAX_USEC); }
};

View File

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

View File

@ -84,15 +84,15 @@ class Lx::Timer
private:
Genode::Entrypoint &_ep;
Genode::Timeout_scheduler &_scheduler;
::Timer::Connection &_timer;
/* One-shot timeout for timer list */
::Timer::One_shot_timeout<Lx::Timer> _timers_one_shot {
_scheduler, *this, &Lx::Timer::_handle_timers };
_timer, *this, &Lx::Timer::_handle_timers };
/* One-shot timeout for 'wait' */
::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;
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);
if (new_jiffies < jiffies)
jiffies = usecs_to_jiffies(_scheduler.curr_time().trunc_to_plain_us().value);
jiffies = usecs_to_jiffies(_timer.curr_time().trunc_to_plain_us().value);
else
jiffies = new_jiffies;
}
@ -199,13 +199,13 @@ class Lx::Timer
/**
* Constructor
*/
Timer(Genode::Entrypoint &ep,
Genode::Timeout_scheduler &scheduler,
Genode::Allocator &alloc,
Timer(Genode::Entrypoint &ep,
::Timer::Connection &timer,
Genode::Allocator &alloc,
void (*tick)())
:
_ep(ep),
_scheduler(scheduler),
_timer(timer),
_timer_alloc(&alloc),
_tick(tick)
{
@ -296,7 +296,7 @@ class Lx::Timer
* Do not use lx_emul usecs_to_jiffies(unsigned int) because
* of implicit truncation!
*/
jiffies = _scheduler.curr_time().trunc_to_plain_ms().value / JIFFIES_TICK_MS;
jiffies = _timer.curr_time().trunc_to_plain_ms().value / JIFFIES_TICK_MS;
}
/**
@ -326,11 +326,12 @@ class Lx::Timer
static Lx::Timer *_timer;
void Lx::timer_init(Genode::Entrypoint &ep,
Genode::Timeout_scheduler &scheduler,
Genode::Allocator &alloc, void (*tick)())
void Lx::timer_init(Genode::Entrypoint &ep,
::Timer::Connection &timer,
Genode::Allocator &alloc,
void (*tick)())
{
static Lx::Timer inst(ep, scheduler, alloc, tick);
static Lx::Timer inst(ep, timer, alloc, tick);
_timer = &inst;
}

View File

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

View File

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

View File

@ -50,6 +50,17 @@ static bool precise_time(Xml_node config)
return false;
}
struct A1 {
A1() {
log(__func__,__LINE__);
}
};
struct A2 {
A2() {
log(__func__,__LINE__);
}
};
struct Test
{
@ -58,6 +69,7 @@ struct Test
Signal_transmitter done;
unsigned id;
Attached_rom_dataspace config { env, "config" };
Timer::Connection timer { env };
Test(Env &env,
@ -76,6 +88,7 @@ struct Test
* because Timer::Connection by now must be backwards compatible
* and therefore starts interpolation only on demand.
*/
timer.curr_time();
log("\nTEST ", id, ": ", brief, "\n");
@ -819,4 +832,14 @@ struct Main
};
void Component::construct(Env &env) { static Main main(env); }
void Component::construct(Env &env) {
/*
Timer::Connection timer { env};
while (1) {
log(__func__,__LINE__);
timer.msleep(1000);
log(__func__,__LINE__);
}
*/
static Main main(env);
}