genode/repos/base-hw/src/core/kernel/timer.cc

115 lines
2.9 KiB
C++

/*
* \brief A timer manages a continuous time and timeouts on it
* \author Martin Stein
* \date 2016-03-23
*/
/*
* 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.
*/
/* Core includes */
#include "cpu.h"
#include "timer.h"
#include "configuration.h"
#include <hw/assert.h>
using namespace Kernel;
void Timer::Irq::occurred() { _cpu.scheduler().timeout(); }
Timer::Irq::Irq(unsigned id, Cpu &cpu)
: Kernel::Irq(id, cpu.irq_pool()), _cpu(cpu) {}
time_t Timer::timeout_max_us() const
{
return ticks_to_us(_max_value());
}
void Timer::set_timeout(Timeout * const timeout, time_t const duration)
{
/*
* Remove timeout if it is already in use. Timeouts may get overridden as
* result of an update.
*/
if (timeout->_listed) {
_timeout_list.remove(timeout);
} else {
timeout->_listed = true; }
/* set timeout parameters */
timeout->_end = time() + duration;
/*
* Insert timeout. Timeouts are ordered ascending according to their end
* time to be able to quickly determine the nearest timeout.
*/
Timeout * t1 = 0;
for (Timeout * t2 = _timeout_list.first();
t2 && t2->_end < timeout->_end;
t1 = t2, t2 = t2->next()) { }
_timeout_list.insert(timeout, t1);
}
void Timer::schedule_timeout()
{
/* get the timeout with the nearest end time */
Timeout * timeout = _timeout_list.first();
assert(timeout);
/* install timeout at timer hardware */
_time += _duration();
_last_timeout_duration = (timeout->_end > _time) ? timeout->_end - _time : 1;
_start_one_shot(_last_timeout_duration);
}
void Timer::process_timeouts()
{
/*
* Walk through timeouts until the first whose end time is in the future.
*/
time_t t = time();
while (true) {
Timeout * const timeout = _timeout_list.first();
if (!timeout) { break; }
if (timeout->_end > t) { break; }
_timeout_list.remove(timeout);
timeout->_listed = false;
timeout->timeout_triggered();
}
}
Timer::Timer(Cpu & cpu)
: _device(cpu.id()), _irq(interrupt_id(), cpu),
_last_timeout_duration(_max_value())
{
/*
* The timer frequency should allow a good accuracy on the smallest
* timeout syscall value (1 us).
*/
assert(ticks_to_us(1) < 1 || ticks_to_us(_max_value()) == _max_value());
/*
* The maximum measurable timeout is also the maximum age of a timeout
* installed by the timeout syscall. The timeout-age syscall returns a
* bogus value for older timeouts. A user that awoke from waiting for a
* timeout might not be schedulable in the same super period anymore.
* However, if the user can't manage to read the timeout age during the
* next super period, it's a bad configuration or the users fault. That
* said, the maximum timeout should be at least two times the super
* period).
*/
assert(ticks_to_us(_max_value()) > 2 * cpu_quota_us);
}