168 lines
4.3 KiB
C++
168 lines
4.3 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(unsigned long min_factor,
|
||
|
unsigned long max_factor)
|
||
|
{
|
||
|
/*
|
||
|
* If the factor gets adapted less than 12.5%, we raise the
|
||
|
* interpolation-quality value. Otherwise, we reset it to zero.
|
||
|
* We can safely do the shift on the factor as it should be
|
||
|
* at least of value 1 << TS_TO_US_RATIO_SHIFT.
|
||
|
*/
|
||
|
if ((max_factor - min_factor) < (max_factor >> 3)) {
|
||
|
if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) {
|
||
|
_interpolation_quality++; }
|
||
|
} else if (_interpolation_quality) {
|
||
|
_interpolation_quality = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
unsigned long Timer::Connection::_ts_to_us_ratio(Timestamp ts,
|
||
|
unsigned long us)
|
||
|
{
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
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 << TS_TO_US_RATIO_SHIFT) / us;
|
||
|
unsigned long const result_ul = (unsigned long)result;
|
||
|
if (result == result_ul) {
|
||
|
return result_ul; }
|
||
|
|
||
|
warning("Timestamp-to-time ratio too big");
|
||
|
return ~0UL;
|
||
|
}
|
||
|
|
||
|
|
||
|
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 < interpolated_time) {
|
||
|
_interpolated_time = interpolated_time; }
|
||
|
|
||
|
return _interpolated_time;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Timer::Connection::_handle_timeout()
|
||
|
{
|
||
|
unsigned long const ms = elapsed_ms();
|
||
|
if (ms - _ms > REAL_TIME_UPDATE_PERIOD_US / 1000UL) {
|
||
|
_update_real_time();
|
||
|
}
|
||
|
if (_handler) {
|
||
|
_handler->handle_timeout(Duration(Milliseconds(ms)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
Timer::Connection::Connection()
|
||
|
:
|
||
|
Genode::Connection<Session>(session("ram_quota=10K")),
|
||
|
Session_client(cap()),
|
||
|
_signal_handler(internal_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);
|
||
|
}
|