genode/repos/base/src/lib/timeout/timer_connection.cc

162 lines
4.1 KiB
C++

/*
* \brief Connection to timer service and timeout scheduler
* \author Martin Stein
* \date 2016-11-04
*/
/*
* 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.
*/
/* Genode includes */
#include <timer_session/connection.h>
#include <base/internal/globals.h>
using namespace Genode;
using namespace Genode::Trace;
void Timer::Connection::_update_interpolation_quality(uint64_t min_factor,
uint64_t max_factor)
{
/*
* If the factor gets adapted less than 12.5%, we raise the
* interpolation-quality value. Otherwise, we reset it to zero.
*/
if ((max_factor - min_factor) < (max_factor >> 3)) {
if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) {
_interpolation_quality++; }
} else if (_interpolation_quality) {
_interpolation_quality = 0;
}
}
uint64_t Timer::Connection::_ts_to_us_ratio(Timestamp ts,
uint64_t us,
unsigned shift)
{
/*
* If the timestamp difference is to big to do the following
* factor calculation without an overflow, scale both timestamp
* and time difference down equally. This should neither happen
* often nor have much effect on the resulting factor.
*/
Timestamp const max_ts = ~(Timestamp)0ULL >> shift;
while (ts > max_ts) {
warning("timestamp value too big");
ts >>= 1;
us >>= 1;
}
if (!us) { us = 1; }
if (!ts) { ts = 1; }
/*
* To make the result more precise, we scale up the numerator of
* the calculation. This upscaling must be considered when using
* the result.
*/
Timestamp const result = (ts << shift) / us;
uint64_t const result_ul = (uint64_t)result;
if (result != result_ul) {
warning("Timestamp-to-time ratio too big");
return ~0UL;
}
return result_ul;
}
Duration Timer::Connection::_update_interpolated_time(Duration &interpolated_time)
{
/*
* The new interpolated time value may be smaller than a
* previously interpolated time value (based on an older real time
* value and factor). In this case, we don't want the user time to
* jump back but to freeze at the higher value until the new
* interpolation has caught up.
*/
if (_interpolated_time.less_than(interpolated_time)) {
_interpolated_time = interpolated_time; }
return _interpolated_time;
}
void Timer::Connection::_handle_timeout()
{
uint64_t const us = elapsed_us();
if (us - _us > REAL_TIME_UPDATE_PERIOD_US) {
_update_real_time();
}
if (_handler) {
_handler->handle_timeout(curr_time());
}
}
void Timer::Connection::schedule_timeout(Microseconds duration,
Timeout_handler &handler)
{
if (duration.value < MIN_TIMEOUT_US)
duration.value = MIN_TIMEOUT_US;
if (duration.value > REAL_TIME_UPDATE_PERIOD_US)
duration.value = REAL_TIME_UPDATE_PERIOD_US;
_handler = &handler;
trigger_once(duration.value);
}
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, char const *label)
:
Genode::Connection<Session>(env, session(env.parent(),
"ram_quota=10K, cap_quota=%u, label=\"%s\"",
CAP_QUOTA, label)),
Session_client(cap()),
_signal_handler(env.ep(), *this, &Connection::_handle_timeout)
{
/* register default signal handler */
Session_client::sigh(_default_sigh_cap);
}
void Timer::Connection::_schedule_one_shot(Timeout &timeout, Microseconds duration)
{
_enable_modern_mode();
_scheduler._schedule_one_shot(timeout, duration);
};
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);
}