mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 18:06:50 +00:00
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:
parent
9e5d479d03
commit
7feea78991
@ -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) {
|
||||
|
@ -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); };
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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; };
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
/********************
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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); }
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user