/* * \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 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); }