/* * \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 #include 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. */ 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, 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; unsigned long const result_ul = (unsigned long)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() { unsigned long 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(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("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); }