genode/repos/base/include/timer/timeout.h

308 lines
7.6 KiB
C++

/*
* \brief Multiplexing one time source amongst different timeouts
* \author Martin Stein
* \date 2016-11-04
*
* These classes are not meant to be used directly. They merely exist to share
* the generic parts of timeout-scheduling between the Timer::Connection and the
* Timer driver. For user-level timeout-scheduling you should use the interface
* in timer_session/connection.h instead.
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _TIMER__TIMEOUT_H_
#define _TIMER__TIMEOUT_H_
/* Genode includes */
#include <util/noncopyable.h>
#include <base/lock.h>
#include <base/log.h>
#include <base/duration.h>
namespace Genode {
class Time_source;
class Timeout_scheduler;
class Timeout;
class Alarm_timeout_scheduler;
}
namespace Timer
{
class Connection;
class Root_component;
}
/**
* 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
*/
virtual Duration curr_time() = 0;
/**
* Return the maximum timeout duration that the source can handle
*/
virtual Microseconds max_timeout() const = 0;
/**
* Install a timeout, overrides the last timeout if any
*
* \param duration timeout duration
* \param handler timeout callback
*/
virtual void schedule_timeout(Microseconds duration,
Timeout_handler &handler) = 0;
/**
* 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;
};
/**
* Timeout callback that can be used for both one-shot and periodic timeouts
*
* This class should be used only if it is necessary to use one timeout
* callback for both periodic and one-shot timeouts. This is the case, for
* example, in a Timer-session server. If this is not the case, the classes
* Periodic_timeout and One_shot_timeout are the better choice.
*/
class Genode::Timeout : private Noncopyable
{
friend class Alarm_timeout_scheduler;
public:
/**
* Interface of a timeout handler
*/
struct Handler : Interface
{
virtual void handle_timeout(Duration curr_time) = 0;
};
private:
class Alarm
{
friend class Alarm_timeout_scheduler;
private:
typedef uint64_t Time;
struct Raw
{
Time deadline;
bool deadline_period;
Time period;
bool is_pending_at(uint64_t time, bool time_period) const;
};
Lock _dispatch_lock { };
Raw _raw { };
int _active { 0 };
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;
public:
Timeout(Timeout_scheduler &timeout_scheduler)
: _alarm(timeout_scheduler) { }
~Timeout() { discard(); }
void schedule_periodic(Microseconds duration, Handler &handler);
void schedule_one_shot(Microseconds duration, Handler &handler);
void discard();
bool scheduled() { return _alarm.handler != nullptr; }
};
/**
* Timeout-scheduler implementation using the Alarm framework
*/
class Genode::Alarm_timeout_scheduler : private Noncopyable,
public Timeout_scheduler,
public Time_source::Timeout_handler
{
friend class Timer::Connection;
friend class Timer::Root_component;
friend class Timeout::Alarm;
private:
using Alarm = Timeout::Alarm;
Time_source &_time_source;
Lock _lock { };
Alarm *_active_head { nullptr };
Alarm *_pending_head { nullptr };
Alarm::Time _now { 0UL };
bool _now_period { false };
Alarm::Raw _min_handle_period { };
void _alarm_unsynchronized_enqueue(Alarm *alarm);
void _alarm_unsynchronized_dequeue(Alarm *alarm);
Alarm *_alarm_get_pending_alarm();
void _alarm_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time first_duration);
void _enable();
/**********************************
** Time_source::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));
~Alarm_timeout_scheduler();
/***********************
** Timeout_scheduler **
***********************/
Duration curr_time() override { return _time_source.curr_time(); }
};
#endif /* _TIMER__TIMEOUT_H_ */